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!

  • terribleplan@lemmy.nrd.li
    link
    fedilink
    English
    arrow-up
    1
    ·
    1 year ago

    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 the keyId will be https://lemmy.nrd.li/u/terribleplan#main-key). A good server will check that the keyId 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 an Accept header of application/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.