PHP の脆弱性を自分の PC で体験する|SQL インジェクションと XSS のハンズオン環境の作り方(新人研修向け)

PHP の脆弱性を自分の PC で体験するハンズオン

この記事でわかること

  • 自分の PC の中だけで、PHP の脆弱性を体験できる練習用の環境の作り方(Windows・管理者の権限なし)
  • SQL インジェクションと XSS が「どう起きて、どう直すか」を、危ない例と直した例を並べて体験する手順
  • 新人研修で使うときの進め方と、AI(Claude Code)にコードを見てもらう方法

対象:Web の開発をはじめたばかりの新人エンジニアと、研修を担当する人/Windows 11・PHP 8.4.27 で確かめた手順/所要時間:準備15分+体験30分

先に答えを書きます。PHP 公式の Windows 用 zip を展開し、設定ファイルで SQLite(ファイル1つで動くデータベース)を有効にすれば、インストールも管理者の権限もなしに練習用の環境が作れます。本記事で配っている練習用ファイル(わざと危なく作った例と、直した例)を置いて、PHP に入っている小さな Web サーバーを自分の PC からしか見えない設定で起動すれば準備完了です。入力欄に1文字入れるだけで、危ない例が壊れる様子と、直した例では何も起きない様子を見比べられます。

目次

はじめる前に、必ず守ること

  • 自分の PC の中だけで動かす:Web サーバーは 127.0.0.1(自分の PC だけを指す住所)で起動し、ほかの PC から見えるようにしない
  • ほかの人のサイトやシステムでは絶対に試さない:許可なく他人のシステムで試すと、不正アクセス禁止法などの法律に触れることがあります
  • 練習用のファイルを本番や社内のサーバーに置かない:わざと危なく作ってあります
  • 会社の PC で試すときは、先に上司や情報システムの担当に確認する
  • 終わったら片付ける:フォルダを消せば元に戻ります

このハンズオンで体験すること

脆弱性何が起きるか直し方
SQL インジェクション入力した文字が、データベースへの命令(SQL)の一部として扱われてしまう。見えてはいけないデータを読まれたり、消されたりするプレースホルダを使い、入力は「値」としてだけ渡す
XSS(クロスサイトスクリプティング)入力した文字が、ページの HTML の一部として扱われてしまう。見ている人のブラウザで、他人のプログラムを動かされるHTML に出す前に htmlspecialchars で「ただの文字」に変える

どちらも原因は同じで、「入力された文字を、そのまま命令や HTML の一部として使っている」ことです。Web アプリの脆弱性の中でも代表的なもので、情報処理推進機構(IPA)の「安全なウェブサイトの作り方」でも、最初のほうに取り上げられています。

準備1:PHP を用意する(インストール不要)

  1. PHP 公式の Windows 版のページ(windows.php.net/download)を開き、PHP 8.4 の「VS17 x64 Non Thread Safe」の Zip をダウンロードします(筆者が試したのは php-8.4.27-nts-Win32-vs17-x64.zip・約34MB)
  2. 同じページには、ファイルごとに SHA256 という「指紋」の値が載っています。PowerShell で次を実行し、出てきた値がページの値と同じなら、途中で壊れたり差し替えられたりしていない本物です
cd $HOME\Downloads
Get-FileHash .\php-8.4.27-nts-Win32-vs17-x64.zip -Algorithm SHA256

確かめたら、ユーザーフォルダの中の php フォルダに展開し、動くかを確かめます。

Expand-Archive .\php-8.4.27-nts-Win32-vs17-x64.zip -DestinationPath $HOME\php
& "$HOME\php\php.exe" -v
PowerShell で php -v を実行した結果。PHP 8.4.27 (cli) と表示されている
PowerShell の表示:PHP が動いた(実際に実行した結果を画像にしたもの。先頭のプロンプトは記事の手順のフォルダに合わせて表示し、ユーザー名は伏せています)

