Webアプリケーション開発において、セキュリティ対策が不十分なPHPプログラムは重大なサイバー攻撃の標的となります。代表的な3つの脆弱性の仕組みと危険なコード、その対策を解説します。実際には、PHP だけに限ったことではありません。Web APPすべてに言えることです。
ユーザーの入力値をそのままSQL文に結合することで、開発者が意図しないSQL文が実行されてしまう脆弱性です。データベース内の情報漏洩や改ざん、消去につながります。
❌ 危険なコード例// URLパラメータからIDを取得
$id = $_GET['id'];
// ユーザー入力をそのままSQL文に結合している
$sql = "SELECT * FROM users WHERE id = " . $id;
$result = $conn->query($sql);
id に 1 OR 1=1 を指定した場合、条件式が常に真となり、全ユーザーの情報が漏洩する危険があります。
ユーザー入力をSQL文に直接結合せず、静的プレースホルダ(プリペアドステートメント)を使用します。
$stmt = $conn->prepare("SELECT * FROM users WHERE id = ?");
$stmt->bind_param("i", $id);
$stmt->execute();
ウェブサイトに悪意のあるスクリプト(JavaScriptなど)を埋め込まれ、それを閲覧した別のユーザーのブラウザ上で実行されてしまう脆弱性です。セッション情報の盗難などに悪用されます。
❌ 危険なコード例$name = $_GET['name'];
// 入力された値をそのまま画面に出力している
echo "こんにちは、" . $name . "さん!";
name に <script>alert('攻撃')</script> などのコードが渡された場合、ブラウザがJavaScriptとして実行してしまいます。
画面に出力する際は、HTMLとして意味を持つ記号を無害な文字列に変換するエスケープ処理(サニタイジング)を行います。
echo "こんにちは、" . htmlspecialchars($name, ENT_QUOTES, 'UTF-8') . "さん!";
Webサーバーが内部でOSコマンドを実行する際に、ユーザー入力をそのまま渡してしまうことで、サーバーを不正に操作される脆弱性です。
❌ 危険なコード例$ip = $_GET['ip'];
// サーバーのシステムコマンドを直接実行している
system("ping -c 4 " . $ip);
8.8.8.8; cat /etc/passwd と入力すると、pingの後にサーバー内の重要ファイルを読み出すコマンドが実行されてしまいます。
原則としてユーザー入力をOSコマンドの引数に渡す設計を避けるか、引数を安全にエスケープする関数を使用します。
$safe_ip = escapeshellarg($ip);
system("ping -c 4 " . $safe_ip);
後半では、より具体的な実装シーン(ファイルアップロードやログイン等)における脆弱性と、実際のサイバー攻撃を想定したケーススタディと具体的な回避コードを解説します。
ファイル形式のチェックが不十分な場合、悪意のあるプログラム(Webシェル)をアップロードされ、サーバーを乗っ取られる危険性があります。
❌ 危険なコード例$target_file = "uploads/" . $_FILES["avatar"]["name"];
// ファイルの拡張子を確認せず、そのまま保存している
move_uploaded_file($_FILES["avatar"]["tmp_name"], $target_file);
attack.php のようなプログラムがアップロードされ、そのファイルにURLから直接アクセスされると、サーバー上で不正なプログラムが実行されます。
md5(uniqid()) など)に変更して保存する。ファイル名やパスを指定するパラメータを悪用され、公開を意図していないサーバー内の重要なシステムファイルや設定ファイルを読み取られてしまう脆弱性です。
❌ 危険なコード例$file = $_GET['file'];
// 指定されたファイルをそのまま読み込んで表示する
include("pages/" . $file);
file の値に ../../config/database.php のような相対パスを入力されると、意図したフォルダを飛び越えて重要ファイルが読み込まれてしまいます。
ユーザー入力でファイル名を直接指定させない(IDによるマッピングなどを行う)か、basename() 関数を使ってファイル名部分のみを抽出します。
$file = basename($_GET['file']); // パス移動の文字列(/ や ..)が無効化される
include("pages/" . $file);
パスワードの検証方法が不適切だったり、何度もログインを試せる状態(総当たり攻撃が可能)になっていると、アカウントを簡単に乗っ取られてしまいます。
❌ 危険なコード例$username = $_POST['username'];
$password = $_POST['password'];
// データベースのパスワードがハッシュ化されずに生のまま比較されている
$sql = "SELECT * FROM users WHERE username = '$username' AND password = '$password'";
パスワードは必ず安全なハッシュ関数(password_hash / password_verify)を使用し、連続失敗時にはアカウントロックや画像認証を導入します。
$stmt = $conn->prepare("SELECT password_hash FROM users WHERE username = ?");
$stmt->bind_param("s", $username);
$stmt->execute();
$result = $stmt->get_result()->fetch_assoc();
if ($result && password_verify($password, $result['password_hash'])) {
// ログイン成功
}
入力欄の改行コードを悪用され、攻撃者が任意の宛先に大量のスパムメールを送信する(踏み台にされる)脆弱性です。
❌ 危険なコード例$email = $_POST['email'];
$message = $_POST['message'];
// ユーザーの入力をそのままメールのヘッダ(第4引数)に渡している
$headers = "From: " . $email;
mb_send_mail("admin@example.com", "お問い合わせ", $message, $headers);
email の中に test@example.com\nBcc: spam@victim.com のように改行とBccを混ぜて送信されると、意図しない宛先にメールが転送されてしまいます。
メールアドレスや件名の入力欄からは、改行コード(\r や \n)を完全に除去またはエラーにします。また、PHPMailer などの安全な外部ライブラリを使用するのが有効です。
入力欄の改行コードを悪用され、攻撃者が任意の宛先に大量のスパムメールを送信する(踏み台にされる)脆弱性です。
❌ 危険なコード例$headers = "From: " . $email;
mb_send_mail("admin@example.com", "お問い合わせ", $message, $headers);
email の中に \nBcc: spam@victim.com のように改行とBccを混ぜて送信されると、意図しない宛先にメールが転送されてしまいます。
入力欄から改行コードを完全に除去するか、PHPMailer などの信頼できるライブラリを使用します。
どれだけ自分で書くコードを安全に記述していても、PHPエンジン自体のバグに対する修正パッチを当てず、古いバージョンを使い続けていると、自動化された攻撃の標的になります。
【危険性】 自身のプログラムに問題がなくても、特定のWebリクエストをサーバーに送るだけでPHP自体のバグ(マルチバイト処理やiconvなどの脆弱性)を突かれ、攻撃者のプログラムをサーバー上で直接実行されてしまいます。これにより、サーバー全体の権限が奪取されます。
【危険性】 PHP内部のメモリリークや無限ループのバグを利用されます。攻撃者が細工したデータを送信するだけで、サーバーのCPU使用率が100%に張り付いたり、PHPプロセスが異常終了(クラッシュ)してWebサイトが完全にダウンします。
【危険性】 PHPのコアシステムのバグを突かれ、サーバーのメモリ上に一時保存されている「他のユーザーのセッションデータ」や「内部変数の値」が丸ごと読み取られてしまうケースがあります。
JavaScript環境においてもPHPと全く同様です。自作コードに不備がなくても、実行エンジン(Node.jsなど)や、利用している外部ライブラリ(npmパッケージ等)のパッチ未適用により、深刻な被害を招きます。Web制作会社さんによくあるケースです。制作会社の大小の規模ではありません。検収段階で専門家による監査が必要です。なお、YIN に外部からAI診断などされますと、すべて攻撃とみなされ、1年間はアクセスできなくなります。
jQuery、React、Vue.jsなどの古いバージョンや外部プラグインを使い続けることで、以下の標的になります。
サーバーサイドでJavaScript(Node.js)を動かしている場合、Webフレームワーク(Express等)やnpmパッケージの脆弱性を放置すると、PHPと同等の壊滅的な被害が発生します。
npm audit や yarn audit コマンドで既知の脆弱性を自動検知したり、Dependabot などを導入してパッチ適用のプルリクエストを自動生成する対策が必須となっています。
【背景】 商品検索機能にユーザー入力を直接結合した危険なSQL文が使われていたため、UNION攻撃により管理者の平文パスワードが画面に露出。数万件の個人情報が流出した。
$stmt = $pdo->prepare("SELECT * FROM products WHERE name LIKE :keyword");
$search_keyword = '%' . $keyword . '%';
$stmt->bindValue(':keyword', $search_keyword, PDO::PARAM_STR);
$stmt->execute();
【背景】 ファイル名のサニタイズ(basename処理)を怠ったため、Webシェル(attack.php)をWeb公開ディレクトリに直接書き込まれ、サーバーの全権限を奪取された。
$original_name = basename($_FILES["avatar"]["name"]);
$extension = pathinfo($original_name, PATHINFO_EXTENSION);
// 拡張子のチェックと、ファイル名のランダム文字列への完全書き換え
$secure_filename = bin2hex(random_bytes(16)) . '.' . $extension;
$target_file = "uploads/" . $secure_filename;
move_uploaded_file($_FILES["avatar"]["tmp_name"], $target_file);