- ブラウザURL入力
- DNS名前→IP
- ルータ経路選択
- ロードバランサ振り分け
- Nginx受け口
- アプリ処理今ここ
- DB保存今ここ
第40週で「通信路から来た値は検査の対象」という線を引きました。今回は、その値が構文として解釈されてしまう経路を見ます。
まず結論
文字列を組み立ててから渡すと、**コードとデータが1本の文字列に溶けます**。溶けた後では、どこまでが命令でどこからが値かを、受け取った側が文字の並びだけで決め直すことになります。
プレースホルダが効く理由を OWASP はこう説明しています。**SQLコードを先にすべて定義し、パラメータは後から渡す**。だから後から渡した値は、もう構文にはなれません。
プレースホルダ(プリペアドステートメント)が効く理由として、OWASP の説明に最も近いものはどれでしょうか。
当てることが目的ではありません。外した予測ほど記憶に残るので、直感で選んでください。
溶けるのは、渡す前
利用者が入れた文字列を使って問い合わせ文を組み立てる、という書き方があります。このとき何が起きているかを、中身ではなく形で見ます。
組み立てて渡す 1本の文字列 … 命令と値が同じ並びの中にある分けて渡す 命令(確定済み) + 値(別枠) … 2つが別のまま届く
上の行では、渡す前の段階で命令と値が1本の文字列に溶けています。受け取った側は、届いた文字の並びを構文の規則で読むしかありません。どこまでが命令でどこからが値かは、その並びから決め直されることになります。
ここで注意したいのは、利用者が入れた値に「これは値です」という印は付いていない、ということです。第2週で見たとおり、01000001 というビット列自体に「数値」とも「文字」とも書かれていません。解釈する側が決めているのでした。文字列も同じで、値が命令の位置に置かれれば、命令として読まれる余地が生まれます。
危険の正体は、特定の記号ではなく値が構文になれる位置にあることです。だから本教材では、危ない文字の一覧は扱いません。
OWASP は、この書き方をした場合のデータベースの振る舞いをどう表現していますか。
OWASP が挙げている理由は、順序の話
では、なぜプレースホルダを使うと解決するのか。OWASP の SQL Injection Prevention Cheat Sheet は、Defense Option 1(プリペアドステートメント)についてこう書いています。
If database queries use this coding style, the database will always distinguish between code and data, regardless of what user input is supplied.
regardless of what user input is supplied — どんな入力が供給されようと、です。入力の中身を調べた結果として区別できる、とは書かれていません。
その仕組みは、続く一文にあります。
Prepared statements are simple to write and easier to understand than dynamic queries, and parameterized queries force the developer to define all SQL code first and pass in each parameter to the query later.
要点は順序です。
1. define all SQL code first … SQLコードを先に全部確定させる2. pass in each parameter later … 各パラメータを後から渡す
構文は1の時点で確定しています。2で渡した値は、確定済みの構文の穴を埋めるだけです。値がどんな並びであっても、構文を作り直す機会はもう来ません。
これが「危険な文字を取り除く」のとは別の解き方だという点が大事です。除去は、漏れなく処理できたかに正しさが依存します。順序で解く方法は、値が構文になれる場面そのものを作りません。依存している対象が違います。
第2週で「ビット列そのものには意味が書かれていない」ことを見ました。今回の話との関係として適切なものはどれですか。
組み立てて渡す問い合わせ文
命令と値が1本の文字列に溶け、区切りは文字の並びから読み取られます。値が構文の形をしていれば、境界の読み取り結果が変わる余地が生まれます。プレースホルダは、構文を先に確定させて値を別枠で渡すことで、この余地を無くします。
溶かして渡す 命令と値が同じ並び … 境界は並びから決まる分けて渡す 構文は確定済み … 値は穴を埋めるだけ
フレームの区切り(第15週)
第15週では、Ethernet が区切りの情報を中身の外側(前に付ける枠)に置き、CSVや改行区切りは中身と同じ流れの中に置くことを比べました。外側に置く方式では、ペイロードにどんなビット列が現れても区切りの判断は影響を受けません。中身に混ぜる方式では区切り文字がデータとしても現れうるため、引用符やエスケープという追加の規則が必ず要ります。
外側に置く 中身が何でも区切りは動かない中身に混ぜる 区切り文字がデータにも現れうる → 追加の規則が要る
問い合わせ文の組み立てと、フレームの区切り。層はまったく違いますが、共通している構造は何でしょうか。
どちらも「どこまでが1つの塊か」を、誰がどこから読み取るかの話です。
この章のまとめ
- 文字列を組み立てて渡すと、渡す前にコードとデータが1本に溶ける。受け取った側は並びから境界を決め直す。
- 値には「これは値です」という印が無い。第2週と同じく、解釈する側が意味を決める。
- OWASP の理由は順序:SQLコードを先に全部定義し、パラメータを後から渡す。だから値は構文になれない。
- 効果の範囲は “regardless of what user input is supplied” — 入力の中身を調べた結果ではない。
- 第15週の区切りと同型。境界を中身に混ぜるか、外側に置くか。
- WAF は第37週のとおり緩和であって修正ではない。
自分の現場で確かめる
読んだだけでは身につきません。手元の環境で実際に見てみてください。
理解度チェック
読んだ直後は「分かった」と感じやすいものです。実際に解いて確かめてください。
「危険な文字をエスケープしていれば、プレースホルダは使わなくてよい」という説明の評価はどれですか。
用語集(3語)
- コードとデータの区別
- OWASP はプリペアドステートメントの効果を “the database will always distinguish between code and data, regardless of what user input is supplied” と説明している。区別が**入力の中身によらず**成立する点が要点。
- プリペアドステートメント / パラメータ化クエリ
- OWASP の説明では “force the developer to define all SQL code first and pass in each parameter to the query later”。SQLコードを先に全部定義し、各パラメータを後から渡すことを開発者に強制する書き方。
- 緩和 (mitigation)
- 危険を減らすが、原因そのものは残る対処。第37週の WAF の項目が「脆弱性を直すものではなく緩和する仕組み」と述べているのと同じ用法。