筆者の PC では、1行目に PHP 8.4.27 (cli) と出ました。「VCRUNTIME140.dll が見つからない」のようなエラーが出たときは、PHP を動かすのに必要な Microsoft の部品(Visual C++ 再頒布可能パッケージ)が入っていません。PHP 公式のページにも、この部品が必要だと書かれています。Microsoft の公式サイトから入れてください。

準備2:設定ファイルで SQLite を有効にする

展開したばかりの PHP では、データベースの SQLite を使う機能が有効になっていません。PHP に入っている開発用の設定の見本をコピーして、3か所を書き換えます。

Copy-Item $HOME\php\php.ini-development $HOME\php\php.ini
notepad $HOME\php\php.ini

メモ帳で開いたら、Ctrl+F で次の3つの行を探し、行の先頭の ;(この行を無効にする印)を消して保存します。

extension_dir = "ext"
extension=pdo_sqlite
extension=mbstring

extension_dir は同じ名前の行が2つあります。"ext" と書かれたほうです。保存したら、次のコマンドで有効になったかを確かめます。

& "$HOME\php\php.exe" -m | Select-String pdo_sqlite
PowerShell で php -m の結果から pdo_sqlite を探した結果。pdo_sqlite と表示されている
PowerShell の表示:SQLite が有効になった(実際に実行した結果を画像にしたもの。先頭のプロンプトは記事の手順のフォルダに合わせて表示し、ユーザー名は伏せています)

pdo_sqlite と表示されれば成功です。何も出ないときは、3行の ; が消えているか、ファイル名が php.ini になっているかを見直します。

準備3:練習用ファイルを置いて、起動する

練習用ファイル一式を zip にまとめました。中身は、データベースを作る setup.php、危ない例と直した例が2組、説明の README.txt です。データはすべて架空のものです。

ダウンロードしたら、展開してデータベースを作り、Web サーバーを起動します。

cd $HOME\Downloads
Expand-Archive .\fcn-php-security-lab-v2.zip -DestinationPath $HOME
cd $HOME\php-security-lab
& "$HOME\php\php.exe" setup.php
& "$HOME\php\php.exe" -S 127.0.0.1:8080
PowerShell で setup.php を実行し、Web サーバーを起動した結果。Development Server started と、ページを開いたときの記録が表示されている
PowerShell の表示:データベースを作って Web サーバーを起動したところ。下の3行は、ブラウザでページを開いたときに出る記録(実際に実行した結果を画像にしたもの。先頭のプロンプトは記事の手順のフォルダに合わせて表示し、ユーザー名は伏せています)

4行目で lab.db を作りました(3件) と出て、5行目のあとは PowerShell が止まったように見えます。これは Web サーバーが動いている状態です。この画面は閉じずに、ブラウザで次のページを開きます。

  • http://127.0.0.1:8080/search_bad.php(危ない検索)
  • http://127.0.0.1:8080/search_good.php(直した検索)
  • http://127.0.0.1:8080/hello_bad.php(危ない表示)
  • http://127.0.0.1:8080/hello_good.php(直した表示)

127.0.0.1 は「この PC 自身」を指す特別な住所です。ここで起動すると、同じネットワークのほかの PC からは見えません。PHP の公式マニュアルでも、この Web サーバーは開発用で、公開するサーバーには使わないように書かれています。

体験1:SQL インジェクション

まずはふつうに検索する

search_bad.php を開き、検索欄に「田中」と入れて検索します。田中さんの部署が表示されます。このページは学ぶために、実際に実行した SQL(データベースへの命令の文)も画面に出しています。

危ない検索のページで「田中」を検索した画面。実行した SQL と、田中さんの部署が表示されている
画面:危ない例でふつうに検索したところ

「’」を1文字だけ入れてみる

次に、検索欄に半角の「'」(シングルクォート)を1文字だけ入れて検索します。すると、エラーが出ました。

危ない検索のページで、シングルクォートを1文字入れて検索した画面。SQL のエラーが表示されている
画面:「’」を1文字入れると、SQL が壊れてエラーになった

