SQL インジェクションのラボ:弱い検索を攻撃し、クエリを直して確かめる

このラボは、各生徒に、商品検索が SQL インジェクションに対して脆弱な小さなお店を渡します。生徒はラボのターミナルから curl で攻撃し、販売していない商品や、お店のユーザー名とパスワードを表示させます。その後、動いているコンテナーの中でコードを直してクエリをパラメーター化し、お店を再起動して、同じ攻撃では何も見つからず、通常の検索は変わらず動くことを確かめます。

公開されている Python のイメージだけから私たちが設計したため、攻撃する他社のアプリはなく、わざと間違えて書いた私たちの短いプログラムがあるだけです。守るルールは、攻撃用ラボの基本どおり、自分のコピーだけを攻撃することです。コピーはインターネットにも他の生徒にも通じておらず、自分のものではないサイトにツールを向けてはいけません。

生徒が学ぶこと

SQL インジェクションが起きる仕組み:プログラムが訪問者の入力をそのままデータベースへの問い合わせの文章に貼り付けるため、引用符で問い合わせが途中で終わり、続きを訪問者が書けてしまいます。生徒は、常に真になる OR で問い合わせの外に出て、UNION で別のテーブルを読み、原因の1行を見つけ、問い合わせと値を別々に送るパラメーター化クエリに置き換えます。値が問い合わせの一部になることはなくなります。さらに、修正が通常の使い方を壊していないかも確かめます。名前に引用符を含む商品もその一部です。安全なコーディングとWeb アプリケーションのセキュリティの単元、およびその法的な面である許可と範囲に向いています。正確な表現は、お使いの認定機関の単元でご確認ください。

ブラウザーで動かす

お店は app という名前のサービスで、ラボのターミナルからはその名前で届きます。ターミナルの docker コマンドは、ラボのサービスを見る、中に入る、再起動する、ログを読むことができます。作成、取得、削除はできません。プログラムは名前付きボリュームにあるので、生徒が保存した内容は、ラボを停止してもう一度起動したあとも残ります。

  1. コンテナーのページを開く. バーの「つくる」を開き、「コンテナーを実行」を選びます。ページは誰でも開けて、Git ラボと SQL ラボはアカウントなしで起動できます。ほかのラボには無料アカウントが必要です。アカウントを作成してサインインし、「起動」を押します。
  2. ラボを探す. 「サンプルラボ」のカードにすべてのサンプルが並んでいます。SQL インジェクション:見つけて直すを探します。
  3. 「起動」を押す. 「起動」はそのまま1回の操作で実行します。「先にファイルを編集」は compose ファイルを開くので、起動する前に変更できます。
  4. 開く. ラボのページで、ラボの横のターミナルを開きます。curl -s -G --data-urlencode "q=pen" http://app:8000/search でお店を検索し、x' OR 1=1 -- の検索で表示させすぎます。docker exec -it app vi /tmp/app/app.py でプログラムを編集し、docker restart app で新しい版を動かし、docker exec app sh /tmp/app/lab-check で4つの項目の PASS か FAIL を見ます。ラボのリンク Corner Shop search page は、検索フォームを新しいタブで開きます。

何を提出させるか

修正前と修正後の攻撃の出力、そして最後の docker exec app sh /tmp/app/lab-check の出力を提出させます。変更が生徒自身のターミナルに見える形になります。続いて、変更した1行と、新しい行が注入できない理由、古い行で攻撃者に何ができたか、ほかに何が役立つか(アプリが接続するアカウント、平文で保存しないパスワードなど)を、自分の言葉で書かせます。チェックはお店が正しく動くかを示し、理解したかどうかを示すのは説明だけです。

チェックはラボ自身のものです。lab-check は動いているお店に自分のアドレスで問い合わせるので、ファイルに何と書いてあるかではなくお店が実際に何をするかを示し、4行すべてが PASS のときだけ成功で終了します。自動では採点されません。このようなラボは16歳や17歳の生徒に使われることがよくあります。コピーは生徒だけのもので、そのモジュールを教える職員は開くことができ、生徒の提出物を開いた記録は、生徒も読めるアクセスログに残ります。

このラボの基本レッスン

SQL Injection Fix Basics は、このラボで進める小さなレッスンで、各手順に説明と、生徒のターミナルにコマンドを入力する「実行」ボタンが付いています。唯一のルールから始め、お店を検索し、すべての商品を表示させ、1つのコマンドで該当の行を直し、お店を再起動してもう一度攻撃し、チェックを実行します。コードを読むことと、修正を手で入力することは生徒に任せます。

無料の個人アカウントには、ホームにある Basics モジュールで配布されます。どの学校もライブラリの Basics フォルダから自分のモジュールにコピーでき、新しい学校ではサンプルモジュールの Lab Basics (Sample) に入っています。意図的に小さくしてあり、ラボで進めるレッスンとは何かを教員に見せるためのもので、残りはラボが担います。変更したい場合は、自分のモジュールにコピーしてください。

GitHub のコード

compose ファイルは、Python の公式イメージをバージョンで固定し、唯一のサービスとして実行し、起動時にお店のプログラムを書き出します。Python は Python Software Foundation ライセンスで、当社が配布するものではありません。リポジトリのテストは、プラットフォームが適用するのと同じ制限のもとで、生徒として攻撃、修正、停止と起動、最初からやり直しをたどります。

課題として出す

何を攻撃し、どの行を直し、何を提出するかを、課題に書いておきます。

  1. ラボを作る. モジュールで課題を追加し、「コンテナーラボ」のカードを選んでから「GitHub リポジトリ」を選びます。https://github.com/mitsurutech/lab-sqli-fix とブランチ main を指定します。compose ファイルは既定の compose.yaml です。変更したい場合は、先にリポジトリをフォークしてください。
  2. 公開して試す. 「公開」すると、確認したコミットに固定されるので、あとでリポジトリを変更しても、すでに出したラボは変わりません。まず自分のコピーを起動してください。
  3. 配布する. ほかの課題と同じように配布します。期限を付けることもでき、課題には何をするか、何を提出するかを書いておきます。
  4. 提出物を採点する. 生徒のコピーを開いて提出された内容を確認し、採点します。足りない場合は理由を書いて差し戻します。

よくある質問

生徒がプログラムに間違いを保存したらどうなりますか?
ラボは起動します。起動時にコンパイルできないプログラムは、app.py.broken としてその横に残され、弱いプログラムが書き直されます。わざと弱いプログラムからやり直したいときは、docker exec app rm /tmp/app/app.py を実行してから docker restart app を実行します。
データベースのアカウントや権限についての部分はありますか?
いいえ。お店は SQLite を使っていて、権限を与えるアカウントがなく、ラボにはサーバー型データベースのクライアントをインストールするためのインターネットもありません。最小権限は、記述式の説明で尋ねるのによい題材です。
生徒は何かをインストールしたり、インターネットに接続したりする必要がありますか?
いいえ。Python と curl はラボのイメージに入っていて、ターミナルはブラウザーの中にあり、ラボのコピーにはインターネットへの出口がありません。