确认和签署Fediverse活动请求
#网络开发人员 #编程 #php #activitypub

首先阅读此帖子:How to Put Your Blog on the Fediverse

阅读该帖子后,您可能会想知道如何参加联邦政府活动,例如共享内容,获得追随者或接受评论。

答案是您需要先学习如何确认请求。

资源

Fediverse仍处于早期阶段。如果您想正确执行操作,请确保您是根据规格编写单元测试,并且不要害怕挖掘他人的代码。我对Chatgpt进行了很多反向工程,因为我不熟悉Ruby。诸如日期格式,JSON编码和base64之类的小东西都可以丢弃您的签名请求!

了解请求流(又称握手)

ActivityPub(核心)都是每个用户都有一个收件箱和一个输出箱,支持GET和发布请求。当互联网上的某人请求突变时,他们会发送邮政请求。当他们想获取当前状态时,他们会使用GET请求。将电子邮件作为您的心理模型,这将是有道理的。

ActivityPub inbox/outbox flow

收件箱和输出箱之间的区别

使用输出箱小于收件箱。 OUTBOX服务获得请求,以便其他服务器可以选择回填帖子。将邮政请求发送到发出框就像REST API一样。客户端将其发布到输出箱,然后服务器稍后对其进行处理,将帖子发送到其他收件箱。收件箱的获取主要用于私人使用。

确认请求常见问题解答

由于大多数交互通过收件箱发生,因此第一件事是建立用户的收件箱。

我应该使用哪种MIME类型或内容类型?

ActivityPub请求/响应应为application/ld+json; profile="https://www.w3.org/ns/activitystreamsapplication/activity+json,除非另有说明。他们应该得到平等对待。

我的HTTP响应包含ActivityPub文档;为什么什么都没发生?

要确认ActivityPub请求,您必须将邮政请求退还给演员收件箱。这就是ActivityPub服务器保持异步的方式。

发布请求无法正常工作!

双检查发送请求时,您正在附加Content-Type标头,并将请求发送为原始字符串。一些库/框架将以表单请求或JSON发送您的数据。

示例已签署的接受/拒绝遵循请求

在上一个教程中,您应该定义一个用于处理收件箱请求的端点,并将其包含在用户的ActivityPub配置文件响应中。该响应至关重要,因为服务器使用它来找到您的收件箱。

设置服务器后处理收件箱的传入邮政请求。您将开始收到application/activity+json类型请求,如以下内容:

{
  "@context": "https://www.w3.org/ns/activitystreams",
  "id": "https://mastodon.social/96cb3649-7a75-49c5-b246-xxxxxxxxxxxx",
  "type": "Follow",
  "actor": "https://mastodon.social/users/codoxuba",
  "object": "https://example.com/activityPub/users/1"
}

Follow请求的情况下。 Actor是启动请求的人/事物。 Object是他们想要遵循的人/事物。 id是由服务器开始请求的服务器创建的。大多数服务器使用ID跟踪请求状态。我不会在这篇文章中介绍。

但是,重要的是要注意,如上所述,actorobjectid可以是未解决的URL,也可以是嵌套的JSON-LD对象。我不会在这篇文章中介绍。

承认即将到来的活动

这是您应该在TCP/IP协议上刷新的地方,因为该概念相似。我会提出预期的是,默认情况下忽略请求是明智的。任何确认都可以在攻击中对您使用。我只想处理遵循请求,因此我会忽略所有其他请求:

class InboxController extends Controller
{
    public function receiveFromInternet(Request $request, User $user)
    {
    // todo: better validation
        $activityType = $request->input('type');

        if ($requestType !== 'Follow') {
            return;
        }

        $acknowledgment = [
            "@context" => "https://www.w3.org/ns/activitystreams",
            "summary" => "Alice accepted a follow",
            "type" => "Accept",
            "actor" => "https://example.com/activityPub/users/1",
            "object" => $request->input('id'),
        ];
    }
}

确认很简单。对于关注请求,您可以选择AcceptRejectTenativeAcceptTenativeReject或忽略。有问题的部分是签署确认。

签署确认

签名请求基于HTTP Signatures Draft RFC。在整个签名过程中,我将使用这些变量:

$url = parse_url($inboxUrl);
$host = data_get($url, 'host');
$path = data_get($url, 'path');

