Never ignore invalid HTTP signatures
This commit is contained in:
parent
d3db42ec9e
commit
06e172d9fa
3 changed files with 31 additions and 11 deletions
|
@ -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) => {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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"))?;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue