Комментарии 32
А есть какие-нибудь готовые библиотеки для популярных языков, чтобы не заниматься самому подсчётом подписей, ответами на webfinger итд? Хочу получать Активити в виде высокоуровневых событий на любимом языке программирования и дальше уже что-то с ними делать.
Реализаций полно, в т.ч. и на питоне, и даже на C вроде есть, но это всё на практике оказалось не самое трудоёмкое. Приходилось ещё разбираться почему с мастодоном одно работает, а с френдикой или пиксельфедом нет и т.п.
Как относится ActivityPub к протоколу WebSub? Это одно и тоже или разные вещи?
Разные. Общее только то, что и то, и другое - подписка на рассылку обновлений.
А что понимается под "происходящими в интернете событиями"? ActivityPub — это на 99% про социальные штуки всякие, тот же мастодон. Существуют мосты в другие протоколы, но именно чтобы нативная поддержка — это именно соцсети. Ну и вордпресс, с недавнего времени, если владелец сайта включил.
WebSub для меня существует сугубо теоретически, использования его на практике я (пока?) не видел.
Мне нужна какая нибудь замена RSS с поддержкой push, например, на вебхук. Чтобы можно было получать уведомления о происходящем в популярных соцсетях на определённые темы
Круто! Вот бы и на Хабре появилась поддержка ActivityPub!
Я как-то хотел свою реализацию написать, но стал спотыкаться на подписях заголовков... Мастодон посылал меня с такими подписями. После чего я решил отложить это дело на лучшие времена и остался на френдике, которая сейчас какого-то фига (лично у меня, а возможно и вовсе вопросы хостеру нужно задавать, а не воркеру) активити не отправляет.
Ну а статья полезная, оставлю в закладках ?
Непонятно, а как собственно писать свои посты или туты, как принято их называть в мастодоне, и сохранять у себя в профиле, чтоб другие могли прийти и почитать, о чём я пишу?
Писать — это как раз Create{Note}, который описан в статье. Сохранять в профиле — это outbox. Можете в мастодоне посмотреть, это коллекция с пагинацией со всеми вашими исходящими активити. Сам мастодон, правда, её с других серверов не прогружает.

