In inbox, verify that http signature digest was verified (ref #1) (#6)

This commit is contained in:
Nutomic 2022-11-14 14:23:06 +00:00 committed by GitHub
parent da32da5a67
commit 3a8dd5c6db
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 44 additions and 28 deletions

28
Cargo.lock generated
View file

@ -631,9 +631,9 @@ dependencies = [
[[package]] [[package]]
name = "futures-channel" name = "futures-channel"
version = "0.3.24" version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050" checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed"
dependencies = [ dependencies = [
"futures-core", "futures-core",
"futures-sink", "futures-sink",
@ -641,9 +641,9 @@ dependencies = [
[[package]] [[package]]
name = "futures-core" name = "futures-core"
version = "0.3.24" version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac"
[[package]] [[package]]
name = "futures-executor" name = "futures-executor"
@ -658,15 +658,15 @@ dependencies = [
[[package]] [[package]]
name = "futures-io" name = "futures-io"
version = "0.3.24" version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbf4d2a7a308fd4578637c0b17c7e1c7ba127b8f6ba00b29f717e9655d85eb68" checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb"
[[package]] [[package]]
name = "futures-macro" name = "futures-macro"
version = "0.3.24" version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42cd15d1c7456c04dbdf7e88bcd69760d74f3a798d6444e16974b505b0e62f17" checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -675,21 +675,21 @@ dependencies = [
[[package]] [[package]]
name = "futures-sink" name = "futures-sink"
version = "0.3.24" version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56" checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9"
[[package]] [[package]]
name = "futures-task" name = "futures-task"
version = "0.3.24" version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea"
[[package]] [[package]]
name = "futures-util" name = "futures-util"
version = "0.3.24" version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90" checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6"
dependencies = [ dependencies = [
"futures-channel", "futures-channel",
"futures-core", "futures-core",

View file

@ -16,7 +16,7 @@ use activitypub_federation::{
UrlVerifier, UrlVerifier,
APUB_JSON_CONTENT_TYPE, APUB_JSON_CONTENT_TYPE,
}; };
use actix_web::{web, App, HttpRequest, HttpResponse, HttpServer}; use actix_web::{web, web::Payload, App, HttpRequest, HttpResponse, HttpServer};
use async_trait::async_trait; use async_trait::async_trait;
use http_signature_normalization_actix::prelude::VerifyDigest; use http_signature_normalization_actix::prelude::VerifyDigest;
use reqwest::Client; use reqwest::Client;
@ -90,8 +90,6 @@ impl Instance {
web::scope("") web::scope("")
// Important: this ensures that the activity json matches the hashsum in signed // Important: this ensures that the activity json matches the hashsum in signed
// HTTP header // HTTP header
// TODO: it would be possible to get rid of this by verifying hash in
// receive_activity()
.wrap(VerifyDigest::new(Sha256::new())) .wrap(VerifyDigest::new(Sha256::new()))
// Just a single, global inbox for simplicity // Just a single, global inbox for simplicity
.route("/inbox", web::post().to(http_post_user_inbox)), .route("/inbox", web::post().to(http_post_user_inbox)),
@ -126,14 +124,13 @@ async fn http_get_user(
/// Handles messages received in user inbox /// Handles messages received in user inbox
async fn http_post_user_inbox( async fn http_post_user_inbox(
request: HttpRequest, request: HttpRequest,
payload: String, payload: Payload,
data: web::Data<InstanceHandle>, data: web::Data<InstanceHandle>,
) -> Result<HttpResponse, Error> { ) -> Result<HttpResponse, Error> {
let data: InstanceHandle = data.into_inner().deref().clone(); let data: InstanceHandle = data.into_inner().deref().clone();
let activity = serde_json::from_str(&payload)?;
receive_activity::<WithContext<PersonAcceptedActivities>, MyUser, InstanceHandle>( receive_activity::<WithContext<PersonAcceptedActivities>, MyUser, InstanceHandle>(
request, request,
activity, payload,
&data.clone().local_instance, &data.clone().local_instance,
&Data::new(data), &Data::new(data),
) )

View file

@ -6,14 +6,21 @@ use crate::{
Error, Error,
LocalInstance, LocalInstance,
}; };
use actix_web::{HttpRequest, HttpResponse}; use actix_web::{
web::{Bytes, Payload},
FromRequest,
HttpRequest,
HttpResponse,
};
use anyhow::anyhow;
use http_signature_normalization_actix::prelude::DigestVerified;
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use tracing::log::debug; use tracing::{log::debug, warn};
/// Receive an activity and perform some basic checks, including HTTP signature verification. /// Receive an activity and perform some basic checks, including HTTP signature verification.
pub async fn receive_activity<Activity, ActorT, Datatype>( pub async fn receive_activity<Activity, ActorT, Datatype>(
request: HttpRequest, request: HttpRequest,
activity: Activity, payload: Payload,
local_instance: &LocalInstance, local_instance: &LocalInstance,
data: &Data<Datatype>, data: &Data<Datatype>,
) -> Result<HttpResponse, <Activity as ActivityHandler>::Error> ) -> Result<HttpResponse, <Activity as ActivityHandler>::Error>
@ -21,10 +28,25 @@ where
Activity: ActivityHandler<DataType = Datatype> + DeserializeOwned + Send + 'static, Activity: ActivityHandler<DataType = Datatype> + DeserializeOwned + Send + 'static,
ActorT: ApubObject<DataType = Datatype> + Actor + Send + 'static, ActorT: ApubObject<DataType = Datatype> + Actor + Send + 'static,
for<'de2> <ActorT as ApubObject>::ApubType: serde::Deserialize<'de2>, for<'de2> <ActorT as ApubObject>::ApubType: serde::Deserialize<'de2>,
<Activity as ActivityHandler>::Error: <Activity as ActivityHandler>::Error: From<anyhow::Error>
From<anyhow::Error> + From<Error> + From<<ActorT as ApubObject>::Error>, + From<Error>
+ From<<ActorT as ApubObject>::Error>
+ From<serde_json::Error>
+ From<http_signature_normalization_actix::digest::middleware::VerifyError>,
<ActorT as ApubObject>::Error: From<Error> + From<anyhow::Error>, <ActorT as ApubObject>::Error: From<Error> + From<anyhow::Error>,
{ {
let mut payload = payload.into_inner();
// ensure that payload hash was checked against digest header by middleware
DigestVerified::from_request(&request, &mut payload).await?;
let bytes = Bytes::from_request(&request, &mut payload)
.await
.map_err(|e| {
warn!("{}", e);
anyhow!("Failed to parse request body")
})?;
let activity: Activity = serde_json::from_slice(&bytes)?;
verify_domains_match(activity.id(), activity.actor())?; verify_domains_match(activity.id(), activity.actor())?;
verify_url_valid(activity.id(), &local_instance.settings).await?; verify_url_valid(activity.id(), &local_instance.settings).await?;
if local_instance.is_local_url(activity.id()) { if local_instance.is_local_url(activity.id()) {

View file

@ -1,3 +0,0 @@
fn main() {
println!("Hello, world!");
}