Cloudflare API Shield の Schema Validation が無償化されたので改めて設定手順
Cloudflare は API Shield という API (Application Programing Interface) を保護するサービスを提供しています。APIの通信は通常のHTML通信と異なりプログラムからコールされるためWAFや対DDoSが提供しているクライアントブラウザを判別する仕組みとは別の保護が要求されます。またPublicに公開されるウェブサーバのHTMLやCSS、画像と異なりJSON等で機密性の高いデータが流れるケースも多く、その中身の文字列や構造を読み取ってセキュリティを実装することが求められます。それを実現するものが API Shield です。REST APIに対応しています。
主な機能として以下が提供されます。
1.認証と認可: APIへのアクセスを制限し、認証されたユーザーのみが利用できるようにします。
2.レート制限: 不正なアクセスやボットによる攻撃を防ぐため、API呼び出しの頻度を制限します。
3.Bot Management: 自動化されたボットからの攻撃を識別し、対策を講じます。
4.WAF(Web Application Firewall)連携: 悪意のあるリクエストを検出し、ブロックすることで、APIの安全性を高めます。
5. Schema Validation: APIが受け付けるリクエストの構造やタイプを定義し、定義外の通信を遮断します。
今回 Schema Validation の機能が無料プランを含めた全Cloudflareユーザーで利用可能になりましたのでその設定手順を以下にまとめます。
さっそくやってみる
1. APIの作成とCloudflare経由の通信の設定
検証のためにAPIを作成するのは難易度が高く手順が煩雑になるため、すでに存在しているものを借ります。
http://httpbin.org/
というウェブサイトが存在します。
POST
ボタンをクリックします。
Try it out
→Execute
をクリックします。そうすると以下の通り何某かのレスポンスが出力されます。
次にname
とage
という2つのパラメータに値を投げ込む以下のcurlコマンドを実行します。
curl -X POST "http://httpbin.org/post" -H "accept: application/json" -d '{"name": "John Doe", "age": 30}' -H "Content-Type: application/json"
以下のようなエコーレスポンスが戻ってくれば成功です。POSTされた値が戻って出力されています。
{
"args": {},
"data": "{\"name\": \"John Doe\", \"age\": 30}",
"files": {},
"form": {},
"headers": {
"Accept": "application/json",
"Content-Length": "31",
"Content-Type": "application/json\u00e3\u0080\u0080-v",
"Host": "httpbin.org",
"User-Agent": "curl/8.5.0",
"X-Amzn-Trace-Id": "Root=1-6715d838-3c20d3bd5bc237de657c5291"
},
"json": {
"age": 30,
"name": "John Doe"
},
"origin": "18.183.160.174",
"url": "http://httpbin.org/post"
}
ヘッダーに出力されているX-Amzn-Trace-Id
はおそらくこのサイトがAWSのApplication Load Balancerを使っていることを示唆していますが、一般的なRESTのResponseには含まれません。
このサイトが素晴らしいのはテスト用に外部の任意のドメインからのCNAME設定を許可していることです。ではCloudflare側でCNAMEを追加します。(この手順をためすにはCloudflare側でドメイン名を持っておく必要があります)
設定が出来たら再度curlを設定したドメインに対して実行します。
curl -X POST "http://a.harunobukameda.com/post" -H "accept: application/json" -d '{"name": "John Doe", "age": 30}' -H "Content-Type: application/json"
先ほどと同様にレスポンスが戻れば成功です。
2. API Shield の有効化とテスト
次にマネージメントコンソール左ペインでSecurity
→API Shield
をクリックします。
Add endpoints
をクリックします。
以下のようにルート/
に対するPOSTを保護対象と設定しAdd endpoints
をクリックします。
この状態で先ほどのcurlコマンドを5回ほど実行して数分待ちます。
面倒な場合以下でcron実行も可能です。
watch -n 1 'curl -X POST "http://a.harunobukameda.com/post" -H "accept: application/json" -d "{\"name\": \"John Doe\", \"age\": 30}" -H "Content-Type: application/json"'
以下のようにアクセス解析が開始されました。
有償版であればさらに追加機能として、ウェブ管理者が認識していないAPIへの通信が存在していた場合、その検知も行われます。
次にSchema Validation
のタブをクリックします。
現時点ではまだSchemaが登録されていないためno schema
となっています。
Add validation
をクリックします。
以下のJSONファイルをAPIスキーマとしてアップロードします。このスキーマはOpenAPI 3.0に準拠しているyamlかjsonを登録可能です。
{
"openapi": "3.0.0",
"info": {},
"servers": [
{
"url": "https://a.harunobukameda.com"
}
],
"components": {},
"paths": {
"/post": {
"post": {
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"age": {
"type": "integer"
}
},
"required": ["name", "age"],
"additionalProperties": false
}
}
}
},
"responses": {
"200": {
"description": "Success"
}
}
}
}
}
}
シンプルにまとめると/postに対して以下が定義されています。
・POSTメソッドのみ受け付け
・content-typeはapplication/json
のみ
・string型のnameとinteger型のageのみパラメータとして受付可能でこの2つは必須
・それ以外のパラメータは受け付け不可
という定義がされています。
set action
でBlock
を指定します。この定義に合致しないリクエストは受け付けない、という意味です。無償版ではLog
モードは設定できません。
設定が完了するとデプロイが行われるまで数分待ちます。以下のようにActive schema
に先ほどのjsonがセットされていればデプロイ完了です。
先ほどのcurlコマンドを実行すると正しいレスポンスが引き続き戻ってきますが、例えば以下のように余分なパラメータを付け加えた場合エラーが戻ります。
curl -X POST "http://a.harunobukameda.com/post" -H "accept: application/json" -d '{"name": "John Doe", "age": 30、"test":"kameda"}' -H "Content-Type: application/json"
error code: 1020
マネージメントコンソールでは以下の通り不正なアクセスがあったことがわかります。
Security
→Event
ではどのような理由で通信がブロックされているか、を確認可能です。
Discussion