Read My Blog With JavaScript

Michael Lynch

JavaScriptでブログを読めるようにしました

クライアントサイドのJavaScriptで私のブログを読めるようになりました。なぜそうしたいのかは正直よくわかりませんが、できるようになりました。

たとえば、自分のサイトにお気に入りのブログの最新記事を一覧表示するブログロールを置きたいけれど、サーバー側で取得したくない場合があるかもしれません。JavaScriptで私のブログの最新5件のタイトルを表示したい場合は、次のようなコードになります。

fetch("https://mtlynch.io/index.xml")
  .then((response) => response.text())
  .then((str) => new DOMParser().parseFromString(str, "application/xml"))
  .then((data) => {
    const articles = [...data.querySelectorAll("item")].map((item) => ({
      title: item.querySelector("title").textContent,
      date: new Date(item.querySelector("pubDate").textContent),
    }));

    // Sort articles by date, newest to oldest.
    articles.sort((a, b) => b.date - a.date);

    // Print the titles of the 5 most recent articles.
    articles.slice(0, 5).forEach((article) => console.log(article.title));
  });

上記のコードを実行すると、次のような出力が得られます。

Refactoring English: Month 10
Get xkcd Cartoons at 2x Resolution
List of 2x-resolution xkcd Cartoons
I Once Appeared in The Old New Thing
Refactoring English: Month 9

このアイデアは、LobstersでのSimon Willison氏による最近のコメントから得ました。

JavaScriptからサイトを読めるようにするには

JavaScriptからのアクセスを可能にするために私が変更しなければならなかったのは、RSSフィードにCross-Origin Resource Sharing(CORS)HTTPヘッダーを設定することだけでした。通常、クライアントサイドのJavaScriptで他のサイトのコンテンツを読み取ることを妨げているのは同一オリジンポリシーです。

同一オリジンポリシーとは、各サイト(オリジン)が自身のドメインに紐づくリソースしか読み取れないというルールです。たとえば、あなたがmtlynch.ioで私のブログを訪れていて、私のページ上のJavaScriptがchase.comからあなたの銀行残高を読み取ろうとしても、同一オリジンポリシーがそのアクセスをブロックします。

JavaScript経由で他のサイトが私のブログを読めるようにするには、ブログのRSSフィードにCORSヘッダーを設定する必要がありました。このブログは現在Netlifyでホスティングしている静的サイトなので、Netlifyの設定ファイルを変更してRSSフィードのURLにCORSヘッダーを指定しました。

[[headers]]
  for = "/index.xml"
  [headers.values]
    Access-Control-Allow-Origin = "*"
    Access-Control-Allow-Methods = "GET, HEAD, OPTIONS"
    Access-Control-Allow-Headers = "Content-Type"

何が問題になりうるのか

CORSはセキュリティの仕組みであり、インターネットに公開されているサイトのセキュリティ制限を緩めることには、常に慎重であるべきだと考えています。ただ、しばらくリスクについて検討してみましたが、今回のケースでは特段のリスクは見当たりませんでした。

攻撃者に秘密のデータを盗み出される可能性はあるか

同一オリジンポリシーの目的は、ユーザーがサイトAに機密データを持っている場合に、サイトAが明示的に許可しない限りサイトBがそのデータにアクセスできないようにすることです。

私のブログは秘密情報を一切持たない静的サイトです。私がmtlynch.ioにアクセスしても、他の人が見るものとまったく同じものしか表示されません。ですから、たとえ攻撃者が私を騙してサードパーティのサイトで悪意のあるJavaScriptを実行させたとしても、そのコードが私のブログ上の権限を悪用することはありません。そもそも私自身、ここで特別な権限を持っていないからです。

埋め込みコメントにはTalkYardを使っていて、そこで私は管理者権限を持っていますが、RSSフィードのCORS設定がTalkYardに影響することはありません。

攻撃者にDDoS攻撃される可能性はあるか

当初は、攻撃者がCORS設定を悪用して私のサイトにDDoS攻撃を仕掛けるのではないかと考えました。もし攻撃者が何百万人もの人々を集めたページを用意し、裏側でJavaScriptが常に私のRSSフィードをリクエストし続けたらどうなるだろうか、と考えたのです。

しかし、そうしたDDoS攻撃はCORSに関係なくすでに可能だと気づきました。CORSが制御するのは、サードパーティのドメインがリクエストの結果を読み取れるかどうかだけで、そもそもリクエスト自体をブロックするわけではありません。CORSの設定にかかわらず、どのドメインからでも他のどのドメインに対してもGETやPOSTリクエストを送ることは可能です。

攻撃者にサイトを偽装される可能性はあるか

検討したもう一つの攻撃は、なりすましです。訪問者がevil.example.comにアクセスすると、そのサーバーがJavaScriptを返し、URLバーにはevil.example.comと表示されたまま、ブラウザ内で私のブログを再構築してしまう、というものです。

この攻撃は成立しうるものの、CORSの設定にかかわらず、私のブログを偽装するにはもっと簡単な方法があります。たとえば、リクエストを私のブログに転送するHTTPプロキシを立てる方がはるかに手軽です。あるいは、サイトをスクレイピングして別の場所でホストするだけでも偽装は可能です。

なぜJavaScriptからブログを読めるようにしたのか

誰かに頼まれてRSSフィードでCORSを有効にしたわけではないので、実際に誰かの役に立つのかどうかはわかりません。ただ、私はオープンなウェブが好きですし、他のサイトが私のサイトと連携できる面白い方法だと思ったので、やってみることにしました。

もし今回の変更をきっかけに、私のRSSフィードを使って何か面白いことをされたら、ぜひ教えてください

原文は Michael Lynch により に公開されました。

この記事は「muse-spark-1.2-contributor」を使用して翻訳されました。