このレッスンですること
SQL インジェクションは、プログラムがデータベースへの質問文を作るとき、訪問者が入力した文字をそのまま質問文に貼り付けてしまうと起こります。訪問者は質問を途中で終わらせて、自分の質問を書き足せます。このレッスンでは、小さなお店を検索し、検索ページに見せてはいけないものを出させ、それを許している1行のコードを直し、攻撃がもう通らないことを示します。
守るルールは1つ
注意
このラボは、わざと弱く作られた Web サイトを動かします。攻撃してよいのはこのサイトだけです。あなた専用のコピーで、インターネットへの出口がないため、ほかの人に影響はありません。自分のものではないサイトで、これを試してはいけません。英国のコンピューター不正使用法(Computer Misuse Act 1990)では犯罪にあたります。
始める前に
メモ
このレッスンの上のバーにある ラボを起動 を押し、動いていると表示されるまで待ってください。お店は app というサービスで、ターミナルからはその名前で届きます。下の 実行 ボタンは、ラボのターミナルにコマンドを同じセッションで順番に入力します。実行の様子を見たり、自分でコマンドを打ったりするときは、バーの ターミナルを開く(新しいタブ) を押します。
1. お店を検索する
お店の検索は Web アドレスです。curl -s で呼び出します。-G --data-urlencode は、検索する言葉をアドレスの後ろに付け、引用符や空白を安全な形に直します。pen で検索すると、Pen 1.2 の1行が出ます。商品名と値段です。
curl -s -G --data-urlencode "q=pen" http://app:8000/searchここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
2. 質問から抜け出す
プログラムの中では、あなたの検索語は2つの引用符のあいだに貼り付けられます。検索語に引用符を入れると、その引用符で質問が早く閉じます。OR 1=1 はどの行でも成り立ちます。-- は、質問の残りをコメントにします。お店は7つの商品を表示します。その中には Staff discount card と Unreleased drone があり、売り物ではないので、表示されてはいけないものです。同じ手口で UNION SELECT を使うと、まったく別のテーブルを尋ねることもできます。お店には users テーブルがあるので、自分で試してみてください。
curl -s -G --data-urlencode "q=x' OR 1=1 --" http://app:8000/searchここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
3. 1行を直す
プログラムはコンテナの中にあり、弱い行は rows = で始まる行です。その行は + で、あなたの検索語を質問文につなげています。パラメータ化クエリ(parameterised query)は、質問文と検索語を別々にデータベースへ送ります。そのため、入力した文字は値にしかならず、質問の一部にはなりません。? が値の入る場所で、検索語はその横に渡します。手で直すには、docker exec -it app vi /tmp/app/app.py でプログラムを開き、その行を置き換えます(i で入力、Esc のあと :wq で保存)。次のコマンドは、下の行と同じ変更を代わりにしてくれます。
rows = db.execute("SELECT name, price FROM products WHERE public = 1 AND name LIKE ?", ("%" + term + "%",)).fetchall()
4. 変更して再起動する
この変更は、その1行だけを置き換えます。お店はプログラムを起動時に一度だけ読むので、docker restart で新しい版を動かします。約10秒かかり、そのあと app と表示されます。ボックスに「まだ実行中」と出ているときは、ターミナルに app と出るのを待ってから次に進んでください。
docker exec app sed -i 's|^ rows = .*| rows = db.execute("SELECT name, price FROM products WHERE public = 1 AND name LIKE ?", ("%" + term + "%",)).fetchall()|' /tmp/app/app.pyここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
docker restart appここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
5. もう一度攻撃する
手順2と同じ攻撃をすると、今度は何も表示されません。データベースは、名前に x' OR 1=1 -- という文字列がまるごと含まれる商品を探しましたが、ないからです。
curl -s -G --data-urlencode "q=x' OR 1=1 --" http://app:8000/searchここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
6. ふつうの使い方がまだできるか確かめる
お店が壊れる修正は、修正ではありません。lab-check は、ふつうの検索、Baker's(名前に引用符がある商品で、弱いプログラムではデータベースのエラーになりました)の検索、OR の攻撃、UNION の攻撃を試し、それぞれに PASS か FAIL を表示します。4つとも PASS で、最後の行が All checks pass. になります。ラボのバーで ラボを停止 を押し、続けて ラボを起動 を押してから、もう一度実行してください。修正は残っています。プログラムは名前付きの ボリューム(volume)にあり、プラットフォームがボリュームを保存するからです。
docker exec app sh /tmp/app/lab-checkここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
先生へ
ヒント
このレッスンは ラボで進めるレッスン の小さな例です。手順と 実行 ボタンがレッスンで、ラボは生徒一人ひとりのコピーです。ここには自動採点はありません。生徒は、攻撃の前と後の画面、最後の lab-check の結果、変更した行を提出し、それが安全な理由を自分の言葉で一文添えます。先生はその証拠を採点してください。自分のモジュールにコピーして変更するか、レッスンビルダーの ラボ メニューから一から作ってください。