💡 Key Takeaways
- Authentication and Authorization: The Foundation That Everyone Rushes Past
- Request Validation: Where Most Bugs Actually Live
- Response Validation: Trust, But Verify
- State Management and Idempotency: The Subtle Art of Consistency
3年前、午前2時に「32/13/2021」という形式の日時フィールドを送信したときに何が起こるかを誰もテストしなかったため、あるプロダクションAPIが壮絶に失敗するのを見ました。そのカスケードは最悪の形で美しかった:47,000件の失敗したトランザクション、サポートチャンネルに押し寄せる怒った顧客、そして私には答えがなかったCEO。その夜は、私のAPIテストのアプローチを永遠に変えました。
💡 主なポイント
- 認証と認可:誰もが急いで通り過ぎる基盤
- リクエストバリデーション:ほとんどのバグが実際に存在する場所
- レスポンスバリデーション:信頼するが、確認せよ
- 状態管理と冪等性:一貫性の微妙な技術
私はサラ・チェンで、過去8年間QAオートメーションエンジニアをしており、そのうちの5年間はフィンテックとヘルスケアプラットフォームのAPIテストに専念してきました。私は単純なCRUDエンドポイントから、毎日何百万ドルも処理する複雑な決済処理APIまで、あらゆるものをテストしてきました。私が学んだのはこれです:ほとんどのAPIの失敗は、独特なエッジケースではなく、体系的なチェックリストで捕まえられる予測可能な問題です。
今日共有するチェックリストは、私がテストするすべてのエンドポイントに対して使用する正確なものです。これは、過去1年間だけで、少なくとも12件のプロダクションインシデントから私のチームを救いました、そして230以上のAPIエンドポイントで99.97%の稼働時間を維持するのに役立ちました。これは理論ではありません—私が午前3時に何度も呼ばれた経験から得た実践的な現実です。
認証と認可:誰もが急いで通り過ぎる基盤
あなたを恐れさせるべき統計があります:私の経験上、7社のAPIを監査したところ、約60%には壊れた認可ロジックを持つエンドポイントが少なくとも1つありました。認証ではなく、認可です。そのエンドポイントは、あなたがログインしていることを確認しましたが、特定のリソースにアクセスすべきかを正しく確認しませんでした。
私の認証と認可のチェックリストは、明白ですがしばしばスキップされる基本から始まります:
- 全く認証トークンなしでテストする—401を返すべき
- 期限切れのトークンでテストする—401を返すべきで、500ではない
- 形式が不正なトークンでテストする—401を返すべきで、クラッシュしない
- 有効なトークンだが権限が異なるものでテストする—403を返すべき
- 異なるユーザーのトークンで他のユーザーのリソースにアクセスを試みる
最後のものが面白くなります。私はかつて、URL内のユーザーIDを変えるだけで、任意のユーザーの支払い履歴を取得できるエンドポイントを見つけました。他のユーザーとして認証されているにもかかわらずです。そのエンドポイントは、ログインしていることを確認しましたが、要求されたユーザーIDが認証されたユーザーIDと一致するかを確認しませんでした。これは「不適切な直接オブジェクト参照(IDOR)」と呼ばれ、驚くほど一般的です。
私はまた、トークンのリフレッシュフローを明示的にテストします。トークンがリクエスト中に切れた場合、APIはそれを適切に処理しますか、それともクライアントを妙な状態に放置しますか?POSTリクエスト中に期限切れのトークンが401を返すが、データがまだ部分的にデータベースに書き込まれていたシステムを見たことがあります。これはデータの一貫性にとって悪夢です。
トークンの代わりにAPIキーを使用しているAPIでは、キーのローテーションが正しく機能することを確認します。新しいキーを生成できますか?古いキーは直ちに無効になりますか、それとも猶予期間がありますか?その猶予期間は文書化されていますか?かつて、ローテーションするキーには24時間の重複期間があり、誰もそれを知っていなかったAPIで作業したことがあります。これがセキュリティ監査の失敗に繋がりました。
その認可マトリックスが私の秘密兵器です。私は、1つの軸にすべてのエンドポイント、もう1つの軸にすべてのユーザーロールを記載したスプレッドシートを作成します。それから、体系的に各組み合わせをテストします。これは面倒ですが、私がこれを適用したプロジェクトの100%で認可のバグを捉えました。はい、100%。これは誇張ではありません—すべてのプロジェクトで、少なくとも1つのエンドポイントにおいて、少なくとも1つの役割に対して認可ロジックが間違っていました。
リクエストバリデーション:ほとんどのバグが実際に存在する場所
もし70%のAPIバグがどこから来ているかを推測する必要があるなら、それはリクエストバリデーションにあるでしょう。開発者は楽観的な生き物です—彼らは入力が合理的であると仮定してコードを書きます。でも、インターネットは合理的ではなく、あなたのAPIを呼び出すシステムも合理的ではありません。
私のリクエストバリデーションのチェックリストは網羅的でなければなりません:
- 完全に空のリクエストボディを送信する—どうなるか?
- 必要なフィールドごとにnullを送信する
- 文字列フィールドには空の文字列を送信する
- ホワイトスペースのみの文字列を送信する
- 長さ制限を超える1文字長の文字列を送信する
- 1000倍長すぎる文字列を送信する
- 正の整数を期待するフィールドには負の数を送信する
- ゼロが無効な場合にはゼロを送信する
- 整数フィールドに小数を送信する
- 非常に大きな数を送信する(整数オーバーフローのテスト)
- 非常に小さな数を送信する(アンダーフローのテスト)
- 文字列フィールドで特殊文字を送信する:引用符、バックスラッシュ、ヌルバイト、Unicode
- SQLインジェクションの試みを送信する(ORMを使用していても)
- 文字列フィールドにXSSペイロードを送信する
- データ型の不一致を送信する(数値が期待される場所に文字列など)
あなたが思っていることは分かります:“サラ、それは狂っている。誰もそんなことに時間をかける余裕なんてない。”しかし—私はこの全体のチェックリストを自動化しました。すべてのこれらのバリエーションを自動的に生成するテストデータ生成器があります。最初のセットアップには約2週間かかりましたが、今では新しいエンドポイントに対してこの全体のスイートを約15分で実行できます。
その効果は実際にあります。先月、このチェックリストが、65,535文字を超える文字列を送信したときに、APIサーバー全体をクラッシュさせるエンドポイントを捕まえました。開発者はデータベースが長さの検証を行うと仮定していましたが、データベースは静かに切り捨てるように設定されており、アプリケーションコードは固定サイズのバッファに完全な文字列を記録しようとしていました。バン—セグメンテーションフォルト、サーバーダウン。
日時フィールドについては、これらが特にひどいため、特別なサブチェックリストがあります:
- 異なる形式で日付を送信する(ISO 8601、MM/DD/YYYY、DD/MM/YYYYなど)
- 無効な日付を送信する(2月30日、月13、日32)
- 過去に遡る日付を送信する(1900年、1年)
- 遠い未来の日付を送信する(2100年、9999年)
- 異なるタイムゾーンオフセットを持つ日付を送信する
- 夏時間の変更時期に日付を送信する
- ミリ秒、マイクロ秒、ナノ秒を含むタイムスタンプを送信する
その夏時間の件は、私を2回襲いました。2回です!私が学んでもおかしくはないのですが、とても珍しいエッジケースなので忘れやすいです。最近では、時計が変更される日曜日の午前2時にトランザクションを実行する特定のテストを持っているので、それが不具合が発生するタイミングです。
レスポンスバリデーション:信頼するが、確認せよ
ほとんどのテスターはリクエストに集中し、レスポンスにはほとんど目を向けません。これは逆です。あなたのAPIのレスポンスは、世界との契約です。それらが一貫性がなく、不完全で、間違っている場合、その契約を破ったことになります。
| テストカテゴリー | 一般的な失敗点 | 期待されるレスポンス | 実際に起こったこと |
|---|---|---|---|
| 認証トークンなし | エラーハンドリングが欠けている | 401 Unauthorized | 500 Internal Server Errorまたは露出データ |
| 期限切れのトークン | トークン検証ロジック | 401 Unauthorized | 500エラーまたは沈黙の失敗 |
| 形式が不正なトークン | 入力検証 | 401 Unauthorized | アプリケーションクラッシュまたはスタックトレースの露出 |
| 有効なトークン、権限が異なる | 認可チェック | 403 Forbidden | 200 OK、無効なデータへのアクセス |
| 無効な日付形式 | 入力サニタイズ | 400 Bad Request | トランザクションのカスケード失敗 |
私のレスポンスバリデーションチェックリストには以下が含まれます:
- レスポンスのステータスコードが文書化された動作と一致することを確認する
- レスポンスコンテンツタイプヘッダーが正しいことを確認する
- レスポンスボディの構造がスキーマと一致することを確認する
- すべての文書化されたフィールドが存在することを確認する
- 文書化されていないフィールドが存在しないことを確認する(これはAPIのバージョン管理に重要です)
- フィールドのデータ型が文書と一致することを確認する
- フィールドの値が文書化された範囲内であることを確認する
- タイムスタンプが正しいタイムゾーンであることを確認する
- ページネーションメタデータが正しく一貫していることを確認する
- エラーレスポンスに有用なエラーメッセージが含まれていることを確認する
- エラーレスポンスにプログラム的に処理するためのエラーコードが含まれていることを確認する
エラーメッセージに関するその前のポイントは重要です。私は「エラー」だけが全エラーメッセージとして返されるAPIを見たことがあります。これは無意味です。良いエラーメッセージは、何が間違っていたか、なぜそれが間違っていたか、そして理想的にはそれを修正するためにできることを伝えます。これらの2つのエラーレスポンスを比較してください:
悪い: {"error": "無効なリクエスト"}
良い: {"error": "無効なリクエスト", "message": "フィールド 'email' が必須ですが提供されていません", "code": "MISSING_REQUIRED_FIELD", "field": "email"}
その次の