Un site web hébergé sur Bluesky
(danielmangum.com)- Daniel Mangum montre qu’en exploitant l’upload de blobs et la structure de référence des records de l’AT Protocol, il est possible de servir un fichier
text/htmltéléversé sur un PDS Bluesky comme une page web qui s’ouvre dans le navigateur - Le mécanisme clé est que le blob téléversé n’est pas rendu public immédiatement : il faut qu’un record référence le blob pour que le PDS le fasse passer dans un état accessible publiquement
- Les embeds d’image Bluesky classiques n’acceptent que les types MIME
image/*, mais un record avec un lexicon inconnu ou un embed personnalisé fondé sur une open union peut stocker une référence vers un blobtext/html getBlobest accessible sans authentification, et les requêtes versbsky.socialsont redirigées vers le vrai PDS,porcini.us-east.host.bsky.network, qui renvoie alors le HTML- Bluesky applique des en-têtes CSP et
nosniffàgetBlob, et limite par défaut les uploads de blobs à 5 Mo, mais le service d’hébergement PDS conserve la charge d’un hébergement de fichiers arbitraires ainsi que les coûts de stockage et de transfert
Comment publier un site web avec un blob AT Protocol
- Ce cas repose moins sur les fonctionnalités classiques de l’app Bluesky que sur le fonctionnement de l’AT Protocol et de l’API PDS
- Bluesky fournit un PDS entryway qui accède au PDS de l’utilisateur et expose plusieurs instances PDS sous le domaine
bsky.social - Il est aussi possible d’accéder directement à chaque instance PDS, et le site d’exemple s’ouvre via l’endpoint
com.atproto.sync.getBlobdeporcini.us-east.host.bsky.network - L’auteur a échangé avec l’équipe Bluesky avant publication ; ce comportement n’est pas un usage applicatif prévu, mais il est connu et ne relève pas d’une procédure de divulgation de vulnérabilité
Pourquoi records et blobs sont séparés
- Dans l’AT Protocol, les données applicatives se divisent en deux grandes catégories : les records et les blobs
- les records sont les entités principales créées par l’utilisateur, avec leur structure et leurs métadonnées
- les blobs sont des données non structurées plus volumineuses, comme des images, et ne sont généralement exposés que lorsqu’un record les référence
- Quand on crée un post avec image dans Bluesky, cela semble être une seule action côté utilisateur, mais au niveau de l’API, l’upload du blob et la création du record sont distincts
- D’après la spécification des blobs, un blob doit d’abord être téléversé vers le PDS avant qu’un record puisse le référencer
- Au moment de l’upload, le serveur ne sait pas encore dans quel Lexicon le blob sera utilisé ; il n’applique donc d’abord que les restrictions générales sur les blobs, puis les restrictions fondées sur le Lexicon au moment de la création du record
- Un blob téléversé avec succès est placé dans un stockage temporaire ; dans cet état, il ne peut ni être téléchargé ni être diffusé, et n’apparaît pas non plus dans la sortie de
listBlobs - Si la création du record réussit et que ce record référence le blob, le serveur fait alors passer ce blob dans un état accessible publiquement
Upload d’un blob HTML et passage en accès public
- Le jeton d’authentification s’obtient en échangeant les identifiants de l’utilisateur via la méthode XRPC
com.atproto.server.createSession - Dans l’exemple,
com.atproto.sync.listBlobsa servi à vérifier le nombre de blobs existants, qui était de391avant l’upload - Le contenu du site web était un simple fichier
index.html, téléversé viacom.atproto.repo.uploadBlobContent-Typeétaittext/html- les métadonnées du blob renvoyées incluaient le CID
bafkreic5fmelmhqoqxfjz2siw5ey43ixwlzg5gvv2pkkz7o25ikepv4zeq,mimeType: text/html,size: 268
- Juste après l’upload, une tentative de récupération via
com.atproto.sync.getBlobéchouait, et le total delistBlobsrestait à391 - Si l’on référence le blob HTML comme embed d’image dans un post Bluesky, cela échoue car le schéma
app.bsky.embed.imageexige un type MIMEimage/*- en laissant
text/htmltel quel, l’erreur renvoyée estWrong type of file. It is text/html but it must match image/*. - en remplaçant
mimeTypeparimage/jpeg, une autre erreur indique que le type MIME du blob stocké ne correspond pas au type MIME de la référence
- en laissant
Rendre un blob public avec un record au lexicon inconnu
- Le type blob n’est pas propre à Bluesky, il fait partie du modèle de données de l’AT Protocol
- L’implémentation PDS de Bluesky est open source, et
findBlobRefsparcourt récursivementLexValuepour trouver les références$type: blob - Comme le PDS doit pouvoir prendre en charge de nouveaux lexicons avec le temps, il doit aussi être capable de traiter ceux qu’il ne connaît pas
- En créant un record de type
com.danielmangum.hack.websitequi référence le blob HTML, le PDS a bien enregistré le record- la réponse incluait
validationStatus: unknown - le PDS ne connaissait pas le lexicon
com.danielmangum.hack.*, donc il ne pouvait pas le valider, mais l’enregistrement du record restait autorisé
- la réponse incluait
- Ensuite, le nombre de blobs dans
listBlobsest passé à392, ce qui confirme que le blob a été conservé de manière persistante getBlobpeut être appelé sans jeton d’authentification, car des acteurs non authentifiés qui traitent les blobs avec les records doivent aussi pouvoir récupérer ces blobs- L’appel à
getBlobviabsky.socialrenvoyait une redirection 302 vers l’URL du vrai PDS, et en suivant la redirection avec-L, le contenu HTML était retourné tel quel
En-têtes de sécurité, CDN et coût de l’accès direct
- Les considérations de sécurité de la spécification des blobs de l’AT Protocol traitent des risques de sécurité liés au fait de servir des fichiers arbitraires téléversés par les utilisateurs depuis un serveur web
- l’exemple classique est un problème de XSS, quand du script ou du contenu SVG s’exécute depuis la même origin
- l’application d’une Content Security Policy sur l’endpoint
getBlobest pratiquement indispensable - fournir directement le stockage de blobs aux navigateurs et agents web n’est, dans les faits, pas pris en charge ; les applications doivent passer par un CDN distinct, un proxy ou un service web dédié
- Le handler
getBlobdu PDS Bluesky applique les en-têtes de sécurité recommandésx-content-type-options: nosniffcontent-security-policy: default-src 'none'; sandbox
- La limite par défaut d’upload de blobs est de 5 Mo
- Les blobs d’images ordinaires de l’application Bluesky ne sont pas servis directement par le PDS, mais via des URL CDN sur
cdn.bsky.app- il existe des URL distinctes pour les miniatures du feed et pour la taille complète
- le record du post inclut le CID de l’image, et l’application doit savoir comment ce contenu est servi par le CDN
- L’accès direct via
getBlobsubsiste, d’après un ticket GitHub, pour des usages comme le labellisation d’images, l’export de contenu utilisateur et de futurs cas d’usage- la même discussion mentionne aussi la possibilité que des utilisateurs fassent du hotlinking vers leur contenu ou utilisent Bluesky comme un hébergement gratuit
- dans l’implémentation initiale, les en-têtes de sécurité n’étaient pas présents, puis ils ont été ajoutés ensuite
- Les plateformes sociales traditionnelles limitent davantage les types de contenus valides, ce qui leur permet d’imposer des restrictions plus strictes sur les blobs dès l’upload ; mais l’extensibilité de Bluesky et de l’AT Protocol impose un traitement plus complexe
Ajouter un embed personnalisé à un post via open union
- Le type
app.bsky.feed.postdéfinit la liste des embeds valides sous forme d’union - Dans le lexicon de l’AT Protocol, une union est par défaut open tant qu’elle n’est pas explicitement marquée
closed- comme de nouveaux types peuvent être ajoutés lors de futures révisions du schéma, les implémentations doivent rester permissives lors de la validation
- si le drapeau
closedest présent, l’ensemble des types est figé
- L’union des embeds d’un post Bluesky n’est pas marquée
closed - Il est donc possible de créer un post avec un embed
$typequi n’est pas énuméré - Dans l’exemple, un embed
com.danielmangum.hack.sitesa été placé dans un recordapp.bsky.feed.post, et cet embed référençait le blob HTMLvalidationStatusde la réponse étaitvalid- dans l’application Bluesky, cet embed est simplement ignoré
- le contenu et la référence restent enregistrés durablement dans le record, ce qui permet à d’autres applications de rendre cet embed
- Ce type de propriété du lexicon peut servir à ajouter de petites extensions par-dessus des cas d’usage existants ; un exemple envisagé serait de faire exécuter de petits fragments de code dans un post au sein d’un sandbox WebAssembly
1 commentaires
Avis sur Hacker News
J’ai apprécié que Daniel se soit renseigné directement auprès de l’équipe, et l’hébergement de blobs est un domaine qui devra forcément continuer à être ajusté à mesure que l’on comprend mieux les vecteurs d’abus
Pour l’instant, c’est amusant de voir ce genre d’usage fonctionner réellement, et l’architecture prévue est bien que le PDS devienne un hôte de base de données, comme un serveur web héberge un site web
C’était sympa à bidouiller à l’époque, et je ne savais pas qu’il avait migré vers Bluesky ; c’est assez intéressant
Bsky prend déjà en charge une méthode consistant à rattacher le domaine utilisateur à redirect.bsky.com via un ALIAS : https://bsky.app/profile/jacob.gold/post/3kh6rnpdzmp2v
Je me suis demandé dans quel contexte de sécurité cela s’exécutait, donc j’ai vérifié une URL de blob PDS avec
curl, et les en-têtes de réponse contenaient notammentaccess-control-allow-origin: *,content-security-policy: default-src 'none'; sandbox,x-content-type-options: nosniffaccess-control-allow-origin: *est intéressant, car cela signifie que le JavaScript d’une page web de n’importe quel domaine peut accéder à ce contenu avecfetch(), tandis quedefault-src 'none'; sandboxest une configuration très restrictive et appréciable, qui empêche de charger des scripts ou images supplémentaires et bloque aussi l’exécution de JavaScript : https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Co...ratelimit-limit: 3000semble probablement être une limite par IPLa suggestion, en fin d’article, d’utiliser WebAssembly va dans le bon sens, et le problème des clics sur les liens peut être limité en plaçant le code non fiable dans une iframe, puis en n’autorisant depuis le parent, via
child-src, que certains domaines oudata:https://github.com/w3c/webappsec/issues/656#issuecomment-246...
https://www.w3.org/TR/CSP3/#exfiltration
default-srcest nécessaire même quand on utilisesandbox, ou si c’est une configuration redondanteJe suis assez enthousiaste à l’idée d’utiliser Bluesky comme stockage de données blob
Avec un ami, on a imaginé stocker des WAD DOOM sur Bluesky et partager des « packs de cartes » comme des publications ; en suivant des comptes, des listes ou des starter packs, on pourrait modifier un client comme GZDoom pour rechercher et afficher les WAD publiés par ces comptes
Ce serait similaire au Steam Workshop, mais en passant par Bluesky
Je ne sais pas non plus si Bluesky a déjà évoqué des consignes d’usage pour le stockage de blobs ; les fichiers DOOM sont petits, donc c’est un bon exemple, mais je me demande aussi si cela pourrait être détourné pour externaliser de gros volumes d’espace serveur
Je n’ai pas connu cette époque précise, mais voir les utilisateurs de BlueSky se jeter dans des eaux techniques profondes me rappelle l’époque où l’on apprenait ses premiers bouts de HTML sur MySpace
Le marché des réseaux sociaux est bien plus saturé qu’avant, mais je me demande si une nouvelle génération de programmeurs pourrait émerger de BlueSky
Bluesky ne propose que la première étape, puis la marche suivante semble assez grande
Avec HTTP/HTML, on peut montrer à quelqu’un qui ne connaît que les bases de Python ou d’un autre langage de programmation comment créer un serveur à partir de zéro en 14 minutes
Je pense qu’il faut un protocole de communication à adressage par hash pour empêcher les géants de la tech d’exploiter les communautés, mais je ne suis pas sûr qu’AT Protocol ait aussi bien calibré son objectif que HTTP pour déclencher une révolution
L’un des points clés est que le PDS évoqué ici fait partie du Personal Data Server, et non du produit Bluesky lui-même, ce qui peut le faire ressembler, au final, à un stockage de données gratuit et illimité
Le service d’abonnement dont l’équipe Bluesky a parfois parlé pourrait être lié à ce genre de choses, et un hébergement offrant davantage d’espace, de bande passante ou de vidéo haute qualité sur un PDS semble bien se prêter à une offre premium
Il devrait être possible de l’héberger ailleurs aussi, donc l’idée qu’on puisse interagir au-delà de l’app Bluesky semble correcte
Le phénomène par lequel, dès qu’on permet de mettre quelque chose sur Internet, quelqu’un finit toujours par essayer d’y faire de l’hébergement de fichiers arbitraires, mériterait sans doute un nom
À l’époque où c’était illimité, plusieurs projets open source étaient apparus pour sauvegarder des systèmes de fichiers dans Gmail, entre autres, et cela s’est encore aggravé après l’arrivée de Drive
Les services gratuits doivent anticiper ce genre de problème et imposer des limites par défaut
https://en.wikipedia.org/wiki/Inner-platform_effect
Si ce sujet vous intéresse, atfile vaut aussi le détour : https://github.com/electricduck/atfile
C’est plutôt impressionnant, et on peut consulter directement l’intéressante issue GitHub liée en bas de l’article
La personnalité bien connue de Bluesky, pfrazee, y apparaît aussi : https://github.com/bluesky-social/atproto/issues/523
J’ai beaucoup d’attentes vis-à-vis d’AT, et il y a aussi beaucoup de gens brillants qui ont fait un excellent travail dans le Fediverse, mais ce paradigme semble plus durable et plus réaliste
L’idée est de fournir par défaut un service centralisé, tout en offrant une vraie prise en charge de la décentralisation quand c’est nécessaire ou pour les utilisateurs avancés
Il faut trouver un moyen de générer un retour sur investissement avec le mirroring du firehose ; par exemple, une alternative à Discord où certains utilisateurs paient pour l’hébergement de vidéos plus longues, une structure à la Patreon où les relais gèrent l’accès et les clés de déchiffrement en échange d’une commission, ou encore une marketplace sociale à la Bandcamp semblent possibles
En tout cas, puisque c’est une plateforme ouverte, il n’y a aucune raison de ne pas pouvoir le faire dès maintenant
https://www.blockchaincapital.com/blog/bluesky-13m-users-and...
https://bsky.social/about/blog/09-11-2024-video
J’ai l’impression que cette approche risque d’être détournée pour du phishing ou la distribution de malwares
Quand on crée un site d’hébergement, quelqu’un essaiera de l’utiliser à des fins malveillantes : cela ressemble presque à une loi de l’informatique
Le même problème existait avec les pièces jointes de Matrix
*.bsky.networkcommencera probablement à être bloqué par Google Safe Browsing, Palo Alto, Bluecoat, etc.Ici, c’est un lien direct vers le serveur Bluesky de l’auteur, c’est-à-dire vers son PDS, donc il est évidemment sous le contrôle de l’auteur
Les récents changements d’API de Strava m’ont rappelé à quel point l’accès aux données stockées sur leur plateforme est limité
Étant donné que c’est le service dominant dans le domaine du fitness, ils pourraient progressivement placer des fonctionnalités derrière un abonnement
Il y aurait certes des préoccupations de confidentialité ou de sécurité, mais je me demande si l’AT Protocol pourrait convenir comme plateforme de stockage de fichiers GPX ou FIT
Cela dit, à ma connaissance, l’AT Protocol ne prend pas encore en charge les publications privées ou à visibilité restreinte, et je pense que cette fonctionnalité est assez essentielle pour un usage de type Strava