はじめに
Web アプリケーションのセキュリティ対策は、非常に重要なものの、対策に専門知識が必要です。SQL インジェクション、クロスサイトスクリプティング、DDoS (BOT)攻撃、認証バイパスなど、多くの危険がインターネットにあふれています。「あなたこのアプリケーションのセキュリティを任せた!」と突然言われたときに、自信をもって対処できるでしょうか?
これらの脅威に対応するため、Web Application Firewall (WAF) が利用できます。Oracle Cloud では、WAF 機能を提供しており、次の特徴があります。
- 非常に多くのプロテクションルールを標準装備
- アクセス傾向を分析して、「これつかったらいいよ」とルール有効化を推奨してくれる機能
- Bot 対策のための高度な機能
- IPアドレスや国情報を使って、基本的なブロック機能
今回は、Bot 対策のための機能をいくつか触ってみましょう。Bot 対策は、OCI WAF では Bot Management というまとまりで、いくつかの機能が提供されています。
- JavaScript Challenge : アクセス元で JavaScript の実行を観察し、ブラウザかどうか判定。JavaScript を実行できない簡易的な Bot を検出
- Human Interaction Challenge : サイトの滞在時間、マウスの動き、ページスクロールなど、通常の人間であれば行う振る舞いを観察して、Bot を検出
- Device Fingerprint Challenge : OS, 画面の解像度, ブラウザのプラグインなど、アクセス元の情報を多角的に分析して、Botを検出
- CAPTCHA Challenge : Bot には読み取りにくい画像を表示して、人間がアクセスしているか確認
- Good Bot Whitelist : Google や Yahoo, New Relic など、サービス展開に有効な Bot をホワイトリストに追加する機能
今回は、Human Interaction Challenge と、Device Fingerprint Challenge の設定方法と、その挙動を確認してみます。Device Fingerprint Challenge のルールに引っ掛けることは難しかったため、Human Interaction Challenge で検出して、Block する動きをみていきます。
Human Interaction Challenge
Human Interaction Challenge は、Bot を特定してブロックするための機能です。マウスの動き、サイトの滞在時間、ページスクロールなど、一般的なユーザーが行うような挙動を観察して判断する、高度な対策のようです。ようです、と言っているのは、中身のアルゴリズムがブラックボックスで、外からはわかりません。
WAF がリクエストを受け付けると、Human Interaction Challenge が様々なイベントをチェックして、一般的なユーザーか、Bot かを判断して、設定されたアクションを行います。
設定をしていきましょう。WAF の設定画面にある、Bot Management から、Edit Human Interaction Challenge で設定をします。
各種パラメータを入れて、Human Interaction Challenge を有効化します。各パラメータの意味を Document や挙動から調査した内容をまとめます。一部明確に書かれていない部分もあり、推測と観察結果が含まれています。
- SET HEADER FOR FAILED REQUEST :
Action に Detect(検出) を指定したときに利用する設定。Detect されたリクエストに、Detect した事を表す Header を追加して、Origin へリクエストを飛ばす。指定した任意の名前と、Detect した Request の累積数が Header に設定される。 - ACTION THRESHOLD (NUMBER OF REQUESTS) :
アクションを起こす条件を指定。Bot と思われるようなリクエストを、何回受信したらアクションを起こすのか、リクエスト数の閾値を指定。
10 と設定したとき、Bot と思われるようなリクエストを10回受信しても Action をしない。11回目受信したときに、Action を行う。
最初はその数値が適切かわからないと思うので、Document に記載されている推奨値が良いと思う。基本的な Ajax を使う Web アプリケーションでは10で指定、Ajax 頻繁に使用する Web アプリケーションは 100 を指定。 - THRESHOLD EXPIRY PERIOD (SECONDS) :
ACTION THRESHOLD
でカウントしている数が、リセットされるまでの間隔。指定した秒数で、定期的にリセットされる (動作検証の結果、このような挙動に見える) - ACTION EXPIRE TIME (SECONDS) :
Bot 検知して 一時的に Block した状態から、Block 解除するまでの秒数を指定 (動作検証の結果、このような挙動に見える) - INTERACTION THRESHOLD (NUMBER OF INTERACTIONS) :
Bot と判断するための条件。マウスを動かす、クリックをする、キーボードを打つ、といった人間が行う活動を 1 インタラクションとする。一定時間に、指定回数以上のインタラクションがあれば、人間の操作と判断する。
OCI Console に次の英語の表示ありTo pass the challenge, the user should make X events (mouse movements, clicks, and so on) during the recording period.
- RECORDING PERIOD (SECONDS) :
ユーザのインタラクションを記録する秒数。おそらく、指定した秒数の間に、インタラクションの回数がINTERACTION THRESHOLD
の閾値を超えると、人間のアクセスと判断される - NAT SUPPORT
有効にすると、NAT 環境で IP アドレスを共有している複数ユーザーの識別を試みるようになる。この機能を有効にすると、WAF 側での処理が増えるため、秒間 200リクエスト超えるようなたくさんのリクエストがある環境では、無効にするのが良い
設定例
Device Fingerprint Challenge
アクセス元ユーザーを IP アドレスだけではなく、様々な情報を基に識別して、Bot かどうかを検出する仕組みのようです。Oracle Cloud に関係なく、Device Fingerprint とは、アクセス元ユーザーの OS、IP アドレス、ブラウザの種類、画面解像度、インストール済みプラグイン、インストール済みフォント一覧、Useragent などの情報を使用して、ユーザーを特定する概念です。IP アドレスは NAT 環境では複数のユーザーで共有するため、複数の情報を基に多角的にユーザーを特定する概念です。Device Fingerprint は、アクセス元ユーザーによって、少しずつ異なるので、利用者を識別するための特徴点となります。
WAF の設定画面にある、Bot Management から、Edit Device Fingerprint Challenge で設定をします。
各種パラメータを入れて、Human Interaction Challenge を有効化します。各パラメータの意味を Document や挙動から調査した内容をまとめます。一部明確に書かれていない部分もあり、推測が含まれています。
-
ACTION THRESHOLD (NUMBER OF REQUESTS) :
アクションを起こす条件を指定。Bot と思われるようなリクエストを、何回受信したらアクションを起こすのか、リクエスト数の閾値を指定。
10 と設定したとき、Bot と思われるようなリクエストを10回受信しても Action をしない。11回目受信したときに、Action を行う。
最初はその数値が適切かわからないと思うので、Document に記載されている推奨値が良いと思う。基本的な Ajax を使う Web アプリケーションでは10で指定、Ajax 頻繁に使用する Web アプリケーションは 100 を指定。 -
THRESHOLD EXPIRY PERIOD (SECONDS) :
ACTION THRESHOLD
でカウントしている数が、リセットされるまでの間隔。指定した秒数で、定期的にリセットされる (動作検証の結果、このような挙動に見える) -
ACTION EXPIRE TIME (SECONDS) :
Bot 検知して 一時的に Block した状態から、Block 解除するまでの秒数を指定 (動作検証の結果、このような挙動に見える) -
MAX ADDRESS COUNT (IP ADDRESSES) :
同じ Device Fingerprint で許可される IP アドレスの最大数 -
MAX ADDRESS COUNT EXPIRATION (SECONDS) :
MAX ADDRESS COUNT の数がリセットされるまでの時間 (秒)
設定例
Bot検知確認
余談ですが、Human Interaction Challenge の機能を設定したため、WAF で保護しているWebページに JavaScript が組み込まれています。
> curl http://waf.sugioci.tokyo/
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; }</style>
<script type="text/javascript">(function(){ if (typeof(___zen) === "undefined") {setTimeout(arguments.callee, 50); return; }___zen.hic("__ZEHIC6475", 1599329407, 3, 15, 60); })()</script><script type="text/javascript" async="async" src="/__zenedge/assets/hic.js?v=1595328929"></script></head>
<body>
<h1>I am web01</h1>
<script type="text/javascript" async="async" src="/__zenedge/assets/f.js?v=1595328929"></script></body>
</html>
埋め込まれている JavaScript を確認してみると、こんな感じのソースコードです。
各種操作イベントを取得しているようなコードに見えます。
(function(d) {
function l() {
g && (document.removeEventListener ? (document.removeEventListener("mousemove", a),
document.removeEventListener("mousedown", a),
document.removeEventListener("click", a),
document.removeEventListener("mouseup", a),
document.removeEventListener("keydown", a),
document.removeEventListener("keypress", a),
document.removeEventListener("keyup", a),
document.removeEventListener("scroll", a)) : document.detachEvent && (document.detachEvent("onmousemove", a),
document.detachEvent("onmousedown", a),
document.detachEvent("onclick", a),
document.detachEvent("onmouseup", a),
document.detachEvent("onkeydown", a),
document.detachEvent("onkeypress", a),
document.detachEvent("onkeyup", a),
document.detachEvent("onscroll", a)),
g = !1)
}
function a() {
f += 1;
f < m || (0 == b && (b = Date.now ? Date.now() : (new Date).getTime() / 1E3,
b = Math.round(b / 1E3)),
document.cookie = h + "\x3d" + b + ";expires\x3d" + c + ";path\x3d/",
l())
}
d.___zen = d.___zen || {};
var h = "_undefined"
, b = "N"
, k = 0
, g = !1
, f = 0
, m = 0
, c = 0;
d.___zen.hic = function(e, d, f, n, p) {
m = f;
h = e;
b = d;
k = n;
e = new Date;
c = e.getTime();
c += 1E3 * p;
e.setTime(c);
c = e.toUTCString();
document.cookie = h + "\x3dN;expires\x3d" + c + ";path\x3d/";
document.addEventListener ? (document.addEventListener("mousemove", a),
document.addEventListener("mousedown", a),
document.addEventListener("click", a),
document.addEventListener("mouseup", a),
document.addEventListener("keydown", a),
document.addEventListener("keypress", a),
document.addEventListener("keyup", a),
document.addEventListener("scroll", a)) : document.attachEvent && (document.attachEvent("onmousemove", a),
document.attachEvent("onmousedown", a),
document.attachEvent("onclick", a),
document.attachEvent("onmouseup", a),
document.attachEvent("onkeydown", a),
document.attachEvent("onkeypress", a),
document.attachEvent("onkeyup", a),
document.attachEvent("onscroll", a));
g = !0;
0 < k && setTimeout(l, 1E3 * k)
}
}
)(window);
それでは本題です。Bot Management の設定を行ったので、動作するか確認してみましょう。今回はシンプルに、1秒に10回のアクセスを curl で行ってみます。
watch -n 0.1 curl http://waf.sugioci.tokyo/
WAF の Logs ページで、Bot として検知したものをフィルターして表示できます。画面左下のメニューで、時間帯や Log の Type などを指定できます。
153.246.137.125
(自分の今の環境のIPアドレス) からのアクセスを、Human Interaction Challenge で検知したことがわかります。
Detect の Log のなかに実際の HTTP Request の内容が見えます。HTTP Header に、Hic-Failed
が追加されています。Value が 193 となっており、193 回 Bot として Detect されていることがわかります。Human Interaction Challenge で指定した HTTP Header が追加されていますね。
Detect したアクセスを確認して、本当に悪意のあるユーザーだと判断できれば、Black List に追加することが出来ます。Bot Management の機能で自動的に Block が出来ますが、善良な一般ユーザーを Block してしまう危険性があるので、一旦は Detect で検知したものを人間の手で Black List へ追加するのが良いとおもいます。
一定期間、Detect されたものを検証して、品質が高そうだと判断できたら、自動 Block 設定へ変更するのが良いでしょう。
IP アドレスベースのアクセスコントロール
Bot っぽい IP アドレスを検知したので、IP アドレスでBlock する設定をします。
なお、WAF には、IP Address List を、管理する機能がありますが、使用しないほうがいいと思います。IP Address List を WAF Policy に紐づけて、特定の IP アドレスを Block 出来ますが、紐づけたあとに IP Address List を変更しても Block されませんでした。反映するためには、もう一度紐づけをすればいいのですが、管理面でつらいです。WAF が、どの IP アドレスを Block しているのか、設定値で把握出来ない原因を生んでしまいます。この原因を生むくらいであれば、WAF Policy に直接 IP アドレス を設定するのがやりやすいと感じています
IP Address リストが良くないと思う理由
- Address List はリアルタイムに反映されない。WAF Policy 側から再度更新しないと反映されないので、現在有効なブロックされる IP アドレス一覧が分からなくなる
- WAF Policy に Address List を紐づけて Update を実施したあと、45 分の間完了を待っている途中で、紐づけた Address List に設定した IP アドレス一覧を変更すると、期待していない動作となる。Block されているはずの IP アドレスが、アクセスする度に Block されたり、通信できたりする。途中で変更したことで、Edge Point 間で反映の差異が発生してしまった挙動に見える。
それでは、Black List の設定をしていきましょう。WAF Policy ページで、Add Access Rule を押します。
Condition を IP Address is
にして、IP Addresses に ブロックしたい IP アドレスをいれます。ちなみに、ここには複数の IP アドレスを改行で指定できます。指定した IP アドレスを全てブロックします。
Publish All を押すと、WAF Edge Point に設定が反映されます。全世界の Edge Point があり、45 分ほど設定に時間が掛かります。
Publish All を押します。
UPDATING となりました。45 分ほど掛かるので、待ちましょう。
UPDATING 中に、指定した IP アドレスからアクセスすると、Block されるアクセスもあれば、正常に通信できるアクセスもあります。アクセスする度に、状況に応じて切り替わります。全 Edge Point に反映する途中なので、このような挙動になると推測しています
なお、Block したものは Log で検索可能です。
Block された時の Response を確認します。403 Forbidden を返す設定にしたので、こんな感じになります。
> curl http://waf.sugioci.tokyo/
<html>
<head><title>403 Forbidden</title></head>
<body bgcolor="white">
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx</center>
</body>
</html>
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
Block されたとき、ブラウザから見るとこんな感じです。
Bot Management で自動 Block
Bot Management で自動 Block をした時の挙動を確認しましょう。Edit を押します。
次のパラメータを指定します。
- BLOCK に変更
- Response Code は 403 Forbidden
- Action Thread を 100回にする。Bot と検出されるまでに、怪しいリクエストを100回までは受け付ける。101 回目以降から、Bot として Block する。
- Action Expire Time を 10秒にする。Bot と検出されてから Block 解除するまでの時間を10秒とすることで、検証をしやすくする。本番環境ではもうちょっと長めでもいいと思う。
Publish All
設定完了後、1秒に10回のアクセスを curl で行ってみます。
watch -n 0.1 curl http://waf.sugioci.tokyo/
100 回の Action Thread
があり、最初の方はアクセスが正常に出来ています。何十秒か経ったあとに、ブロックされ始めたときのレスポンスです。403 が返ってきています。
なお、Block されてから 10秒後に 解除され、アクセスが正常に戻ります。
> curl -i http://waf.sugioci.tokyo/
HTTP/1.1 403 Forbidden
Date: Sun, 06 Sep 2020 02:03:41 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Connection: keep-alive
Cache-Control: no-store
Cache-Control: no-cache, no-store, must-revalidate
Cache-Control: max-age=0
X-Zen-Fury: 5562caaa10fd9e820ef10fbdee07d384c5f6c75d
Server: ZENEDGE
X-Cdn: Served-By-Zenedge
<html>
<head><title>403 Forbidden</title></head>
<body bgcolor="white">
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx</center>
<script type="text/javascript" async="async" src="/__zenedge/assets/f.js?v=1595328929"></script></body>
</html>
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
ブロックされたときは、ブラウザから見ると、こんな感じです。
参考URL
Human Interaction Challenge
https://docs.cloud.oracle.com/en-us/iaas/tools/oci-cli/2.9.6/oci_cli_docs/cmdref/waas/human-interaction-challenge/update.html
Device Fingerprint Challenge
https://docs.cloud.oracle.com/en-us/iaas/tools/oci-cli/2.9.6/oci_cli_docs/cmdref/waas/device-fingerprint-challenge/update.html
Fingerprint について
https://www.saitolab.org/fp_site/