「'」は、SQL の文の中で「ここからここまでが文字」という区切りの印です。入力した「'」がそのまま SQL の文に入ったので、区切りの数が合わなくなり、文として壊れました。入力が「値」ではなく、命令の文の一部として読まれている証拠です。

このハンズオンでは、壊れる様子を見るところで止めます。本当の攻撃では、この性質を使って命令の文を書き換え、本来は見えないデータまで読み出します。練習用のデータベースにも、表示されないはずの「社外秘のメモ」が入れてあります。こうしたデータが、入力欄ひとつから読まれてしまうのが SQL インジェクションです。

原因のコード

search_bad.php のコード。6行目で入力を SQL の文字列につなげ、15行目でそのまま実行している
コード:search_bad.php(危ない例)

6行目で、入力された名前を、SQL の文の文字列に「.」でそのままつなげています。15行目で、その文をそのまま実行しています。入力に何が入っていても、文の一部になってしまいます。

直した例:プレースホルダを使う

search_good.php のコード。6行目で prepare とプレースホルダを使い、13行目で値を別に渡している
コード:search_good.php(直した例)

6行目では、SQL の文の中で値が入る場所を ?(プレースホルダ)にして、先に文の形だけを準備しています(prepare)。13行目の execute で、入力を値として別に渡しています。こうすると、入力に「'」が入っていても、ただの文字として扱われます。search_good.php で同じように「'」を入れると、エラーにならず「見つかりませんでした」と出るだけです。

直した検索のページで、同じくシングルクォートを入れた画面。見つかりませんでしたと表示されている
画面:直した例では、同じ入力でも「見つかりませんでした」と出るだけ

体験2:XSS(クロスサイトスクリプティング)

HTML のタグを入れてみる

hello_bad.php を開き、入力欄に <b>太字</b> と入れて「表示」を押します。<b> は文字を太くする HTML のタグです。すると、入力したタグは表示されず、「太字」の文字が本当に太字になりました。

危ない表示のページで、b タグをつけた文字を入れた画面。太字で表示されている
画面:危ない例では、入力したタグが HTML として効いて太字になった

入力した文字が、ページの HTML の一部として読まれた証拠です。太字なら害はありませんが、本当の攻撃では、ここにプログラム(JavaScript)を入れます。その仕掛けを入れたリンクを開いた人のブラウザで、他人のプログラムが動き、ログインの情報を盗まれたり、にせの入力画面を出されたりします。

原因のコードと、直した例

hello_bad.php のコード。8行目で入力をそのまま HTML に出している
コード:hello_bad.php(危ない例)

8行目で、入力をそのまま HTML の中に出しています。直した例では、出す前に htmlspecialchars という関数を通しています。

hello_good.php のコード。8行目で htmlspecialchars を通してから出している
コード:hello_good.php(直した例)

htmlspecialchars は、< や >、" などの HTML で特別な意味を持つ記号を、画面に記号として表示するための書き方に置き換えます。hello_good.php で同じものを入れると、タグがそのまま文字として表示されました。

直した表示のページで、同じ入力をした画面。タグがそのまま文字として表示されている
画面:直した例では、タグがただの文字として表示された

やってみよう:Claude Code に危ないコードを見てもらう

自分で書いたコードに同じ問題がないかを確かめるのに、AI に見てもらう方法もあります。練習用のフォルダで Claude Code を起動し、次のように頼みました。

このフォルダの search_bad.php と hello_bad.php に、セキュリティ上の問題がないか確認してください。問題があれば、何が起きるか・どの行か・どう直すかを、新人エンジニアにもわかる言葉で教えてください。ファイルは変更しないでください。

返ってきた指摘をまとめると、次のとおりです(返事には攻撃の入力の例も書かれていましたが、記事では省いています)。

