API認証
APIキー
API キーは、ランダムに生成されたアルファベットと数字の文字等で構成されています。本来APIキーは認証目的ではなく、それをリクエストヘッダーに付与してAPI間で通信を行うことでAPIの使用状況をモニタリングしたり、APIの呼び出しを制限したり、様々なAPIの制御に使うことができます。認証用途として使う場合もありますが、あくまでも簡易な方法であることを理解して使用しましょう。
クライアント認証
Amazon API GatewayでREST APIを作成する場合にはクライアント証明書に対応したクライアント認証が可能です。クライアント認証を使用することでパスワードレスでの認証が可能になります。また、クライアントとAPI間の通信を暗号化し、なりすましや中間者攻撃を防ぐことも可能になります。スマートホームデバイス、工業用IoTデバイス、またはBtoB向けモバイルアプリがAPI Gatewayにアクセスする際にはクライアント認証が用いられることが多いのではないでしょうか。そうすることによって正当なデバイスからの通信であることを証明することができます。
高いレベルでのセキュリティを担保できる代わりに証明書管理の運用負担や更新管理、インストールの複雑さなど導入管理コストはどうしても増えてきます。また、古いブラウザや一部のIoTデバイスではそもそも証明書のインストールや管理が困難な場合もあります。なのでコンシューマー向けのAPIにはOAuth2を利用したり、最初の小規模なPoCの段階などではAPIキーを利用するのも選択肢としては良いでしょう。
JWT認証
コンシューマー向けのWebアプリやモバイルアプリなどユーザごとの認証が必要な場合にはJWT認証が採用される場合が多いでしょう。JWT認証を使うにはユーザはログインIDとパスワードをアプリケーションに入力します。するとサーバー側(OIDC準拠IDPサーバー)がIDトークンとアクセストークンをクライアントに払い出します。このアクセストークンをクライアントではCookieなどに保存、APIリクエストを送るたびにAuthorization: Bearer <JWT>
ヘッダーにセットします。API(Amazon API Gateway)側では送られてきたトークンに対して、証明書をチェックすることで改ざんが行われていないか、または有効期限が切れていないかをチェックします。JWTは一定以上のセキュリティレベルを担保する仕様として安定しており、設定自体もそこまで複雑ではないため、コンシューマー向けのAPI認証・認可を行う手段としては最も広く使われているのではないでしょうか。
IAM認証(Amazon API Gatewayの場合のみ)
他のAWSサービスからAmazon API Gatewayとやり取りを行う場合にはIAM認証がよく利用されます。IAMポリシーでAPIアクセスを制御可能なため、AWS CloudTrailでAPI呼び出しの監査ログを取得も可能です。例えば、Amazon API GatewayにAPI認証を有効化して、リクエストを送る側のIAMユーザやIAMロールに以下のようなIAMポリシーを適用します。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "execute-api:Invoke",
"Resource": "arn:aws:execute-api:us-east-1:123456789012:abcd1234/*/*"
}
]
}
クライアントは、API Gatewayにリクエストを送信する際に AWS Signature Version 4(SigV4) を使ってリクエストを署名する必要があります。SigV4はAWS SDKを使用すると簡単に生成することができます。AWSサービス間でAmazon API Gatewayを呼び出す場合にはIAM認証を使用するのがベターでしょう
IP アドレス制限
開発環境などあらかじめVPNなどで接続先のIPアドレスが分かっている場合にはIPアドレスで制限をかけしまうのも良いでしょう。ある種、堅守かつ簡易な設定で可能になります。Amazon API Gatewayでは、以下のようにIAMポリシーで接続先のIPを限定することが可能になります。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Action": "execute-api:Invoke",
"Resource": "arn:aws:execute-api:us-east-1:123456789012:abcd1234/*/*",
"Condition": {
"NotIpAddress": {
"aws:SourceIp": "203.0.113.0/24"
}
}
}
]
}
他にもAWS WAFを使ってIPアドレスのルールを作って許可及び拒否のリストを作ることができます。また、更に複雑な条件でIP制限を行いたい場合にはLambdaオーソライザーを使うという手段もあります。プログラムでIPアドレスの許可及び拒否のリストを定義することができるため複雑な条件分岐などを記述することが可能になります。
Usage Plan とスロットリング
Usage PlanとはAPIキーを持つクライアントに対してリクエスト制限を適用する仕組みであり、特定のクライアントが一定のレート(リクエスト数)を超えないように制御できます。スロットリング設定を行うことで1秒あたりにアクセス可能なリクエスト数を設定することが可能になるため、この設定をしておくことでDDoSなど想定外のAPIの過負荷を防ぐことができます。DDoSを大量に食らうことで多額の料金を払う羽目になってしまうケースがありますが、AWSの場合にこれを防ぐためにはAWS Shield Advancedを有効化する必要があります。しかし、料金が$3,000/月とかなり高額です。なので多くの場合ではスロットルさせて一回サービス止めるか、Cloudflareを併用するのが現実的な手段となるでしょう
また、クォータを設定しておくことで1日・1週間・1か月単位でのリクエスト上限を設定することができます。これにより特定のクライアントが意図しない大量のリクエストを送信してAPIのリソースを圧迫することを防ぐことが可能になります。例えば、無料ユーザには月に1万リクエストを許可して有料ユーザには月に10万リクエストを許可するなど、全体のリクエストを体系立てて制御することが可能になります。
そして全リクエストは後からログとしてダウンロード可能であり、悪意のあるユーザなどを見分けることが可能となります。もし、大量にアクセスのあるAPIを運用している場合には、これらを設定して安定したAPI運用を目指すことを考えましょう。
キャッシュ戦略
APIの背後にキャッシュをもたせることでパフォーマンスを向上させようとする仕組みはよくあると思います。Moment Cacheなどを使うことでキャッシュサーバーの運用自体はだいぶ楽に行えるようになりました。しかし、セキュリティの観点ではキャッシュの設計に注意しましょう。例えばユーザー認証のあるサイトでAさんがログインしている状態でBさんのキャッシュが返ってきてしまったら、重大なセキュリティインシデントとなってしまう可能性があります。
なので、キャッシュの部分については、使用するライブラリの動作(デフォルトでキャッシュさせるような設定になっていないかなど)含めて全容を把握して設定を行うようにしましょう。例えば、ユーザ情報を返すAPIのパスが/users
で始まるのであれば、そもそもそのURL配下はキャッシュさせないというのでもよいでしょう。その他にもユーザ情報関連はフロントエンド側のみにキャッシュをさせてサーバー側のキャッシュとは役割を使い分けるという戦略もありです。
JSONインジェクション及びSQLインジェクション
JSONインジェクションとはリクエストのJSONに意図しないデータを挿入して攻撃を行うことです。以下のようにAPI作成者が意図しないJSONリクエストを送られて、そのままAmazon DynamoDBなどにデータを格納できてしまうとアプリケーションの不正操作や不具合等を意図的に発生させてしまうかもしれません。
{
"user": {
"id": "123",
"name": { "$ne": "admin" }
}
}
また、リクエストにXSSのようなスクリプトを埋め込まれてそのままブラウザに表示されることが発生する危険性もあります。
{
"comment": "<script>alert('Hacked!');</script>"
}
SQLインジェクションはRDBを使用している際にしか起こりませんが同様にアプリケーション内のSQLを不正動作させるようなリクエストを送ってユーザ情報を抜き取ったりが可能になる攻撃です。
Amazon API Gatewayを使う場合は標準でこれらはサニタイズされますがそれ以外の場合はサニタイズのための仕組みを入れるようにしましょう。WAF の Managed Rule にこれらを防ぐルールがあるのでそれらも可能であれば使用しましょう。そしてアプリケーション側ではJSONの型チェックやSQLのサニタイズを行うようにしましょう。
各プログラム言語でJSONのバリデーションを行ってくれるライブラリは存在していますし、主要なフレームワークでもSQLインジェクション対策の仕組みは必ず実装されているのでそれらを正しく使用するようにしましょう。
リソースの認可チェック
ユーザー認証のあるシステムであれば、扱うデータがその当人しか扱えないかその他の人も扱えるかが分かれてくるでしょう。例えば家族の写真をシェアできるようなアプリで他人の家族の写真が勝手に見れてしまえば大きなインシデントになってしまいます。これらのデータはアプリケーション内でチェックしてfamily_id
などが一致しなければエラーを返すように実装する必要があります。jwt認証を使用している場合には標準クレームとして自分自身のユーザIDが含まれているのでそれを元にアプリケーション内でチェックを行うことができます。
まとめ
本記事ではREST APIで考慮が必要なセキュリティ対策について述べてみました。あまりREST APIの実装に慣れていなかったりすると、ついうっかり忘れてしまう内容も合ったかと思います。しっかりこれらを抑えてセキュアなREST APIを実装するようにしましょう。