OSSのAPI ファイアウォール「API Firewall (wallarm社)」の翻訳メモ
はじめに
wallarm社が開発しているOSS(golang製)のAPI ファイアウォールである「API Firewall」の機能に興味があったのでドキュメント類を勝手に翻訳(DeepL)しました。
デモの内容はリクエスト&レスポンスの粒度で記載されていますので、API用ファイアウォール(API用WAF)がどのようなものかご存知ない方にはとても興味ある内容になっていると思います。
API Firewall のリポジトリはこちらです。(本記事はtag/v0.6.8
版)
「README.md(ツールの概要)」と「ツールの利用デモ(Docker版)」を記載しています。
時間ない人向け
wallarm API Firewall の主な機能
- 悪意のあるリクエストをブロックすることで、REST APIのエンドポイントを保護
- ↑ 本記事で紹介するデモの内容
- 不正なAPIレスポンスをブロックすることで、APIデータの漏洩を防止
- シャドウAPIエンドポイントの検出
- OAuth 2.0プロトコルベースの認証のためのJWTアクセストークンの有効化
- 漏洩したAPIトークン、キー、およびCookieを拒否
参考
- wallarm/api-firewall: Fast and light-weight API proxy firewall for request and response validation by OpenAPI specs. | GitHubリポジトリ
- API Firewall overview - Wallarm Documentation | 公式ドキュメント
README.md
API Firewall はOpenAPI/Swaggerスキーマに基づいたAPIリクエストとレスポンスの検証を行う高性能なプロキシです。
クラウドネイティブ環境におけるREST APIエンドポイントを保護するために設計されています。
API Firewall はポジティブセキュリティモデルを使用して、リクエストとレスポンスに対して、事前に定義されたAPI仕様に一致する呼び出しを許可し、それ以外は拒否することでAPIの堅牢化を実現します。
API Firewall の主な機能は以下のとおりです。
- 悪意のあるリクエストをブロックすることで、REST APIのエンドポイントを保護
- 不正なAPIレスポンスをブロックすることで、APIデータの漏洩を防止
- シャドウAPIエンドポイントの発見
- OAuth 2.0プロトコルベースの認証のためのJWTアクセストークンの有効化
- (NEW) 漏洩したAPIトークン、キー、およびCookieを拒否
この製品はオープンソースでDockerHubで公開されており、すでに10億回(!!)のプルを獲得しています。このプロジェクトをサポートするためにリポジトリ(wallarm/api-firewall)にスターを付けることができます。
ユースケース
ブロッキングモードでの動作
- OpenAPI 3.0仕様に合致しない悪意のあるリクエストをブロックする
- 不正なAPIレスポンスをブロックし、データ漏洩や機密情報の流出を防止します。
モニタリングモードでの実行
- シャドウAPIや文書化されていないAPIエンドポイントを発見する
- OpenAPI 3.0 仕様に合致しない不正なリクエストとレスポンスをログに記録します。
API スキーマの検証およびポジティブセキュリティモデル
API Firewallの起動時に、API Firewallで保護するアプリケーションの OpenAPI 3.0 仕様 を提供する必要があります。起動した API Firewall はリバースプロキシとして動作し、リクエストとレスポンスが仕様に定義されたスキーマと一致するかどうかを検証します。
スキーマに一致しないトラフィックは、STDOUT
およびSTDERR
のDockerサービスを使用してログに記録されるか、ブロックされます(設定されたAPI Firewallの動作モードによる)。
ロギングモードで動作している場合、API Firewall はいわゆるシャドウAPIエンドポイント、つまりAPI仕様でカバーされていないがリクエストに応答するエンドポイントもロギングします(コード404を返すエンドポイントを除く)。
OpenAPI 3.0 仕様をサポートしており、YAML または JSON ファイル (.yaml
, .yml
, .json
ファイル拡張子) として提供される必要があります。
OpenAPI 3.0 仕様でトラフィック要件を設定できるようにすることで、API Firewall はポジティブセキュリティモデルに依存します。
技術データ
API Firewall は OpenAPI 3.0 のリクエストとレスポンスのバリデーションを内蔵したリバースプロキシとして動作します。Golangで書かれており、fasthttpプロキシを使用しています。
このプロジェクトは、極めて高いパフォーマンスと、ゼロに近いレイテンシを追加するために最適化されています。
API Firewall に関連するWallarmのブログ記事
-
Discovering Shadow APIs with Wallarm API firewall
(Wallarm API Firewall によるShadow APIの発見) -
Wallarm API Firewall outperforms Nginx in a production environment
(Wallarm API Firewall、本番環境でNginxを上回る性能を発揮)
パフォーマンス
API Firewall の開発にあたっては、お客様に最速の API を提供できるよう、スピードと効率を最優先しました。最新のテストでは、API Firewall が 1 つのリクエストを処理するのに要する平均時間は 1.339ms
で、Nginx より 66% も高速であることが実証されています。
API Firewall 0.6.2 with JSON validation
$ ab -c 200 -n 10000 -p ./large.json -T application/json http://127.0.0.1:8282/test/signup
Requests per second: 13005.81 [#/sec] (mean)
Time per request: 15.378 [ms] (mean)
Time per request: 0.077 [ms] (mean, across all concurrent requests)
NGINX 1.18.0 without JSON validation
$ ab -c 200 -n 10000 -p ./large.json -T application/json http://127.0.0.1/test/signup
Requests per second: 7887.76 [#/sec] (mean)
Time per request: 25.356 [ms] (mean)
Time per request: 0.127 [ms] (mean, across all concurrent requests)
このパフォーマンス結果は、API Firewallのテスト中に得られた唯一のものではありません。
その他の結果や、API Firewallのパフォーマンスを向上させるために使用した方法については、このWallarmのブログ記事(Wallarm API Firewall outperforms Nginx in a production environment)で説明しています。
Docker Compose を使って Wallarm API Firewall のデモ
このデモでは、アプリケーション httpbin と、httpbin API を保護するプロキシとして Wallarm API Firewall を導入しています。
両アプリケーションは、Docker Composeで接続されたDockerコンテナで実行されています。
システム要件
このデモを実行する前に、お使いのシステムが以下の要件を満たしていることを確認してください。
- Docker Engine 20.x 以降(Mac、Windows、Linix用)がインストールされていること
- Docker Compose がインストールされていること
- Mac、Windows、またはLinux用にmakeがインストールされていること(適切なパッケージ管理ユーティリティを使用すること)
利用するリソース
このデモでは、以下のリソースを使用しています。
デモコードの説明
デモコードには、以下の設定ファイルが含まれています。
- volumes ディレクトリにある以下の OpenAPI 3.0 仕様書。
-
httpbin.json
はhttpbin OpenAPI 2.0 仕様を OpenAPI 3.0 仕様フォーマットに変換したものです。 -
httpbin-with-constraints.json
は、httpbin OpenAPI 3.0 仕様に、明示的に API の制限を追加したものです。
-
この2つのファイルは、デモのデプロイメントをテストするために使用されます。
-
Makefile
はDockerのルーチンを定義する設定ファイルです。 -
docker-compose.yml
はhttpbinとAPI FirewallのDockerイメージの構成を定義するファイルです。
ステップ 1: デモコードを動かす
デモコードを実行するには
-
デモコードを含む GitHub リポジトリをクローンします。
git clone https://github.com/wallarm/api-firewall.git
-
クローンしたリポジトリの
demo/docker-compose
ディレクトリに変更します。cd api-firewall/demo/docker-compose
-
以下のコマンドでデモコードを実行します。
make start
- API Firewall で保護されたアプリケーション httpbin は、
http://localhost:8080
で利用可能です。 - API Firewall で保護されていないアプリケーション httpbin は、
http://localhost:8090
で利用可能です。
デモのデプロイメントをテストする際に、保護されていないアプリケーションにリクエストを送信して、その違いを確認することができます。
- API Firewall で保護されたアプリケーション httpbin は、
-
デモのテストに進む。
ステップ 2: OpenAPI 3.0 オリジナル仕様に基づくデモのテスト
デフォルトではこのデモはオリジナルの httpbin OpenAPI 3.0 仕様で実行されています。
このデモのオプションをテストするには、次のリクエストを使用します。
- API Firewall が公開されていないパスに送られたリクエストをブロックすることを確認します。
curl -sD - http://localhost:8080/unexposed/path
期待されるレスポンス:
HTTP/1.1 403 Forbidden
Date: Mon, 31 May 2021 06:58:29 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 0
Apifw-Request-Id: 0000000200000001
- API Firewall が整数データ型を必要とするパラメーターに文字列を渡したリクエストをブロックすることを確認します。
curl -sD - http://localhost:8080/cache/arewfser
期待されるレスポンス:
HTTP/1.1 403 Forbidden
Date: Mon, 31 May 2021 06:58:29 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 0
Apifw-Request-Id: 0000000200000001
このケースは、API Firewall が Cache-Poisoned DoS 攻撃からアプリケーションを保護することを実証しています。
ステップ 3: より厳格な OpenAPI 3.0 仕様に基づくデモのテスト
まず、デモで使用しているOpenAPI 3.0仕様のパスを更新してください。
-
docker-compose.yml
ファイルのAPIFW_API_SPECS
環境変数の値を、より厳しいOpenAPI 3.0仕様のパス(/opt/resources/httpbin-with-constraints.json
)に置き換えてください。 - コマンドを使用して、デモを再起動します。
make stop
make start
次に、このデモオプションをテストするために、以下の方法を使用することができます。
- API Firewallが、以下の定義に合致しない必須クエリパラメータintを持つリクエストをブロックすることを確認する。
...
{
"in": "query",
"name": "int",
"schema": {
"type": "integer",
"minimum": 10,
"maximum": 100
},
"required": true
},
...
以下のリクエストを使って、定義をテストしてください。
# 必要なクエリパラメータが不足しているリクエスト
curl -sD - http://localhost:8080/get
# 期待されるレスポンス
HTTP/1.1 403 Forbidden
Date: Mon, 31 May 2021 07:09:08 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 0
Apifw-Request-Id: 0000000100000001
# int型のパラメータが有効範囲内の値を持つリクエスト
curl -sD - http://localhost:8080/get?int=15
# 期待されるレスポンス
HTTP/1.1 200 OK
Server: gunicorn/19.9.0
Date: Mon, 31 May 2021 07:09:38 GMT
Content-Type: application/json
Content-Length: 280
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Apifw-Request-Id: 0000000300000001
...
# int型のパラメータ値が範囲外である場合のリクエスト
curl -sD - http://localhost:8080/get?int=5
# 期待されるレスポンス
HTTP/1.1 403 Forbidden
Date: Mon, 31 May 2021 07:09:27 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 0
Apifw-Request-Id: 0000000200000001
# int型のパラメータ値が範囲外である場合のリクエスト
curl -sD - http://localhost:8080/get?int=1000
# 期待されるレスポンス
HTTP/1.1 403 Forbidden
Date: Mon, 31 May 2021 07:09:53 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 0
Apifw-Request-Id: 0000000400000001
# int型のパラメータ値が範囲外である場合のリクエスト
# 悪性: 8バイト整数のオーバーフローはスタックドロップで応答する可能性がある
curl -sD - http://localhost:8080/get?int=18446744073710000001
# 期待されるレスポンス
HTTP/1.1 403 Forbidden
Date: Mon, 31 May 2021 07:10:04 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 0
Apifw-Request-Id: 0000000500000001
- API Firewallが以下の定義に合致しないクエリパラメータ
str
を持つリクエストをブロックすることを確認します。
...
{
"in": "query",
"name": "str",
"schema": {
"type": "string",
"pattern": "^.{1,10}-\\d{1,10}$"
}
},
...
以下のリクエストを使って、定義をテストしてください(intパラメータはまだ必要です)。
# strパラメータ値が定義された正規表現に一致しないリクエスト
curl -sD - "http://localhost:8080/get?int=15&str=fasxxx.xxxawe-6354"
# 期待されるレスポンス
HTTP/1.1 403 Forbidden
Date: Mon, 31 May 2021 07:10:42 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 0
Apifw-Request-Id: 0000000700000001
# strパラメータ値が定義された正規表現に一致しないリクエスト
curl -sD - "http://localhost:8080/get?int=15&str=faswerffa-63sss54"
# 期待されるレスポンス
HTTP/1.1 403 Forbidden
Date: Mon, 31 May 2021 07:10:42 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 0
Apifw-Request-Id: 0000000700000001
# strパラメータ値が定義された正規表現にマッチするリクエスト
curl -sD - http://localhost:8080/get?int=15&str=ri0.2-3ur0-6354
# 期待されるレスポンス
HTTP/1.1 200 OK
Server: gunicorn/19.9.0
Date: Mon, 31 May 2021 07:11:03 GMT
Content-Type: application/json
Content-Length: 331
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Apifw-Request-Id: 0000000800000001
...
# strパラメータ値が定義された正規表現に一致しないリクエスト
# 悪性: SQLインジェクション
curl -sD - 'http://localhost:8080/get?int=15&str=";SELECT%20*%20FROM%20users.credentials;"'
# 期待されるレスポンス
HTTP/1.1 403 Forbidden
Date: Mon, 31 May 2021 07:12:04 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 0
Apifw-Request-Id: 0000000B00000001
ステップ 4: デモコードの停止
デモのデプロイを停止して環境をクリアするには、コマンドを使用します。
make stop
Discussion