Add security checks for activitypub inbox #78
Loading…
Reference in New Issue
No description provided.
Delete Branch "federation-authorisation"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
TODO:
@ -66,3 +66,4 @@
.await?;
if !is_mod_or_admin {
// TODO: more accurately, not_a_mod_or_admin?
return Err(APIError::err("not_an_admin").into());
Good idea, add a
not_a_mod_or_admin
string toen.json
@ -168,0 +174,4 @@
.as_single_xsd_string()
.unwrap()
.to_string();
check_slurs(&content)?;
Hrm, for comments, its best to do remove_slurs
let content_slurs_removed = remove_slurs(&data.content.to_owned());
Also just as a technical consideration / thought, if a comment isn't in our local DB because its been slur rejected (or rejected for any reason), any replies might also not come through because we don't have that comment parent_id.
Thanks, didnt notice that it was handled differently for comments.
This means that federation for posts with slurs will just fail silently then, gonna need some thinking on how to handle it properly (mainly the case where two instances have different slur filters).
@ -182,3 +187,3 @@
updated: note.updated().map(|u| u.to_owned().naive_local()),
deleted: None,
ap_id: note.id_unchecked().unwrap().to_string(),
ap_id: check_actor_domain(note, expected_domain)?,
This is tricky, and maybe we should move the check out of the commentForm here. Because on comment creates / edits, we should check the specific actor_id and make sure they're the same.
But on comment removes, the actor id can be someone else.
Look into the
check_actor_domain()
function, thats exactly what it does based on whether or notexpected_domain
is Some or None.@ -337,0 +351,4 @@
.unwrap()
.to_string();
let title = group.inner.preferred_username().unwrap().to_string();
// TODO: should be parsed as html and tags like <script> removed (or use markdown source)
The titles and preferred usernames I'm just treating as text, not html. But ya all the content type fields, like summary, do need this. It'd probably be easiest to do it this way, like the docs have, and not have to deal with html conversions at all.
I think we are actually sending markdown now, but we should still sanitise that, could be that our markdown parser (or the parser in a Lemmy client) actually renders HTML that is embedded in markdown. Anyway its not related to this PR, I just moved that comment.
@ -465,2 +477,4 @@
// because that would result in a database error (same data inserted twice)
// this seems to be the "easiest" stable alternative for remove_item()
to.retain(|x| *x != sender.get_shared_inbox_url());
to.retain(|x| *x != community.get_shared_inbox_url());
I still have to add columns for the inbox, so we aren't just generating them randomly.
What do you mean?
@ -57,2 +64,3 @@
let post = PostForm::from_apub(&page, client, pool).await?;
let post = PostForm::from_apub(&page, client, pool, Some(user.actor_id()?)).await?;
// TODO: not sure if it makes sense to check for the exact user, seeing as we already check the domain
Seems like bc we have checks where the actor and the post_creator / comment_creator are different (for removes), then the checks unfortunately will have to be all in these files, and not in the
from_apub
.But yes I also do think its a good idea to check not just the domain, but the exact actor_id.
@ -42,1 +49,4 @@
let remove = Remove::from_any_base(activity)?.unwrap();
let actor = get_user_from_activity(&remove, client, pool).await?;
let community = get_community_from_activity(&remove, client, pool).await?;
// TODO: we dont federate remote admins at all, and remote mods arent federated properly
I don't see why we couldn't just start that now. A federated mod would pass this check.
I'll add in the chat about removes.
@ -55,0 +72,4 @@
} else {
Ok(())
}
}
Nice, I like this.
@ -110,1 +135,4 @@
let dislike = Dislike::from_any_base(undo.object().to_owned().one().unwrap())?.unwrap();
check_is_undo_valid(&undo, &dislike)?;
// TODO: need to implement Undo<Dislike>
Should be easy enough, just call the same function as the receive_undo_comment_like, as both just remove the comment_like row anyway:
blocking(pool, move |conn| CommentLike::remove(conn, &like_form)).await??;
Yep, but I will do it after this PR.
@ -43,0 +45,4 @@
// ensure that update and actor come from the same instance
let user = get_user_from_activity(&update, client, pool).await?;
update.id(user.actor_id()?.domain().unwrap())?;
This should check not just the domain, but the exact actor id.
...
Oh wait I think you're doing that below. Just the below check should be good enough then.
@ -73,2 +73,3 @@
let community = get_community_id_from_activity(&activity).await;
// TODO: i dont think this works for Announce/Undo activities
//let community = get_community_id_from_activity(&activity).await;
Looks like the check makes sure its in the allowed_instances list. Not sure I understand removing this. If you've blocked an instance, you don't want to receive anything, including announcements from it.
Problem is that Undo and/or Announce activities dont have the community set in cc, so this was throwing errors (dont remember exactly what it was about).
Gonna have to test this again, we might have to move this check into the create/update etc files.
@ -77,3 +78,3 @@
check_is_apub_id_valid(&community)?;
verify(&request, actor.as_ref())?;
// TODO: probably better to do this after, so we dont store activities that fail a check somewhere
Good idea, you could move this below the match, and do
Done, I thought maybe there was a reason to do it this way.
@ -161,3 +162,3 @@
let note = Note::from_any_base(update.object().as_one().unwrap().to_owned())?.unwrap();
let private_message_form = PrivateMessageForm::from_apub(¬e, client, pool).await?;
let domain = Some(update.id_unchecked().unwrap().to_owned());
These domain checks aren't needed right? Just check the actor_id.
Maybe not exactly needed, but cant hurt to have them. And like we discussed, checking the actor id doesnt really help with security, so this is the easier solution (I added some docs about this too).
@ -133,3 +143,4 @@
apub: &Self::ApubType,
client: &Client,
pool: &DbPool,
expected_domain: Option<Url>,
Ya this can pry be removed. I think the http sig verify, and the checking that its the correct actor_id, should be enough. That's 2 checks, a domain would be a third.
No, this is the main check to make that eg in case of a
Create<Page>
, both Create and Page are coming from the same instance.@ -214,2 +225,4 @@
.as_ref()
.map(|c| c.as_single_xsd_string().unwrap().to_string());
check_slurs(&name)?;
check_slurs_opt(&body)?;
I must have overlooked where this got replaced, or I had in wrong in the first place.
Both here and in the
api/post.rs
, its better to doremove_slurs
on the post body, notcheck_slurs
.Basically any long content field shouldn't reject, but just softly replace the slurs.
Okay, done.
Updated. Turns out the
check_is_apub_id_valid(&community)?
in shared_inbox actually works fine, but there were some other problems that I fixed. Also the tests for remove post/comment are failing, but its working fine when I try it manually, so I could use your help there. Other than the failing tests, this should be good to merge.WIP: Add security checks for activitypub inboxto Add security checks for activitypub inboxI am getting some uniqueness errors:
What are you doing to get them?