精読「Web API: The Good Parts」(3)
Web API: The Good Parts
Web APIの設計と開発における「本当に大切な部分」を凝縮した一冊。シンプルで使いやすいAPIを実現するためのベストプラクティスと、その背景にある思想を解説します。RESTの基本から、セキュリティ、パフォーマンス、エラーハンドリング、バージョニングまで、実務に役立つ知識が詰まっています。
この書籍では、実例を通じて良いAPI設計の判断基準を学べるだけでなく、なぜそのアプローチが優れているのかを深く理解できます。さらに、「これだけは避けたい」という悪い例も取り上げられており、実践的なスキルが身につきます。
開発者、アーキテクト、プロダクトマネージャーにとって、Web API設計の基礎と応用を学ぶ最良のガイドとなる一冊です。
関連記事
設計変更をしやすいWeb APIを作る
Web APIは公開後も継続的に運用し、利用方法や機能要求の変化に対応する必要がある。また、場合によってはAPIの廃止も検討しなければならないことがある。この章では、APIの変更や廃止に関する課題について解説する。
設計変更のしやすさの重要性
Web APIは公開後、変更が必要になることが多く、その際にはさまざまな問題が発生する。特に、APIの利用者が外部のシステムやサービスである場合、その変更が予期せぬエラーを引き起こす可能性がある。例えば、公開されているAPIの仕様変更が突然行われると、利用者側で処理が停止したり、表示不具合が生じたりすることがある。
また、モバイルアプリ向けAPIでは、古いバージョンのクライアントを使用しているユーザーがいるため、APIを変更するとそのユーザーがエラーを経験する可能性がある。さらに、ウェブサービス上で利用しているAPIでは、キャッシュの問題が発生することがある。
つまり、公開後のWeb APIの変更はリスクを伴い、その影響を最小限に抑える工夫が求められる。
APIをバージョンで管理する
このテキストは、APIのバージョン管理に関する基本的な考え方を説明している。要点は次の通り。
APIの変更と後方互換性
既存のAPIを変更しないことが最良の方針。変更が必要な場合、新しいAPIを別のエンドポイントまたはパラメータで提供し、旧APIはそのまま維持する。これにより、古いAPIを使っているクライアントはそのまま使用し、新しいAPIに移行することが可能。
バージョン管理の方法
新しいバージョンを公開する方法として、URIにバージョン番号を埋め込むのが一般的。例えば、http://api.example.com/v2/users/123
のように、v2
という形でバージョンを指定する。この方法は視覚的にもわかりやすく、ユーザーがどのバージョンを使っているのか一目でわかる。
バージョン番号の付け方
セマンティックバージョニング(SemVer)が推奨されている。セマンティックバージョニングでは、バージョン番号は「メジャー.マイナー.パッチ」の形で記載され、変更がある場合に適切にバージョン番号が更新される。例えば、後方互換性のある変更ではマイナーバージョンを、後方互換性のない変更ではメジャーバージョンを上げる方法が推奨されている。
バージョン管理の実際の例
各種サービス(Tumblr、Twitter、Facebookなど)では、APIバージョン番号をURIに含める方法が採用されており、/v2/
や/v1/
といった形でバージョンを指定している。
この方法を採ることで、APIの利用者に対して、変更や新しい機能を導入する際に柔軟かつスムーズに移行を提供できる。
バージョンを変える際の指針
APIのバージョニングにおける方針として、非常に重要なポイントをいくつか挙げられる。
後方互換性の維持
バージョンを上げる際に最も重要なのは後方互換性を保つこと。できるだけマイナーバージョンアップで対応し、バージョンアップは後方互換性を保つことが難しい場合に限定するという考え方は理にかなっている。クライアント側やサーバ側でのメンテナンスコストを最小限に抑えるためにも、無駄にバージョンを増やすべきではないという姿勢が重要。
軽微な変更の取り扱い
データ形式や項目名の変更など軽微な変更では、バージョンを上げるのではなく、互換性を保ったまま新しいフィールドを追加するアプローチが推奨されている。この場合、古いAPIを使用しているクライアントには問題が起こらず、新しい開発者には新しい方法を促すことができる。また、ドキュメントで古い方式の廃止予定を明記することが重要。
セキュリティやルール変更の場合
セキュリティや認証方法の変更は後方互換性を保つのが難しいため、メジャーバージョンアップとともに行われることが一般的。TwitterやFacebook、GitHubのように、サービスの成長に伴ってAPIの仕様を大きく変更することはあるが、その際はバージョンアップと共に行うことで、ユーザーへの影響を最小限にできる。
エイリアスの使用
最新バージョンにアクセスするためのエイリアス機能にはリスクが伴う。エイリアスを使うと、将来的に挙動が変化する可能性があり、予期しないエラーを引き起こすことがあるため、避けるべきだという意見は非常に理にかなっている。バージョンを指定しない場合に最も古いサポートされているバージョンにアクセスするという設計のほうが、クライアントにとっては安定性があり、好ましいといえる。
このように、APIのバージョン管理は、クライアントの安定性とサーバー側のメンテナンスコストを考慮したうえで慎重に行う必要がある。
APIの提供を終了する
APIの提供を終了する際、特に既存のバージョンが広く使用されている場合、運用のコスト増や影響を最小化するために段階的に対応を進めることが重要。例えば、Twitterのケーススタディのように、古いバージョンの廃止には以下のポイントを考慮するべき。
事前アナウンス
APIのバージョン終了には、事前に十分な通知が必要。例えば、TwitterはAPIバージョン1.0の廃止を計画的にアナウンスし、複数回の通知を行い、ブラックアウトテストを実施した。このような告知を早期に行うことで、ユーザーに十分な準備期間を提供できる。
APIの終了仕様を盛り込む
APIが廃止された際に返されるレスポンスとして、HTTPステータスコード410(Gone)を返すことが考えられる。これにより、利用者に対して明確にAPIの廃止が伝わり、移行を促進できる。また、クライアント側でも古いバージョンを使用している場合には、強制アップデートを仕込んでおくと、ユーザー体験を損なうことなくスムーズに移行を進めることができる。
サポート期限を利用規約に明記
APIの廃止ポリシーを事前に規約に記載しておくことで、ユーザーはいつまで古いバージョンを使用できるか、また廃止までの移行期間がどれくらいあるのかを把握できる。DoubleClick社のように、一定期間(例えば12ヶ月)内に移行しないと古いバージョンのサポートが終了することを規定するのも有効。
バージョンアップ後の互換性
APIの新しいバージョンが公開される際、後方互換性を保ちながらアップデートを進めることが理想。Facebookのようにマイナーバージョンアップの場合、古いバージョンのAPIを後方互換性を保ちながら扱うことも選択肢となるが、この場合、エラーを引き起こす可能性があるため注意が必要。
新しいバージョンへの移行支援
APIの新しいバージョンへの移行は、利用者にとって負担をかける可能性があるため、適切なドキュメントやサポート体制を整備することが重要。移行を円滑に進めるためには、提供終了の日時を明確にし、利用者が新しいバージョンへ移行できるようにサポートすることが求められる。
これらを踏まえた上で、APIのバージョン管理を計画的に行い、利用者が予期しない障害に見舞われないようにすることが、API提供者にとって重要な役割となる。
オーケストレーション層
このセクションでは、API設計のアプローチに関する理論と、Netflixがどのようにそれを実現したかについて述べられています。以下に重要なポイントをまとめます。
LSUDsとSSKDsの違い
-
LSUDs(Large Scale Unrestricted Developers)向けAPI: 多くの人々や不特定多数の利用者を対象にした汎用的なAPI設計が求められる。しかし、このような設計では、すべてのニーズを満たすのが難しく、利用者が求める特定の機能を実現するためには、使いづらさや複雑さが生じることがある。例えば、1つのアクションを複数のAPIで実行しなければならないことや、不要なデータも含まれてしまうことがある。これを「OSFA(One-Size-Fits-All)アプローチ」と呼ぶ。
-
SSKDs(Small Scale Specific Known Developers)向けAPI: 限られた利用者に特化したAPI設計ができ、各利用者のニーズに合わせてより使いやすいAPIが提供できる。しかし、利用者ごとに異なるユースケースがあると、それぞれに対応したAPIを作成・維持するのは手間がかかり、難易度が高くなる。
Netflixのオーケストレーション層
Netflixは、特に多様なデバイスに対応するためにAPI設計を柔軟に対応させる必要があった。そのため、汎用的なAPIとクライアントの間に「オーケストレーション層」を導入している。このオーケストレーション層では、クライアント側のエンジニアが自分たちのデバイスの機能やリリースサイクルに合わせて、エンドポイントを修正できる仕組み。具体的には、以下のようなことが可能になる:
- 複数のAPIを1つにまとめる
- 返すデータ量を調整する
これにより、クライアントごとに最適化されたユーザー体験を提供できる。
オーケストレーション層の利点
柔軟性: クライアント側の開発者がAPIを自分たちのデバイスの特性に合わせて調整できるため、より効率的な開発と運用が可能になる。
維持管理の容易さ: 汎用的なサーバー側APIを基盤として、オーケストレーション層でカスタマイズを行うため、APIの修正や管理が容易になる。
Netflixでは、専用のエンドポイント管理ツールを提供しており、これにより大規模な開発チームが効率的に多様なデバイスに対応することが可能となっています。この手法は、リソース指向のAPIを利用し、オーケストレーション層を設けることで、複数の環境をサポートしやすくするという点で、小規模なサービスでも参考になるアプローチです。
結論
Netflixが採用しているオーケストレーション層のアプローチは、複数のデバイスやユースケースに対応する際に、APIの柔軟性と管理のしやすさを高めるための有効な方法であると言える。
まとめ
- [Good] APIのバージョンの更新は最低限にとどめ、後方互換性にも注意する
- [Good] APIのバージョンはメジャーバージョンをURIに含める
- [Good] APIの提供終了時はすぐに終了するのではなく最低6ヶ月公開を続ける
堅牢なWeb APIを作る
「安全性」と「安定性」の2つの面から、堅牢なWeb APIを作るための方法を考えていく。
Web APIを安全にする
Web APIのセキュリティ
Web APIは個人情報や機密情報を扱うため、強力なセキュリティ対策が必要。APIは機械的アクセスを前提としており、通常のウェブサイトとは異なるリスクが伴う。悪意のある攻撃や不正操作を防ぐため、堅牢な認証とアクセス制御が求められる。
特に、モバイルアプリとの連携が増えており、APIのセキュリティ対策が疎かになることがある。情報漏洩や不正取引のリスクを避けるため、常に最新の対策を講じることが重要。
セキュリティの問題
主なセキュリティ問題には、以下のようなものがある
- サーバとクライアント間の情報不正入手
- サーバの脆弱性による情報漏洩や改ざん
- ブラウザを前提としたAPIの問題
これらに対する対策を講じることが必要。
サーバとクライアントの間での情報の不正入手
サーバとクライアント間の情報不正入手のリスクに関して、HTTPSの導入が有効な対策。HTTP通信は暗号化されていないため、公共のWi-Fiなどでは通信内容を盗み見ることができる。これを防ぐために、HTTPS(HTTP Secure)を使うことで、通信内容を暗号化し、セッションハイジャックやパケットスニッフィングなどのリスクを低減できる。
HTTPSはTLS(Transport Layer Security)で通信を暗号化し、通信経路で情報が盗み見されることを防ぐ。これにより、APIのリクエストやレスポンス、セッション情報などが保護され、公共のWi-Fiでも安全に通信できる。しかし、HTTPSを使っても、サーバやライブラリのバグ、クライアント側で証明書検証を行わない場合などには依然としてリスクが残る。
HTTPS化は基本的なセキュリティ対策として有効だが、サーバとクライアント両方で適切な検証や対策を行うことが重要。
ブラウザでアクセスするAPIにおける問題
ウェブAPIにおけるセキュリティ問題として、XSS(クロスサイトスクリプティング)とXSRF(クロスサイトリクエストフォージェリ)が挙げられる。これらは、ブラウザを介した攻撃において特に注意が必要。
XSS (Cross-Site Scripting)
XSSは、ユーザーから送られた入力がそのままHTMLページに埋め込まれ、悪意のあるJavaScriptコードが実行される脆弱性。この場合、ユーザーのクッキー情報などが盗まれたり、ページの改ざんが行われたりする危険がある。
対策:
- ユーザー入力は必ずチェックし、不正なデータを除去すること。
- JSONレスポンスを返す際、
Content-Type
ヘッダーをapplication/json
に設定し、ブラウザがJSONとして処理するように指定します。 - さらに、
X-Content-Type-Options: nosniff
ヘッダーを利用して、ブラウザがContent-Type
を無視してデータを推測することを防ぎます。 - JavaScriptコードをエスケープすることで、悪意のあるコードが実行されないようにする。
XSRF (Cross-Site Request Forgery)
XSRFは、悪意のあるウェブサイトがユーザーの認証情報を使って、別のウェブサイトに不正なリクエストを送る攻撃。たとえば、銀行のサイトにおいて、ユーザーが悪意のあるリンクをクリックすることで、不正にお金を送金される可能性がある。
対策:
- CSRFトークンを使用し、リクエストが正当なものであることを確認する。
- サーバー側で
Referer
やOrigin
ヘッダーをチェックし、リクエスト元が信頼できるものであるか確認する。 - セッション管理において、クッキーには
SameSite
属性を設定して、外部サイトからのリクエストがセッションを使って送信されないようにする。
実際の実装
-
XSS対策として、入力されたデータのエスケープや、
X-Content-Type-Options: nosniff
の設定を行うことで、HTMLとして解釈されてしまう問題を防ぐ。 -
XSRF対策では、特にPOSTリクエストでのCSRFトークンの使用や、
Origin
ヘッダーを使用したサーバーサイドチェックを行うことで、悪意のあるサイトからの不正リクエストを防ぐ。
これらの対策を講じることで、ブラウザを経由した攻撃からAPIを守ることができる。
悪意あるアクセスへの対策を考える
このセクションでは、ユーザー自身が不正アクセスや攻撃を試みるケースに対する対策が取り上げられている。主にパラメータの改ざんやリクエストの再送信、そして支払いの偽装といった攻撃手法について考察している。
パラメータの改ざんに対する対策
- アクセス制限の強化: ユーザーIDやリソースIDを簡単に推測できないようにし、アクセスするデータの所有権をきちんとチェックする。たとえば、クエリパラメータの変更により、他のユーザーの情報にアクセスできないように、サーバ側で検証を行う。
- 範囲外のパラメータのチェック: たとえば、アイテムの消費数に対するバリデーションを行い、ユーザーが持っていないアイテムを消費することができないようにする。
リクエストの再送信に対する対策
- 状態管理の強化: 同じリクエストが複数回送信された場合、それが正当なリクエストかどうかを判断するために、サーバ側で状態を管理する。例えば、ゲームにおける戦闘の進行状況を記録し、勝利報酬を繰り返し受け取ることができないようにする。
- 重複リクエストの検出: 短期間内に同じリクエストが何度も送信されないようにし、例えば、ポイントの付与リクエストが繰り返し送られた場合には、それを無効化する。
支払いの偽装に対する対策
- トランザクションの一意性の確認: 支払いが行われたことを確実にサーバ側で記録し、同じ支払いが再度行われることを防ぐために、一度送信されたトランザクションが再度処理されないようにする。これには、トランザクションIDや一意の注文IDを生成して管理する方法が有効。
- リプレイ攻撃防止: サーバとクライアント間の通信がキャプチャされてもリプレイされないよう、例えばトークンを使った認証やタイムスタンプの検証を行い、再送信されたリクエストが無効であることを保証する。
その他の重要な対策
- HTTPSによる暗号化: 通信内容が途中で漏洩しないようにHTTPSを使用して通信を暗号化する。
- セッション管理の強化: セッションIDや認証トークンを適切に管理し、セッションハイジャックや不正ログインを防ぐ。ログイン後、一定時間が経過したら自動的にセッションを終了させるなどの対策も有効。
- APIへのアクセス制限(レートリミット): サーバへのアクセス頻度を制限することにより、大量のリクエストやボットによる不正行為を防ぐ。
ユーザーが不正を試みるリスクに対応するためには、単に認証やアクセス制限を行うだけでなく、通信の内容やリクエストの振る舞いも精密に監視し、サーバ側での整合性チェックや重複リクエストの防止、そして支払いの検証などを強化することが必要。
セキュリティ関係のHTTPヘッダ
このセクションでは、HTTPヘッダを使用してセキュリティを強化するための様々な方法が説明されている。具体的なヘッダとその目的は以下の通り。
X-Content-Type-Options
- 目的: ブラウザがレスポンスのContent-Typeを無視してファイルの内容に基づいて推測しないようにする。
-
設定:
X-Content-Type-Options: nosniff
- 効果: 例えば、JSONファイルがHTMLとして誤って解釈されるのを防ぐ
X-XSS-Protection
- 目的: XSS(クロスサイトスクリプティング)攻撃を防ぐため、ブラウザのセキュリティ機能を有効にする。
-
設定:
X-XSS-Protection: 1; mode=block
- 効果: XSS攻撃が検出された場合、ブラウザが自動的にリクエストをブロックする。
X-Frame-Options
- 目的: ページが他のサイトでフレーム内に表示されないように制御。
-
設定:
X-Frame-Options: deny
- 効果: クリックジャッキング攻撃の防止に役立つ
Content-Security-Policy (CSP)
- 目的: 指定したドメインからのみリソース(画像やスクリプトなど)を読み込むように制限。
-
設定例:
Content-Security-Policy: default-src 'none'
- 効果: 外部からのリソース読み込みを防止し、XSS攻撃を抑制する。
Strict-Transport-Security (HSTS)
- 目的: サイトがHTTPSでのみアクセスされることを保証。
-
設定:
Strict-Transport-Security: max-age=15768000
- 効果: 一度HTTPSにアクセスされた後、ブラウザがHTTPではなくHTTPSで接続するように記録する。
Public-Key-Pins
- 目的: SSL/TLS証明書の偽造を防止するため、公開鍵ピンニングを利用。
-
設定例:
Public-Key-Pins: max-age=2592000; pin-sha256="E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g="
- 効果: サイトが特定の証明書を使用していることをブラウザに記録させ、偽造された証明書をブロックする。
Set-Cookie ヘッダとセキュリティ
- 目的: クッキーをセキュアに管理。
-
設定例:
-
Secure
: HTTPS接続時のみクッキーを送信 -
HttpOnly
: JavaScriptからクッキーにアクセスできないようにする
-
- 効果: XSS攻撃を防ぎ、セッション情報を守る
実際のAPIのセキュリティ対応例
実際に公開されているAPIのレスポンスヘッダを見てみると、例えばFoursquareのAPIやGitHubのAPIで、Strict-Transport-Security
ヘッダが設定されており、HTTPSを強制していることが確認できる。これにより、中間者攻撃や通信の傍受を防ぐことができる。
これらのヘッダを適切に設定することで、ウェブアプリケーションのセキュリティが大幅に向上する。
大量アクセスへの対策
ネットワーク上に公開されたサービスは、外部からの大量アクセスによってリソースが逼迫し、最終的にサーバがダウンしてしまうリスクがある。このリスクに対処するため、Web APIにおいてはアクセス制限を設けることが重要。特に「DoS攻撃」や過剰なアクセスによりサービスが利用できなくなる事態を防ぐために、レートリミット(一定時間内に許可される最大アクセス回数)を設定することが一般的。
レートリミットの設定
レートリミットは、通常、以下の要素を考慮して設定される:
- ユーザー識別方法:ユーザーをどのように識別するか(IPアドレス、APIキーなど)。
- 制限値:どれだけのアクセスが許されるか(例:1分あたり60回)。
- 時間単位:レートリミットをどの単位で適用するか(例:1分、1時間、1日など)。
- リセットタイミング:リミットがリセットされるタイミング(例えば、時間単位ごとにリセット)。
レートリミットのユースケース
多くの公開APIでは、ユーザー単位やIP単位でアクセス回数に上限を設けている。例えば、以下のような制限を設けているサービスがある:
- Twitter:ユーザーやアプリケーションに対して、15分に最大180回のアクセス。
- GitHub:ユーザー/IPに対して、1時間に最大5000回のアクセス。
- Instagram:ユーザーやアプリケーションに対して、1時間に最大5000回のアクセス。
これらの制限は、過剰なアクセスからサービスを守りつつ、適切な使用を促すために設定されている。
レートリミットの緩和
ただし、特定の大規模なアプリケーションや開発者に対しては、アクセス制限を緩和することもある。特に、そのアプリケーションがサービスに大きな収益をもたらす場合や、ビジネス的に重要な場合には、特別な許可を与えることがある。また、APIを使用する顧客が制限に引っかかりやすい場合には、料金を支払うことでアクセス枠を増加させる仕組みもよく採用されている。
結論
サービスが高負荷にさらされないようにするためには、APIのレートリミットを適切に設定し、ユーザーの利用状況に応じて柔軟に調整することが重要。APIを公開する際には、アクセス頻度に関するニーズを考慮し、過剰な負荷がかからないように設計することがサービスの安定性を確保するための鍵となる。
まとめ
- [Good] 個人情報など特定のユーザー以外に漏洩したくない情報がある場合はHTTPSを使う
- [Good] XSS、XSRFなど通常のウェブと同様のセキュリティだけでなくJSONハイジャックなどAPI特有の脆弱性に気を配る
- [Good] セキュリティ強化につながるHTTPヘッダをきちんと付ける
- [Good] レートリミットを設けることで一部のユーザーによる過度のアクセスによる負荷を防ぐ
Discussion