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.