Never ignore invalid HTTP signatures

This commit is contained in:
silverpill 2022-12-07 22:10:58 +00:00
parent d3db42ec9e
commit 06e172d9fa
3 changed files with 31 additions and 11 deletions

View file

@ -32,6 +32,9 @@ pub enum AuthenticationError {
#[error(transparent)] #[error(transparent)]
HttpSignatureError(#[from] HttpSignatureError), HttpSignatureError(#[from] HttpSignatureError),
#[error("no HTTP signature")]
NoHttpSignature,
#[error(transparent)] #[error(transparent)]
JsonSignatureError(#[from] JsonSignatureError), JsonSignatureError(#[from] JsonSignatureError),
@ -76,11 +79,17 @@ pub async fn verify_signed_request(
request: &HttpRequest, request: &HttpRequest,
no_fetch: bool, no_fetch: bool,
) -> Result<DbActorProfile, AuthenticationError> { ) -> Result<DbActorProfile, AuthenticationError> {
let signature_data = parse_http_signature( let signature_data = match parse_http_signature(
request.method(), request.method(),
request.uri(), request.uri(),
request.headers(), request.headers(),
)?; ) {
Ok(signature_data) => signature_data,
Err(HttpSignatureError::NoSignature) => {
return Err(AuthenticationError::NoHttpSignature);
},
Err(other_error) => return Err(other_error.into()),
};
let actor_id = key_id_to_actor_id(&signature_data.key_id)?; let actor_id = key_id_to_actor_id(&signature_data.key_id)?;
let actor_profile = if no_fetch { let actor_profile = if no_fetch {
@ -113,12 +122,13 @@ pub async fn verify_signed_activity(
db_client: &impl GenericClient, db_client: &impl GenericClient,
activity: &Value, activity: &Value,
) -> Result<DbActorProfile, AuthenticationError> { ) -> Result<DbActorProfile, AuthenticationError> {
let signature_data = get_json_signature(activity).map_err(|error| { let signature_data = match get_json_signature(activity) {
match error { Ok(signature_data) => signature_data,
JsonSignatureError::NoProof => AuthenticationError::NoJsonSignature, Err(JsonSignatureError::NoProof) => {
other_error => other_error.into(), return Err(AuthenticationError::NoJsonSignature);
} },
})?; Err(other_error) => return Err(other_error.into()),
};
let actor_profile = match signature_data.signer { let actor_profile = match signature_data.signer {
JsonSigner::ActorKeyId(ref key_id) => { JsonSigner::ActorKeyId(ref key_id) => {

View file

@ -157,6 +157,8 @@ pub async fn receive_activity(
let object_id = find_object_id(&activity.object)?; let object_id = find_object_id(&activity.object)?;
activity.actor == object_id activity.actor == object_id
} else { false }; } else { false };
// HTTP signature is required
let mut signer = match verify_signed_request( let mut signer = match verify_signed_request(
config, config,
db_client, db_client,
@ -169,8 +171,13 @@ pub async fn receive_activity(
request_signer request_signer
}, },
Err(error) => { Err(error) => {
if is_self_delete { if is_self_delete && matches!(
error,
AuthenticationError::NoHttpSignature |
AuthenticationError::DatabaseError(_)
) {
// Ignore Delete(Person) activities without HTTP signatures // Ignore Delete(Person) activities without HTTP signatures
// or if signer is not found in local database
return Ok(()); return Ok(());
}; };
log::warn!("invalid HTTP signature: {}", error); log::warn!("invalid HTTP signature: {}", error);
@ -178,7 +185,7 @@ pub async fn receive_activity(
}, },
}; };
// Verify embedded signature // JSON signature is optional
match verify_signed_activity(config, db_client, activity_raw).await { match verify_signed_activity(config, db_client, activity_raw).await {
Ok(activity_signer) => { Ok(activity_signer) => {
if activity_signer.acct != signer.acct { if activity_signer.acct != signer.acct {

View file

@ -13,6 +13,9 @@ const SIGNATURE_EXPIRES_IN: i64 = 12; // 12 hours
#[derive(thiserror::Error, Debug)] #[derive(thiserror::Error, Debug)]
pub enum HttpSignatureVerificationError { pub enum HttpSignatureVerificationError {
#[error("missing signature header")]
NoSignature,
#[error("{0}")] #[error("{0}")]
HeaderError(&'static str), HeaderError(&'static str),
@ -41,7 +44,7 @@ pub fn parse_http_signature(
request_headers: &HeaderMap, request_headers: &HeaderMap,
) -> Result<HttpSignatureData, VerificationError> { ) -> Result<HttpSignatureData, VerificationError> {
let signature_header = request_headers.get("signature") let signature_header = request_headers.get("signature")
.ok_or(VerificationError::HeaderError("missing signature header"))? .ok_or(VerificationError::NoSignature)?
.to_str() .to_str()
.map_err(|_| VerificationError::HeaderError("invalid signature header"))?; .map_err(|_| VerificationError::HeaderError("invalid signature header"))?;