3 ポイント 投稿者 GN⁺ 2024-11-25 | 1件のコメント | WhatsAppで共有
  • Daniel Mangumは、AT Protocolのblobアップロードとレコード参照構造を利用して、Bluesky PDSにアップロードしたtext/htmlファイルを、ブラウザで開けるWebページのように提供できることを示した
  • 中核となる動作は、アップロードされたblobがすぐには公開されず、何らかのrecordがblobを参照して初めてPDSが公開アクセス可能な状態に切り替える点にある
  • 通常のBluesky画像embedはimage/* MIMEタイプのみ許可するが、未知のlexiconのrecordやopen unionベースのカスタムembedではtext/html blob参照を保存できる
  • getBlobには認証なしでアクセスでき、bsky.socialへのリクエストは実際のPDSであるporcini.us-east.host.bsky.networkへリダイレクトされてHTMLを返す
  • BlueskyはgetBlobCSPヘッダーnosniffを適用し、デフォルトのblobアップロードを5MBに制限しているが、PDS運用サービスには任意ファイルのホスティングや保存・転送コストの負担が残る

AT Protocol blobでWebサイトを公開する方法

  • この事例はBlueskyアプリの通常機能というより、AT ProtocolとPDS APIの動作仕様に基づくもの
  • BlueskyはユーザーのPDSにアクセスするためのPDS entrywayを提供しており、複数のPDSインスタンスをbsky.socialドメインで公開している
  • 個々のPDSインスタンスにも直接アクセスでき、例のWebサイトはporcini.us-east.host.bsky.networkcom.atproto.sync.getBlobエンドポイントで開かれる
  • 記事公開前にBlueskyチームと連絡を取り、この挙動は意図されたアプリケーションの使い方ではないものの既知の動作であり、脆弱性開示手続きには当たらないことを確認した

recordsとblobsが分離されている理由

  • AT Protocolでは、アプリケーションデータは大きくrecordsblobsに分かれる
    • recordsはユーザーが作成する中核エンティティで、構造やメタデータを持てる
    • blobsは画像のような、より大きく非構造なデータで、通常はrecordから参照されて初めて公開される
  • Blueskyで画像付き投稿を作成すると、ユーザーには一度の操作に見えるが、APIレベルではblobアップロードとrecord作成が分離されている
  • blob specificationによれば、blobはrecordから参照される前にまずPDSへアップロードされなければならない
  • サーバーはアップロード時点ではそのblobがどのLexiconで使われるか分からないため、初期アップロード時には一般的なblob制限だけを適用し、record作成時点でLexiconベースの制限を適用する
  • 正常にアップロードされたblobは一時ストレージに入り、この状態ではダウンロードや配布はできず、listBlobsの出力にも含まれない
  • record作成が成功し、そのrecordがblobを参照すると、サーバーはblobを公開アクセス可能な状態へ切り替える

HTML blobのアップロードと公開切り替え

  • 認証トークンはcom.atproto.server.createSession XRPCメソッドでユーザー資格情報を交換して取得する
  • 例ではcom.atproto.sync.listBlobsで既存blob数を確認しており、アップロード前の数は391個だった
  • Webサイト本体はシンプルなindex.htmlファイルで、com.atproto.repo.uploadBlobでアップロードされる
    • Content-Typetext/html
    • 返されたblobメタデータにはCID bafkreic5fmelmhqoqxfjz2siw5ey43ixwlzg5gvv2pkkz7o25ikepv4zeqmimeType: text/htmlsize: 268が含まれる
  • アップロード直後にcom.atproto.sync.getBlobで取得しようとしてもアクセスできず、listBlobsの件数も391のままだった
  • Bluesky投稿の画像embedとしてHTML blobを参照すると、app.bsky.embed.imageスキーマがimage/* MIMEタイプを要求するため失敗する
    • text/htmlのままだとWrong type of file. It is text/html but it must match image/*.というエラーが発生する
    • mimeTypeimage/jpegに変えると、保存済みblobのMIMEタイプと参照MIMEタイプが異なるというエラーになる

未知のlexicon recordでblobを公開する

  • blob型はBluesky専用ではなく、AT Protocolデータモデルの一部
  • Bluesky PDS実装はオープンソースで、findBlobRefsLexValueを再帰的に探索して$type: blob参照を見つける
  • PDSは時間の経過とともに新しいlexiconをサポートする必要があるため、自身が知らないlexiconも処理できなければならない
  • com.danielmangum.hack.website型のrecordを作成してHTML blobを参照させると、PDSはrecordを保存した
    • 応答にはvalidationStatus: unknownが含まれる
    • PDSはcom.danielmangum.hack.* lexiconを認識していないため検証できなかったが、recordの保存は許可された
  • その後listBlobsの件数は392に増え、blobが永続保存されたことが確認できた
  • getBlobは認証トークンなしで呼び出せる。recordとともにblobを扱う非認証主体もblobを取得できる必要があるためだ
  • bsky.socialgetBlob呼び出しは302リダイレクトで実際のPDS URLを返し、-Lでリダイレクトを追うとHTML内容がそのまま返された

セキュリティヘッダー、CDN、直接アクセスの負担

  • AT Protocol blob仕様のSecurity Considerationsでは、任意のユーザーアップロードファイルをWebサーバーから配信する際に生じるコンテンツセキュリティ上のリスクを扱っている
    • 代表例は、同一originでスクリプトやSVGコンテンツが実行されるXSS問題
    • getBlobエンドポイントにはContent Security Policyの適用が事実上必須
    • ブラウザやWebエージェントにblobストアを直接提供する方式は実質的にサポートされておらず、アプリケーションは独立したCDN、プロキシ、Webサービスなどを経由すべきとされる
  • Bluesky PDSのgetBlobハンドラは推奨セキュリティヘッダーを適用している
    • x-content-type-options: nosniff
    • content-security-policy: default-src 'none'; sandbox
  • デフォルトのblobアップロード上限は5MB
  • Blueskyアプリケーションの一般的な画像blobはPDSから直接配信されず、cdn.bsky.appのCDN URLとして提供される
    • feed thumbnail URLとfull size URLが別々に存在する
    • 投稿recordには画像CIDが含まれ、アプリケーション側がCDN提供方式を理解している必要がある
  • getBlobへの直接アクセスは、GitHub issue上で画像ラベリング、ユーザーコンテンツのエクスポート、将来のユースケースのために残されている
    • 同じ議論には、ユーザーがコンテンツをhotlinkしたりBlueskyを無料ホスティングのように使ったりする可能性も含まれている
    • 初期実装にはセキュリティヘッダーがなく、後から追加された
  • 従来型のソーシャルプラットフォームでは有効なコンテンツ種類が限定されているため、アップロード時点でblobにより強い制限をかけられるが、BlueskyとAT Protocolの拡張性はより複雑な処理を要求する

open unionで投稿にカスタムembedを入れる

  • app.bsky.feed.post型は有効なembed一覧をunionで定義している
  • AT Protocol lexiconではunionは明示的にclosedでない限り、デフォルトでopen
    • 将来のスキーマ改訂で型が追加されうるため、実装は検証時に寛容であるべき
    • closedフラグがある場合は型集合が固定される
  • Bluesky投稿のembed unionはclosedと記されていない
  • そのため、列挙されていないembed $typeでも投稿を作成できる
  • 例ではapp.bsky.feed.post record内にcom.danielmangum.hack.sites embedを入れ、その中でHTML blobを参照している
    • 応答のvalidationStatusvalid
    • Blueskyアプリケーションでは当該embedは静かに無視される
    • recordにはコンテンツと参照が永続保存されるため、他のアプリケーションはこのembedをレンダリングできる
  • このようなlexicon特性は、既存のユースケースの上に小さな拡張を付け足す形で活用でき、例として投稿内の小さなコード片をWebAssemblyサンドボックスで実行するような形も可能性として挙げられている

1件のコメント

 
GN⁺ 2024-11-25
Hacker News の意見
  • Daniel がチームに直接問い合わせてくれたことはありがたく、Blob ホスティングは悪用経路をさらに理解しながら調整を続けざるを得ない領域だと思う
    今はこうした活用が実際に動いているのを見るのが面白いし、PDS は Web サーバーが Web サイトをホストするように、データベースホストになることを意図した構造になっている

    • Beaker Browser を復活させる計画があるのか気になる
      以前いじって遊ぶにはよかったし、Bluesky に移ったとは知らなかったので、かなり興味深い
    • コンテンツが bsky のような特定の AppView を経由せず、ユーザー自身のドメインにつながるなら、悪用の可能性は下がるのではないかと思う
      Bsky はすでに、ユーザードメインを redirect.bsky.com に ALIAS として紐づける方法をサポートしている: https://bsky.app/profile/jacob.gold/post/3kh6rnpdzmp2v
    • Beaker は追いかけるのが本当に素晴らしいプロジェクトだったし、その経験は Bluesky でかなり役立ちそう
    • この方式は、著作権、CSAM、ポルノのようなコンテンツモデレーションの落とし穴に正面から突っ込むようなものだ
  • これがどのようなセキュリティコンテキストで実行されるのか気になって、curl で PDS の Blob URL を確認してみたところ、レスポンスヘッダーには access-control-allow-origin: *content-security-policy: default-src 'none'; sandboxx-content-type-options: nosniff などがあった
    access-control-allow-origin: * は、どのドメインの Web ページの JavaScript からでも fetch() でこのコンテンツにアクセスできるという意味なので興味深く、default-src 'none'; sandbox は追加のスクリプトや画像を読み込めないようにし、JavaScript の実行も防ぐ非常に制限の強い設定なのでよい: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Co...
    ratelimit-limit: 3000 は、おそらく IP ベースの制限に見える

    • JavaScript をすべてブロックするか許可リストで制限することが、CSPでアプリを完全に閉じ込めてデータ流出を防ぐ、ほぼ唯一の方法であり、prefetch があるとそれでも十分でない場合がある
      記事の末尾で WebAssembly の利用を提案しているのは正しい方向で、リンククリックの問題は、信頼できないコードを iframe に入れ、親側で child-src によって特定ドメインや data: のみを許可する形で制限できる
      https://github.com/w3c/webappsec/issues/656#issuecomment-246...
      https://www.w3.org/TR/CSP3/#exfiltration
    • sandbox を使う場合でも default-src が必要なのか、それとも重複設定なのか気になる
  • Bluesky をBlob データストアとして使える可能性にはかなり期待している
    友人と一緒に DOOM WAD を Bluesky に保存し、投稿のように「マップパック」を共有する方法を考えてみた。アカウント、リスト、スターターパックをフォローすれば、GZDoom のようなクライアントを改造して、それらのアカウントがアップロードした WAD を検索・閲覧できるようにできそうだ
    Steam Workshop に似ているが、Bluesky を介する方式だ

    • 面白いアイデアだが、これを自前でホストするつもりなのか、それとも Bluesky のサーバーに置くつもりなのか気になる
      Bluesky がBlob ストレージ用途のガイドラインに触れたことがあるのかも知らないし、DOOM ファイルは小さいのでよい例だが、大容量のサーバースペースを外部化するような形で悪用され得るのかも気になる
    • 結局、任意データ用のRSS フィードのように Bluesky を使うということではないかと思う
  • この特定の時代を経験したわけではないが、BlueSky ユーザーたちが技術的な深みへ飛び込んでいく様子は、MySpace で初めて HTML を学んでいた頃を思い出させる
    ソーシャルメディア市場は以前よりはるかに飽和しているが、BlueSky から新世代のプログラマーが出てくるのか気になる

    • MySpace や昔のフォーラムは、テキストをボックスに入れると Web ページに表示される段階から始まり、画像の追加、BBCode/マークアップ、HTML と CSS、完全なスクリプト作成へと続く抽象化のはしごを提供していた
      Bluesky は最初の段階だけを提供していて、その先はかなり大きな飛躍のように見える
    • 「14分相当」とはいうが、新しいアイデアを試すには必読に見えるリンクが12個ほどある
      HTTP/HTML なら、基本的な Python や他のプログラミング言語しか知らない人にも、14分以内にサーバーを一から作る方法を見せられる
      テック大企業がコミュニティを搾取できないようにするには、ハッシュアドレス指定通信プロトコルが必要だと思うが、AT Protocol が HTTP のように革命を起こすほど目標をうまく捉えているかは確信が持てない
  • ここで扱われている PDS は Bluesky 製品そのものではなく、Personal Data Server の一部なので、結果として無限の無料データストレージのように見え得る点が重要なポイントの一つだ
    Bluesky チームが時折言及しているサブスクリプションサービスは、こうした部分とつながる可能性があり、PDS でより多くの容量、帯域幅、高画質動画を提供するようなホスティングは、プレミアムティアとして売るのに適していそうだ

    • 正しく理解しているなら、この PDS は Bluesky がホストしているものだった
      他の場所でもホストできるはずなので、Bluesky アプリを超えて相互作用できるという話は正しそうだ
  • インターネットに何かを投稿できるようにすると、結局誰かがその上で任意ファイルホスティングを試みるという現象には、名前が必要そうです

    • Google アカウントが今では追加課金するまで15GB 制限になっている重要な理由も、おそらくそれでしょう
      無制限だった時代には、ファイルシステムを Gmail などにバックアップしようとするオープンソースプロジェクトがいくつも登場し、Drive ができてからはさらにひどくなりました
      無料サービスはこうした問題が起きることを見越して、基本的な制限を設けるべきです
    • すでに「parasitic computation」という言葉があるので、「parasitic data storage」と呼べそうです
    • Johnson の法則: あるものが多くの注目を集めるほど、その影響範囲も大きくなる
    • Inner Platform effectに近いです
      https://en.wikipedia.org/wiki/Inner-platform_effect
  • こういうテーマが面白いなら、atfileも見る価値があります: https://github.com/electricduck/atfile

  • かなりよくできていて、記事の下部にリンクされている興味深い GitHub Issue をすぐ見られます
    Bluesky の著名人 pfrazee も登場しています: https://github.com/bluesky-social/atproto/issues/523
    AT には大きく期待しており、Fediverse で素晴らしい仕事をした賢い人たちも多いのでしょうが、このパラダイムのほうがより持続可能で現実的に見えます
    基本は中央集権型で提供しつつ、必要なときや上級ユーザーには本当の分散化サポートを与える方式です

    • 持続可能性の面では、「Blockchain Capital から資金を受ける」より良いビジネスモデルを期待しています
      firehose ミラーリングから投資リターンを得る方法が必要で、たとえば一部ユーザーがより長い動画のホスティング費用を支払う Discord 代替、リレーがアクセスキーや復号鍵を管理して手数料を受け取る Patreon 的な構造、Bandcamp のようなソーシャルマーケットプレイスも可能に見えます
      いずれにせよオープンなプラットフォームなので、今でもできない理由はありません
      https://www.blockchaincapital.com/blog/bluesky-13m-users-and...
      https://bsky.social/about/blog/09-11-2024-video
  • この方式はフィッシングやマルウェア配布に悪用されそうな気がしませんか

    • 悪用されないホスティングサイトなんてあるのか、という気もします
      ホスティングサイトを作れば、誰かが悪意ある用途に使おうとする、というのはほとんどコンピューティングの法則のように見えます
    • 実際そうなるでしょう
      Matrix の添付ファイルでも同じ問題がありました
    • 5分ほど経てば、*.bsky.network が Google Safe Browsing、Palo Alto、Bluecoat などにブロックされ始めそうです
    • よく分かりません
      これは筆者の Bluesky サーバー、つまりPDS への直接リンクなので、当然筆者が制御しているものです
  • Strava の最近の API 変更を見て、彼らのプラットフォームに保存されたデータへのアクセスがどれほど制限されているかを思い出しました
    フィットネス分野の支配的なサービスであるだけに、機能を段階的にサブスクリプションの壁の内側へ入れていくこともあり得ます
    プライバシーや安全上の懸念はあるでしょうが、AT Protocol が GPX や FIT ファイルの保存プラットフォームとして適しているのか気になります

    • 連合型の Strava 代替があるといいですね
      ただし AT Protocol はまだ非公開または限定公開の投稿をサポートしていないと認識しており、Strava 用途ではこの機能がかなり重要だと思います