『Web API: The Good Parts』 を読んだ
- 作者: 水野貴明
- 出版社/メーカー: オライリージャパン
- 発売日: 2014/11/21
- メディア: 大型本
- この商品を含むブログ (6件) を見る
同僚から借りて読みました。 全体としては Web API の設計に少しでも携わる人間ならとりあえず読んでおいたらいいんじゃないかなーという感じです。 薄いし。
本書を読んだからと言って最高の Web API の設計ができるようになるとは思わないですが、Web API の設計をする際に知っておくべきことが一通りまとまっていて良い感じだと思いました。
学びメモ
知らなかったことや、なんとなく知ってたけど改めて調べたことなどまとめておきます。
RFC 5861 での Cache-Control
ヘッダの拡張
RFC 5861 にて、Cache-Control
ヘッダの 2 つの拡張が定義されています。
stale-while-revalidate
ディレクティブ : プロキシサーバー上でmax-age
を超えてキャッシュが切れた後も、(裏側で非同期にキャッシュの検証を行いつつ) キャッシュ済みの古いレスポンスデータを返しても良い時間を指定できる。stale-if-error
ディレクティブ : オリジンサーバーへのリクエストに失敗する場合に、キャッシュ済みの古いレスポンスデータを返してもよい時間を指定できる。
RFC 5861 の現在の state は “Informational” らしいのでどれぐらいサポートされてるのかわからないですが、「プロキシサーバー上でキャッシュが無効になったあとも、裏でキャッシュ更新しつつユーザーからのリクエストに対してはレスポンスを返してほしい」 って状況はあるので、こういうのが既存のプロキシサーバーなどでサポートされてるとアプリケーション開発時に便利に使えそうです。
本書の 4.3.6 節 「Cache-Control ヘッダ」 に記述されています。
独自のメディアタイプを作る
メディアタイプの登録プロセスの効率と柔軟性を増やすために、サブタイプのいくつかの登録 『木』 (registration “trees”) が RFC 6838 にて定義されています。 それぞれの木には facet *1 が定義されているので、新たに作る独自のメディアタイプの用途に応じて登録木を選択し、それに応じた facet を付けることになります。 (登録木によっては登録作業なども必要。)
- Standards Tree : IETF 標準に関連付けられるなどした、標準のメディアタイプのための木。 Facet はない。
- Vendor Tree : 公に使用可能な製品に結びつけられたメディアタイプのための木。 Facet は “vnd.”。 (例 : “application/vnd.ms-excel”)
- Personal or Vanity Tree : 実験的に作られたり、商業的に流通しない製品の一部だったりするメディアタイプのための木。 Facet は “prs.”。
- Unregistered x. Tree : 私的に、ローカルな環境でのみ使われるメディアタイプのための木。 Facet は “x.”。
Web API で使用されるメディアタイプを新たに独自に定義する場合は、vendor tree か personal or vanity tree のどちらかになるでしょう。
本書の 4.4.3 節 「自分でメディアタイプを定義する場合」 に書かれています。
CORS 周り
XMLHttpRequest
*2 には同一生成元ポリシー (Same Origin Policy) が適用されるため、通常は生成元の異なる URL に XMLHttpRequest
でリクエストを投げることができません。 異なる生成元へのリクエストが可能になるように考えられた仕様として CORS (Cross-Origin Resource Sharing) があります。
それ自体は知ってたのですが、
- CORS でプリフライトリクエストが投げられる条件 (
POST
メソッドでもプリフライトリクエストするとは限らない、みたいな話を聞いたことはあったけど、詳しくは知らなかった)、とか Cookie
やAuthentication
などのヘッダで認証情報をやり取りする場合は、サーバー側はAccess-Control-Allow-Credentials
ヘッダを返す必要があり、クライアント側はXMLHttpRequest#withCredentials
というプロパティをtrue
にしないといけない
など知らなかったので、いろいろあるなーと思いました。
ちなみに最近は CORS (や他の仕様) を置き換えるものとして Fetch Standard が作られてるみたいですね。
本書の 4.5 節 「同一生成元ポリシーとクロスオリジンリソース共有」 に書かれています。
セキュリティ周りの話
X-Content-Type-Options
ヘッダ
IE (どのバージョンまで? 11 も?) には Content-Type
ヘッダの値を無視してレスポンス内容のデータ形式を推定する Content Sniffing という機能があり、JSON として解釈されるべきコンテンツが HTML として解釈されて XSS 脆弱性になる、という問題があります。
IE 8 以降の場合は、サーバーがレスポンスとして次のレスポンスヘッダを返すことで Content Sniffing を無効にできます。 さらに、Firefox や Chrome、IE 9 以降では、このヘッダを付けることで JSON ハイジャック *3 (後述) の危険性を減らすことができるそうです。 (JSON を返すときは常に付けましょう。)
X-Content-Type-Options: nosniff
詳細は次のページに書かれています。
本書では、6.3.1 節 「XSS」 に書かれています。
JSON ハイジャック
JSON 形式のレスポンスを JavaScript としてブラウザに解釈させることで、別オリジンのサーバーから配信される JSON の内容を悪意ある第三者の web ページが読みだすことができてしまう、という脆弱性。 例えば、悪意ある第三者の web ページが Array
オブジェクトのコンストラクタを書き換えたうえで、配列を返す JSON ファイルを script
要素で読み込むと、JSON の中身が書き換えられた Array
オブジェクトのコンストラクタに渡されてデータが読まれる、という感じです。 (これは Firefox 2 系で発生していた脆弱性で、最近のブラウザは対策済みらしいです。)
サーバー側の対策として、以下のものがあります。
script
要素では読み込めないようにする :script
要素で読み込まれた場合には送られないヘッダフィールド (X-Requested-With
ヘッダなど) を必須にする。- JSON のレスポンスをブラウザが JS として認識しないようにする :
Content-Type: application/json
にすることはもちろん、X-Content-Type-Options: nosniff
を付ける。 - JSON のレスポンスを JS として解釈不可能にする (あるいは JS として実行されてもデータが読まれないようにする) : レスポンスの先頭に
while (true);
を付けるとか。
本書では、6.3;3 節 「JSON ハイジャック」 に書かれています。
セキュリティ周りの各種ヘッダ
X-XSS-Protection
レスポンスヘッダ : XSS を発生させそうなパターンがレスポンスに含まれている場合に、ブラウザがレスポンスをブロックする機能を有効にしたり無効にしたりできるヘッダフィールドぽい。 この機能は Chrome と Safari、IE (8 以降) がサポートしてるぽい?- ちゃんとしたドキュメントはあんまり見当たらない。
- XSS ブロックの機能はデフォルトで有効っぽい *4 から、サーバーサイドでは基本的には付けなくてもよさそう?
- 参考 : IE8 Security Part IV: The XSS Filter | IEBlog
Strict-Transport-Security
レスポンスヘッダ : ブラウザからのアクセスを HTTPS に限定させる。- http でリクエストされたときに https にリダイレクトする、という挙動だと、最初の http でのアクセスが中間者攻撃で書き換えられる恐れがある → 以前にこのヘッダが送られてきていたら、ブラウザは最初から https でアクセスする。
- RFC : RFC 6797 - HTTP Strict Transport Security (HSTS)
- 参考 : HTTP Strict Transport Security - Security | MDN
Public-Key-Pins
レスポンスヘッダ : ブラウザに暗号公開鍵とドメイン (?) を結び付けさせる → ブラウザは将来に証明書が偽造された場合に検知できるようになる。- 攻撃者により認証局が危殆化した場合に中間者攻撃されるのを防げる。
- RFC : RFC 7469 - Public Key Pinning Extension for HTTP
- 参考 : Public Key Pinning - Web security | MDN
本書の 6.5 節 「セキュリティ関係の HTTP ヘッダ」 に書かれています。
ブラウザがレスポンス内容を UTF-7 と誤認することによる XSS の成立
XSS 対策として 「<」 という文字列をエスケープするなどの方法がありますが、ブラウザにレスポンス内容の文字エンコーディングを UTF-7 だと誤認させることで、「<」 のエスケープをしていても 「<script>」 という文字列をそのままブラウザに食わせることができる、という攻撃があるとのことです。
UTF-7 では、「<」 という文字は ASCII の 「+ADw-」 として表現されるためです。 「+Adw-script+AD4-」 という文字列を UTF-7 としてブラウザに解釈させれば 「<script>」 とみなされるので、「<」 という文字のエスケープをしていてもそれをすり抜けて XSS ができることがあるようです。 (古いブラウザの脆弱性。)
最近のブラウザだと問題ないようですが、念のため対策するのであれば 「+」 をエスケープすると良いようです。
- 参考 : 第1回 UTF-7によるクロスサイトスクリプティング攻撃[前編]:本当は怖い文字コードの話|gihyo.jp … 技術評論社
- 参考 : UTF-7でXSSを発生させる10の方法 - 葉っぱ日記
本書の 6.3.1 節 「XSS」 に書かれています。
ステータスコード 429 Too Many Request
レートリミットに達した場合に返すためのステータスコードとして 429 Too Many Request が定義されているとのこと。 初めて知りました。
ヘッダフィールドの値として使用できる時刻の形式は決められてる?
本書には以下のように書かれていて、X-Rate-Limit-Reset
のようなヘッダフィールドの値として Unix タイムスタンプを使うことは問題がある、といってるんですけど実際どうなんでしょうね。 (本書 195 ページ。)
HTTP ヘッダに Unix タイムスタンプを入れるのは、実は問題があるのです。 というのは、RFC 7231 の HTTP 1.1 仕様によればヘッダに入れてよい時間の形式は以下の 3 種類に限定されているからです
多分根拠は RFC 7231 の以下の箇所だと思うんですけど、これって別にあらゆるヘッダフィールドの値として使われる時刻の形式について言っているのではなくて、HTTP-date
について言ってるだけな気もします。 わかんないですけど。
A recipient that parses a timestamp value in an HTTP header field MUST accept all three HTTP-date formats. When a sender generates a header field that contains one or more timestamps defined as HTTP-date, the sender MUST generate those timestamps in the IMF-fixdate format.
RFC 7231 (section 7.1.1.1 Date/Time Formats)