I’m trying to better understand Activitypub and I understand that there’s a signature to avoid forgeries of known accounts.
However I’m having trouble understanding what prevents a malicious actor from sending a private spam message supposedly from a never before seen account name with valid generated key pair but for a domain they’ve never bought since there is no DNS lookup or test.
Thank you!
To do the signature validation you need to know/trust the public key signing the incoming request (which will use the
keyId
in the signature to specify the key of the actor, usually a Person, that made the thing, when your server gets this comment thekeyId
will behttps://lemmy.nrd.li/u/terribleplan#main-key
). A good server will check that thekeyId
only differs from the actor of the object by having a fragment appended.Your server needs to fetch my Person object, of
https://lemmy.nrd.li/u/terribleplan
. If you load that with anAccept
header ofapplication/activity+json
you get:{ "@context": [ "https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1", { "lemmy": "https://join-lemmy.org/ns#", "litepub": "http://litepub.social/ns#", "pt": "https://joinpeertube.org/ns#", "sc": "http://schema.org/", "ChatMessage": "litepub:ChatMessage", "commentsEnabled": "pt:commentsEnabled", "sensitive": "as:sensitive", "matrixUserId": "lemmy:matrixUserId", "postingRestrictedToMods": "lemmy:postingRestrictedToMods", "removeData": "lemmy:removeData", "stickied": "lemmy:stickied", "moderators": { "@type": "@id", "@id": "lemmy:moderators" }, "expires": "as:endTime", "distinguished": "lemmy:distinguished", "language": "sc:inLanguage", "identifier": "sc:identifier" } ], "type": "Person", "id": "https://lemmy.nrd.li/u/terribleplan", "preferredUsername": "terribleplan", "inbox": "https://lemmy.nrd.li/u/terribleplan/inbox", "outbox": "https://lemmy.nrd.li/u/terribleplan/outbox", "publicKey": { "id": "https://lemmy.nrd.li/u/terribleplan#main-key", "owner": "https://lemmy.nrd.li/u/terribleplan", "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzX8XfO3F/nBKgST+Rqu8\noBxyE1GdvdXpYUYXq9OqwYEVIsE4Jth+aRzx4rSnotnMYyxbhBst3t77dSZAf7ir\nHjpdSoRYdZ0Ce3qJc4mpnctPtDSIjWl+fYwG9oPF51D8cwJewUejHcj6v6ud44Q5\nHbuiYqrMQo2YtWGKMAmjErE8cFinuNcpoNDCCzopCXWfpks48II6f4/aT/Kd66zo\niUYvBMrEmqWATZVbTwnh2MSwu7XTh8O5SlUeceb3LpC7dyCCpkVJU+DYDVqOfPBA\nSb+KmxqOVnewZor6zVDtfelXXx7Zikbff+IcUGbuiJRUlNsyqaq2kxJMZjO/UYCc\newIDAQAB\n-----END PUBLIC KEY-----\n" }, "name": "terribleplan", "summary": "<p>DevOps as a profession and software development for fun. Admin of lemmy.nrd.li.</p>\n", "source": { "content": "DevOps as a profession and software development for fun. Admin of lemmy.nrd.li.", "mediaType": "text/markdown" }, "icon": { "type": "Image", "url": "https://lemmy.nrd.li/pictrs/image/680ced6c-b461-4d7c-906a-9091268f6e7e.jpeg" }, "endpoints": { "sharedInbox": "https://lemmy.nrd.li/inbox" }, "published": "2023-06-10T16:10:13.859768+00:00" }
You can see my Person object contains
.publicKey.publicKeyPem
, that is what your server will use (and store after fetching it once) to validate the incoming payload/header.Oh, there’s also
Linked Data SignaturesVerifiable Credential Data Integrity that puts signatures right in the JSON itself. This is a real neat option that would allow for all sorts of great things like super easy and forwarding of messages with verifiable integrity, and the ability to store things in a verifiable/trusted way forever. Nobody really implemented it because:- It was pretty under-baked and not standardized at all when AP started getting implemented (as evidenced by it changing names and basically being entirely rewritten). Mastodon, for example, is still on the old LD spec and would not be able to interop if your AP server did not also send the HTTP header signatures.
- IIRC there were some privacy concerns about always signing every action, thus not being able to deny that you did the thing you did. Not sure how real these concerns were, but I remember seeing this argument in the past and thinking it was unconvincing
On the point of 2, it could be made optional, so that the user could choose.
Maybe… I am working on an AP implementation that will reject anything not signed with VCDI because it has such desirable properties. In my implementation all crypto is done client-side only, so the server can’t reasonably be expected to do HTTP signing.