SRI をビルドプロセスへ組みこもう

この記事は “How to implement SRI in your build process” の抄訳です。

顧客から「おたくのサイトがマルウェアを配布している」との連絡があった場合を想像してみてください。心臓は止まり、冷汗をかき、Tweet が溢れ始めるでしょう。

「あれ?おかしいぞ」

システムは汚染されていませんでした。

実際にハッキングされたのはWeb サイトで利用しているスクリプトを配布している CDN のプロバイダで、そのスクリプトがマルウェア化していたのでした。そこで顧客に事実を報告しましたが、彼らは気にしません。製品の安全性と、顧客からの信頼は失われてしまいました。これが 2 年前に起きたとしたら、

「気の毒なことだったね」

と言ったでしょう。しかし今ならこう言うでしょう:

「SRI を使うべきだ」

Subresource Integrity (SRI) は比較的新しい Web アプリケーションのセキュリティ標準であり、上記のような状況から身を守るための W3C 仕様です。SRI は Web サイトのコンテントセキュリティにおける、セーフティネットや命綱のようなものです。script タグや link タグに integrity 属性を追加し、その値に SHA-2 ハッシュ値を指定するだけで利用できます:

<script src="http://code.jquery.com/jquery-2.2.3.min.js" 
    integrity="sha256-a23g1Nt4dtEYOj7bR+vTu7+T8VP13humZFBJNIYoEJo=" 
    crossorigin="anonymous">
</script>

SRI を利用するためには CDN で CORS が有効になっていなければなりません。詳しくは SRI を紹介する Hacks ブログの記事をご覧ください。

対応状況

対応しているファイル種別

SRI の第 1 バージョンは CDN で配布される Cascading Styles Sheets (CSS) や JavaScript (JS) ファイルに対するサブリソースハイジャッキングによる大規模な攻撃への対応策として設計されました。今の所 Flash や画像、動画などはサポートされていませんが、将来の改定で使用に追加されることが期待されます。

ブラウザの対応状況

2016 年 4 月時点の CanIUse.com によると、モバイル、デスクトップを合わせて 52% 程度のブラウザが SRI に対応しています。デスクトップでは、Firefox (44 以降)、Chrome (47 以降)、Opera (36 以降)が対応しています。モバイルでは次にあげる Android 上でのブラウザのみが対応しています: Chrome(49 以降)、Android Browser(49 以降)、Opera(36 以降)、Firefox(45 以降)

付記:SRI は iOS 版 Firefox などのブラウザでは利用できません。これはブラウザやアプリ内での Web 閲覧を含め WebKit をブラウザエンジンとして利用することを、全てのアプリに対して Apple が要求しているためです。詳しくはこちらのステータスアップデートをご覧ください…

SRI をビルドプロセスに組みこもう

SRI をビルドプロセスに組み込む方法はいく通りもあります。GruntGulpBroccoliWebpackEmber CLIHandlebar といったツールには、すでにプラグインが用意されています。

実世界でのビルドプロセスを考慮して、Grunt プラグインである grunt-sri をハッシュ値生成に利用することにしました。

jQuery を利用する場合

code.jquery.com に対して SRI を適用する場合、 前述した実装よりもシンプルな手法を取れます。すでに Grunt を利用している場合、grunt-sri を利用するように設定します。grunt-sri は指定されたファイルを捜査して、SRI を実装するために必要なメタデータを含む JSON ファイルを生成します。このファイルはデータリソースとして、アプリケーションをビルドする際に簡単に利用できます。grunt-sri を利用する簡単な例は、次のようになります:

// Gruntfile.js
grunt.loadNpmTasks("grunt-sri");
grunt.initConfig({
    sri: {
        generate: {
            src: [ 'public/**/*.js', 'public/**/*.css' ],
            options: { algorithms: [ 'sha256' ] }
        }
    }
});

次に grunt sri:generate を実行します。./payload.json が出力され、アプリケーションや他の Grunt タスクから利用できるようになります。生成された SRI 用の SHA ハッシュ値は、次のように利用できます。これは grunt-sri のドキュメントにも記載されています。

// ES6 from https://github.com/neftaly/grunt-sri#javascript
var payload = require("./payload.json");
var sri = (id) => payload.payload[id];

var element = `<link
    href='https://2r4s9p1yi1fa2jd7j43zph8r-wpengine.netdna-ssl.com/style.css?cache=${ sri("@cssfile1").hashes.sha256 }'
    integrity='${ sri("@cssfile1").integrity }'
    rel='stylesheet'>`;

実装に関する詳細は https://github.com/neftaly/grunt-sri 、もしくは我々が code.jquery.com に対して送った pull request、特にこの部分をご覧ください。

JavaScript / Node.js 以外の例

昔ながらの Makefile を利用されている場合でも、問題なく組み込めます。UNIX ライクな環境をお使いと仮定すると、以下のように node モジュールを利用する必要なく実現できます:

# Makefile
generate:
    cat FILENAME.js | openssl dgst -sha256 -binary \
        | openssl enc -base64 -A

こちらの Gist に様々なプラットフォームで SRI 向けに SHA ハッシュ値を生成する例を公開しています。

結論

Subresource Interity を利用すると、自分でコントロールできないサーバから配信される静的アセットの安全性を、とてもシンプルに保てます。ビルドプロセスに組み込むためのツールもいくつか提供されています。モダンな Web サイト / アプリケーション開発者なら SRI を実装するだけでなく、その利益を説明し議論することも忘れてはならないでしょう。

最後に、この記事をレビューを助けてくれた Frederik BraunJonathan KingstonFrancois Marier そして Havi Hoffman に感謝します。

Justin Dorfman について

Justin は MaxCDN のディベロッパーリレーションズディレクターで、企業の持つ技術の普及とネットワークを利用する開発者の手助けの責任者。BootStrapCDN を 2012 に開始。Bootstrap、Font Awesome、Grunt、Ionic、jQuery Foundation、Twemoji、Nginx、Gnu Bash への貢献者で、 FOSS コミュニティにへ深く関わっている。

Justin Dorfman による他の記事はこちら

Joshua Mervine について

Heroku の SRE。開発とシステムエンジニアリングに 20 年以上の経験を持つ。2013 に BootstrapCDN の開発をリード。自分のプロジェクトと他のプロジェクトへの貢献を通じて、オープンソースとコミュニティに深く関わる。

Joshua Mervine による他の記事はこちら