$publicKeyId = 'https://example.com/activityPub/users/1#main-key';

$date = now()->toRfc7231String();

接下来,将您的确认为JSON,然后创建一个摘要。您必须使用base64编码哈希,并使用用于生成哈希的算法的算法预处理值。大多数Fediverse服务器仅支持SHA-256。

$document_str = json_encode($document);
$sha256 = hash('sha256', $document_str, true);
$digest = 'SHA-256=' . base64_encode($sha256);

您必须使用该摘要来创建签名的Signature标头。不包括摘要将产生无效的签名:

$dataToSign = "(request-target): {$method} {$path}\nhost: {$host}\ndate: {$date}\ndigest: {$digest}";

要签名的数据应该看起来像这样。没有尾随的新线条。没有马车返回。

(request-target): post /users/@bob/inbox
host: mastadon.social
date: Wed, 30 Aug 2023 12:01:01 GMT
digest: SHA-256=MmNmMjRkYmE1ZmIwYTMwZTI2ZTgzYjJhYzViOWUyOWUxYjE2MWU1YzFmYTc0MjVlNzMwNDMzNjI5MzhiOTgyNA==

在我们签署消息之前,您必须生成一个键对。公钥将分发给Fediverse。

openssl genrsa -out private.pem 2048
openssl rsa -in private.pem -outform PEM -pubout -out public.pem

注释您必须在用户的活动插件中包含公共密钥。

您必须使用相同的算法签名以创建摘要。根据编程语言,请参阅OpenSSL库。使用PHP,通常已经将其安装为PHP扩展名:

$privateKey = openssl_pkey_get_private(Storage::get('private.pem'));

$signature = null; // mutated by openssl
$enodedSignature = null;
if (openssl_sign($data, $signature, $privateKey, OPENSSL_ALGO_SHA256)) {
    $encodedSignature = base64_encode($signature);
} else {
    throw new Exception('Could not sign activityPub data');
}

签名后,您将获得一个基本64编码的字符串,而不是前面生成的摘要。在包含签名数据之前,我们必须构建Signature标头,以便其他服务器可以验证标题。如果签署额外的标头,请修改签名标头的(request-target)部分。

# signature header used when signing host, date, and digest.
$signatureHeader = sprintf( 'keyId="%s",algorithm="rsa-sha256",headers="(request-target) host date digest",signature="%s"', $id, $base64 );

RAW HTTP标头在准备发送时将看起来像这样的东西:

Host: mastodon.social
Date: Wed, 18 Aug 2022 22:00:00 GMT
Digest: SHA-256=6ccPqhz2TJmLL08E8AFny1/Wube60hOH3g6zzwv/Ttg=
Signature: keyId="https://example.com/activityPub/users/1#main-key",algorithm="rsa-sha256",headers="(request-target) host date digest",signature="Cdih8iQQQPeDInLCN4H94Lm/hTKSNOjSnjleI8gZfndRsTwO1CqG41s+BRF2Oh51yETWEsR2ezceDgUgH+ME4jdrgUIMPm/Ox4B6c5QEASPPlFpcOfWcLryCCvEkQOVd3tbMITeY+uY6WITuZKsXREAidmDopJ2pZ3Wvk4rXuTYHZEW2vsreLYCrXDkTCm4ySL2THlOrzc0JQh/4EYRaQx+v3VqVBJvY9+qPLIm1Y9RuRoN35SMNN/IcTkxHVue+mDu6I8IIq/QVmg8kKDbwQ/ywQGzegYt+P2lKujdx0sR3gbXAHX2sTDHCKncVu/PYLJF5/LoxhVxNc3s3QEo5Bw=="
Content-Type: application/activity+json

发送确认

最后,我们可以更新标题并将消息发布到用户的收件箱:

$headers = [
    'Host' => $host,
    'Date' => $date,
    'Digest' => $digest,
    'Signature' => $signatureHeader,
    'Content-Type' => 'application/activity+json',
];

$activityResponse = Http::withHeaders($headers)
    ->withBody($document_str)
    ->contentType('application/activity+json')
    ->post($inboxUrl, $document);

包起来

成功发送了发布请求后,您应该能够接受关注者。但是,跟踪您的追随者将取决于您!