エスケープすれば通る、が通らなかった理由
引っかかる文字の並びが2つ分かりました。次に考えたのは、それを別の書き方で送れば通るのではないかということです。
結論から書くと、通りませんでした。そしてその理由を知ったとき、通らなくてよかったと思いました。
「表示は同じで、送る文字だけ変える」という手がなぜ効かなかったか。判定する側が照合の前に何をしているか。そして、もし通っていたら何を意味するかです。
第1回:症状の切り分け/第2回:二分探索で原因を絞る/第3回:エスケープが効かなかった理由(この記事)/第4回:意味を変えずに書き直す/第5回:正しく止められたときに何を残すか
思いついた手
HTMLには、文字を番号で書く方法があります。記号そのものを書く代わりに、その文字の番号を決まった形で書くと、表示するときに元の記号に戻ります。
| 送る文字列 | ブラウザでの見え方 | |
|---|---|---|
| そのまま | 記号そのもの | 記号 |
| 番号で書く | 記号とは違う数字の列 | 同じ記号 |
つまり、読者が見るものは変えずに、サーバーへ送る文字列だけを変えられます。引っかかる並びは送られないので、これで通るはずでした。
変換する処理を書いて、全部の記事に当てて、投げました。
同じところで落ちた
403です。変換していないときと、まったく同じでした。
念のため確かめました。変換後の文字列には、引っかかる並びは1文字も含まれていません。それでも落ちます。
調べると、この手の判定には前処理があります。照合する前に、番号で書かれた文字を元の記号に戻してから見る、という処理です。
こちらが番号に変えて送っても、判定する側は戻してから照合しています。見ているものは、変換する前と同じでした。
他の逃げ方も同じ理屈で塞がっています。大文字と小文字を混ぜる、余計な空白を挟む、別の方式で符号化する。どれも、照合の前に揃えられます。
| こちらの手 | 判定側の前処理 | 結果 |
|---|---|---|
| 文字を番号で書く | 元の文字に戻す | 効かない |
| 大文字小文字を混ぜる | どちらかに揃える | 効かない |
| 余計な空白を挟む | 詰める | 効かない |
| 別の方式で符号化する | 復号する | 効かない |
通っていたら、それは穴だった
ここが、この一件でいちばん納得した部分です。
自分がやろうとしたのは、「判定に引っかからない形に変えて、受け取った側で元に戻してもらう」ことでした。
これは、攻撃する側がやりたいことと完全に同じです。
| やること | 狙い | |
|---|---|---|
| 自分 | 記号を番号に変えて送る | 判定をすり抜けて、記事を登録したい |
| 攻撃する側 | 記号を番号に変えて送る | 判定をすり抜けて、コードを実行したい |
手口が同じなので、自分だけ通る理由がありません。もしこの手で通っていたら、それは同じ手で攻撃も通るということでした。
止められている側にいると、通る方法を見つけたときに前進した気分になります。
ただ、止めている理由が正しいなら、通る方法は穴です。自分が使えたということは、他の人も使えます。今回は前処理があったので塞がっていました。無かったら、この防御はほぼ意味を持っていません。
方針を変えた
ここで、やろうとしていたことの向きを変えました。
| 考え方 | 成り立つか | |
|---|---|---|
| それまで | 同じ内容を、判定に見つからない形で送る | 成り立たない。成り立ったら穴 |
| ここから | 判定に引っかからない内容に書き直す | 成り立つ |
違いは、隠すか、書き換えるかです。隠す方向は、うまくいってもいかなくても困ります。書き換える方向なら、書いたものがそのまま通ります。
次回は、実際にサンプルコードをどう書き直したかを書きます。意味を変えずに形だけ変える手が3つありました。
今回の学び
まず、見た目を変えて通す手は、たいてい先に塞がれているということ。番号で書く、大文字小文字を混ぜる、空白を挟む。どれも攻撃する側が先に試した手なので、判定する側は照合の前に揃えてから見ています。
次に、迂回できたかどうかで、防御の質が測れること。自分の手が通ってしまったら、同じ手で攻撃も通ります。塞がれていたのは、その防御がきちんと作られていたということでした。
そして、隠す方向と書き換える方向は別物だということ。同じ内容を見つからない形で送るのは、成功しても困ります。内容そのものを別の形にするほうは、通ったものがそのまま残ります。向きを変えるまで、ずっと塞がった側を押していました。