Кстати, а есть кто прогружает? А то я у себя вроде и реализовал, но так и не тестил, судя по логам сервера ни outbox, ни реплаи к комментам спросом не пользуются другими инстансами, кроме каких-то ботов туда вроде никто не обращается.
...на тот же адрес, по которому вы видите профиль в браузере:
Профиль чей и где? Непонятно. У меня вообще нет никакого профиля. Я только экспериментирую. Я так понял надо вместо mastodon.social указать свой домен?
Не понятно что за ACTOR_ID когда надо формировать заголовок Signature. Решил ставить URL до своего профиля.
В общем, с третьего захода удалось сгенерировать корректную подпись и начать правильно отдавать информацию о своём акторе. Но выскакивает ошибка Вашего сервера.
Unhandled exception: smithereen.exceptions.InternalServerErrorException: java.sql.SQLIntegrityConstraintViolationException: Duplicate entry 'SaemonZixel-y777.ru' for key 'users.username'
Тестирую вот отсюда: http://y777.ru/test-send-activity
Код на PHP следующий:
$body = '{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "http://y777.ru/createTestPost1",
"type": "Create",
"actor": "http://y777.ru/users/saemonzixel",
"to": "https://www.w3.org/ns/activitystreams#Public",
"object": {
"id": "http://y777.ru/testPost1",
"type": "Note",
"published": "2023-11-05T12:00:00Z",
"attributedTo": "http://y777.ru/users/saemonzixel",
"to": "https://www.w3.org/ns/activitystreams#Public",
"inReplyTo": "https://friends.grishka.me/posts/884435",
"content": "<p>Привет с Хабра!</p>"
}
}';
$date = date('D, j M Y H:i:s T');
$digest = "SHA-256=".base64_encode(hash('sha256', $body, true));
$toSign = "(request-target): post /users/1/inbox\nhost: friends.grishka.me\ndate: $date\ndigest: $digest";
$private_key = file_get_contents('activitypub.private.pem');
$binary_signature = "";
openssl_sign($toSign, $binary_signature, $private_key, 'sha256WithRSAEncryption');
$public_key_pem = file_get_contents('activitypub.public.pem');
$r = openssl_verify($toSign, $binary_signature, $public_key_pem, "sha256WithRSAEncryption");
echo "--- toSign ---\n$toSign\n--- openssl_verify() ---\n$r\n";
$context = stream_context_create(array(
'http' => array(
'method' => 'POST',
'content' => $body,
'header'=> "Date: $date\r\n" .
"Digest: $digest\r\n" .
"Signature: keyId=\"http://y777.ru/users/saemonzixel#main-key\",headers=\"(request-target) host date digest\",signature=\"".base64_encode($binary_signature)."\",algorithm=\"rsa-sha256\"\r\n" .
"Content-Type: application/jrd+json; charset=utf-8\r\n",
'ignore_errors' => '1'
),
"ssl"=>array(
"verify_peer"=>false,
"verify_peer_name"=>false,
)
));
$http_resp = file_get_contents("https://friends.grishka.me/users/1/inbox", false, $context);
echo "--- Response ---\n$http_resp\n\n----------------\n";
echo "Signature: keyId=\"http://y777.ru/users/saemonzixel#main-key\",headers=\"(request-target) host date digest\",signature=\"".base64_encode($binary_signature)."\",algorithm=\"rsa-sha256\"\r\n";
file_put_contents('activitypub.log', print_r($http_resp, true), FILE_APPEND);
exit;
Профиль чей и где? Непонятно. У меня вообще нет никакого профиля.
Это я имел в виду, что если вы хотите посмотреть объект актора с другого сервера. Например, с мастодона.
Unhandled exception: smithereen.exceptions.InternalServerErrorException: java.sql.SQLIntegrityConstraintViolationException: Duplicate entry 'SaemonZixel-y777.ru' for key 'users.username'
О, кажется, вы нашли у меня баг :)
В базе он есть, но с https://, а не http://, потому что я так-то нешифрованный http вообще не поддерживаю. Починил.
Но вообще, конечно, надо придумать, что делать для общего случая, когда в базе уже есть актор с таким username@domain, но с другим ID. Как минимум надо возвращать адекватную ошибку.
Теперь Ваш сервер отвечает: Bad request: In the absence of a valid LD-signature, HTTP signature must be made by the activity actor
Хотя я проверяю подпись публичным ключём и подпись корректная.
Может алгоритм не sha256WithRSAEncryption, а какой-то другой должен быть?
Алгоритм правильный, я бы перепроверил keyId
в заголовке, там тоже надо https://
Я перепроверил KeyId, там всё правильно, https нет. У меня на сервере нет и не будет поддержки https.
Я перепроверил подпись на джаве и подпись у меня генерируется правильная.
Вот код, которым проверял:
String message = "(request-target): post /users/1/inbox\nhost: friends.grishka.me\ndate: Sun, 19 Nov 2023 00:04:03 MSK\ndigest: SHA-256=VMA4hX2mi+Wa88vu97Q+JWHm8fWAO1s8xW2Jc7y72eQ=";
byte[] sign = Base64.getDecoder().decode("obYfri5qtRScx51y4JQsn7McQtTU22ZKNTjPWlF7Muq8aQuezlGMsDX7Pwuss7OnLyPrTwMhb2fOBEzsHzWIEtBBD1Le4YDlHBweADTMOX0YymCoAXu4DQezo17U8g3eaM3eNHkI8o1Toh0dkjmTTKg0aciRswwvFDVXxUz99au1UhDk3b68uG2YIw9r3dAuZQTk2SYIliMNSy9K5scc1aoTz87cpl+A/RQm2GXYnp5vml8rzb7J69rOaFckSfRbIMf1sSaiAKR4Ivmt4ltVn1VR+2rwyRoxTGKinmOiJHqx3AwAhCS3rf5yUAHT/tBiTxja4o/SVgN0SaFAYq1NaA==");
// 1 - reading public key :
Scanner scanner = new Scanner(new File("activitypub.public.pem"));
StringBuilder sb = new StringBuilder();
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
if (line.startsWith("---") == false)
sb.append( line );
}
// 2 - loading public key
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec( Base64.getDecoder().decode(sb.toString()));
PublicKey publicKey = KeyFactory.getInstance("RSA").generatePublic(publicKeySpec);
// 3 - verifying content with signature and content :
Signature sig = Signature.getInstance("SHA256withRSA");
sig.initVerify(publicKey);
sig.update(message.getBytes());
if (sig.verify(sign) == false)
System.out.println("Bad signature!");
else
System.out.println("OK!");
У меня на сервере нет и не будет поддержки https.
Так тогда ничего и работать не будет. Все ActivityPub-сервера требуют HTTPS, по-моему это даже часть спецификации. Я потому и написал в статье, что в случае, если нет возможности настроить его полноценно у себя, можно воспользоваться ngrok или каким-нибудь другим аналогичным прокси-сервисом.
Тогда я зря потратил свои силы и время. Печалька.
Похоже что с activitypub мне не по пути.
А в чём проблема с HTTPS, если не секрет?
HTTPS тормозной протокол. С ним сайты открываются медленнее. Я не хочу чтоб мои сайты открывались и работали медленно.
А с точки зрения безопасности: мне нечего скрывать. У меня ничего ценного нет. Да и мнения я о себе невысокого. Кому я вообще нужен?))
Можно конечно заморочится с самоподписным сертификатом, но мне пока-что лень.
Вы, кстати, не знаете, есть ли какой-нибудь стандартизованный способ узнать кто администратор инстанца и как с ним связаться?
HTTPS тормозной протокол. С ним сайты открываются медленнее.
На сколько миллисекунд медленнее? Вы экономите на спичках.
На типичном инстансе потери времени на работу с БД значительно превысят задержки от хендшейков и шифрования. А сессионное шифрование давно аппаратно ускоряемо на CPU и затраты на него заметить будет невозможно без замеров.
А с точки зрения безопасности: мне нечего скрывать.
Если бы вам было что скрывать - вы бы это не выкладывали на публично доступный сайт. Вопрос в том хотят ли пользователи, которые туда пришли чтобы их трафик видели.
Можно конечно заморочится с самоподписным сертификатом, но мне пока-что лень.
Зачем? Let's Encrypt, ZeroSSL и т.п. на что существуют? Берёте простой acme.sh и через несколько минут у вас автоматически обновляющийся сертификат.
Вы, кстати, не знаете, есть ли какой-нибудь стандартизованный способ узнать кто администратор инстанца и как с ним связаться?
У инстансов поддерживающих Mastodon-совместимый API (это делает не только Mastodon) есть такой метод:
GET /api/v2/instance
Например:


Однако конкретно этот метод в основном есть только в Mastodon. Если мне не изменяет, в Misskey и Pleroma его нет.
Я так вижу, Вы хотите начать спор, который превратиться в "срач". Простите, нет. Я не люблю такие вещи. Я за конструктивное общение уважающих друг друга людей. И ещё, я за свободный мир и предлагаю остаться при своих мнениях, убеждениях, взглядах на мир и ограничиться только темой статьи)
За подсказку: как найти контактную информацию админа инстанса - спасибо)
Правда, к сожалению, не все pixelfed-инстансы умеют отвечать на это. Например https://insta.sdns.club/api/v2/instance выдаёт 404-страницу(
А с точки зрения безопасности: мне нечего скрывать. У меня ничего ценного нет. Да и мнения я о себе невысокого. Кому я вообще нужен?))
Есть. Ваш сайт которым кто-то пользуется. Можно например (совсем без вашего ведома):
Разместить на нем рекламу - см например https://habr.com/ru/articles/506218/ но это НЕ единственный пример (в тяжелых случаях - возможно что с вопросами по этой рекламе - придут к вам)
У вас с сайта можно хоть что-то скачать что пользователь может запустить? Ну вот будет теперь скачиватся то что что надо Кому То (атака против конкретного пользователя или группы пользователей а не вашего сайта, который только средство)
На вашем сайте больше одной странице и информация о визите туда может быть (пусть даже психом с диагнозом) расценена как что-то плохое? Вы всем провайдерам по пути (а также всем владельцам middlebox'ов) отдали информацию о том, какие страницы посещает ваши пользователи и даете собрать на них профиль
Пишем минимальный ActivityPub-сервер с нуля