ファイルと行指摘直し方
search_bad.php の6行目(実行は15行目)SQL インジェクション(重大)prepare と ? を使う
search_bad.php の11行目・19行目実行した SQL やデータベースのエラーの詳細を画面に出している。攻撃のヒントになる本番では出さず、決まった文言だけを出して、詳細はログに残す
hello_bad.php の8行目XSS(重大)htmlspecialchars を通してから出す

どの行が問題か、なぜ危ないか、どう直すかまで、正しく答えてくれました。さらに、学ぶためにわざと画面に出していた「実行した SQL」や「エラーの詳細」も、本番では攻撃のヒントになると指摘しています。ただし、AI の指摘がいつも正しく、全部を見つけてくれるとは限りません。指摘を読んで「なぜそうなるか」を自分で説明できるかを確かめる、という使い方がおすすめです。Claude Code の始め方は「Claude Code の教科書」で解説しています。

新人研修で使うときの進め方(30分の例)

時間やること
5分守ることの説明(自分の PC だけ・他人のシステムでは試さない)
10分体験1・体験2で、危ない例が壊れる様子と、直した例を見比べる
10分課題:hello_bad.php を自分で直し、<b> が文字として出るかを確かめる
5分ふり返り:「入力をそのまま命令や HTML に使わない」を、自分の言葉で説明する

課題は、直した例の hello_good.php を見ずに取り組んでもらい、終わってから見比べると身につきやすくなります。setup.php を実行し直せば、データベースはいつでも最初の状態に戻ります。

終わったら片付ける

  1. Web サーバーを動かしている PowerShell で Ctrl+C を押して止める
  2. エクスプローラーで、ユーザーフォルダの php-security-lab フォルダを消す
  3. PHP をもう使わないなら、php フォルダも消す(インストールしていないので、これで元に戻ります)

会社でみんなの環境をそろえるなら

研修の参加者が多いときは、Docker(同じ環境を箱のようにまとめて配れる道具)で環境をそろえる方法もあります。ただし Docker Desktop を Windows に入れるには、管理者の権限や WSL の準備が要ることが多く、そこでつまずく新人も少なくありません。まずは本記事の zip の方法で始め、慣れてきたら Docker にする、という順番がおすすめです。

よくある質問

自分の PC で試すのは、法律的に問題ありませんか?

自分の PC で、自分が置いた練習用のファイルに対して試すぶんには、他人のシステムに入ることにはなりません。問題になるのは、許可なくほかの人のサイトやシステムで試すことです。会社の PC や社内のネットワークで試すときは、会社のルールにしたがい、先に担当者に確認してください。

ウイルス対策ソフトに止められることはありますか?

筆者の PC では止められませんでした。会社の PC では、セキュリティの設定によって、PHP の実行や Web サーバーの起動が止められることがあります。その場合は無理に回避せず、担当者に相談してください。

ほかの脆弱性も体験できますか?

同じ作り方で、ほかの脆弱性の練習用ファイルも作れます。まずは、もっとも基本的な2つ(SQL インジェクションと XSS)で「入力をそのまま使わない」という考え方を身につけるのがおすすめです。

参考資料

確かめた日と環境:2026年10月26日、Windows 11 Pro/PowerShell 5.1/PHP 8.4.27(公式 zip・SHA256 が公式の値と一致することを確認)で、本記事の練習用ファイルを実際に動かしました。画面の画像は、自分の PC の中だけで動かした練習用ページを撮ったものです。Claude Code の指摘は Claude Code 2.1.302(Max プラン)で claude -p を1回実行した結果をまとめたもので、同じ依頼でも答えは毎回少しずつ違うことがあります。

改訂履歴:2026年10月26日 公開(同日、準備の手順に PowerShell の表示の画像を3枚追加)

本記事は、脆弱性のしくみと対策を学ぶための教材です。ほかの人のシステムで試すことは目的としていません。記事の作り方は制作方針をご覧ください。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

インフラの運用・構築に10年以上携わり、AWS 認定8資格を持つ運営者です。いまは Claude Code などの AI ツールを日々の作業の自動化に使いながら、これから使う人のための教科書サイトを運営しています。

コメント

コメントする

目次