前回投稿した「Webセキュリティについて調べてみた」の続きになります。
具体的には前回記載した以下3つの脆弱性についての対策を考えていきます。
- SQLインジェクション攻撃
- OSコマンド・インジェクション攻撃
- XSS(クロスサイト・スクリプティング)攻撃
SQLインジェクションの脆弱性について
※前回のコードを抜粋
- send.php(ログインの処理)
//脆弱性のあるコード
$query = "SELECT * FROM users WHERE ID = '$userID' AND PWD = '$password'";
$result = $conn->query($query);
★攻撃手法
①piyo' --
②1' or 1 = 1;--
実行されるクエリ:
①SELECT * FROM users WHERE ID = 'piyo' -- ' AND PWD = ''
②SELECT * FROM users WHERE ID = '1' or 1 = 1;-- ' AND PWD = ''
★修正箇所
- プレースホルダの使用
変動する箇所とデータベースへの登録処理の間に挟み、SQL構文として解釈される特殊文字を自動的にエスケープして値(文字列リテラル)として安全に扱う仕組みです。
// SQLクエリを定義(プレースホルダを使用)
①$query = "SELECT * FROM users WHERE ID = ? AND PWD = ?";
// クエリの準備
$stmt = $conn->prepare($query);
// パラメータをバインド("ss" は文字列型)
②$stmt->bind_param("ss", $userID, $password);
// クエリ実行
$stmt->execute();
// 結果を取得
$result = $stmt->get_result();
①query = "SELECT * FROM users WHERE ID = ? AND PWD = ?"; // クエリの準備 $stmt = $conn->prepare($query);
プリペアドステートメントであらかじめSQL文を用意しておきます。
②$stmt->bind_param("ss", $userID, $password);
ここで値をバインドしています。
ssは文字列型の変数が2つバインドされるという意味です。
※プリペアドステートメントとは事前に用意したSQL文のことです。
実装方法には疑問符プレースホルダと名前付きプレースホルダの2つがあります。
疑問符プレースホルダ :
? を使って値を入れる位置を表す SELECT * FROM users WHERE id = ?
名前付きプレースホルダ:
:名前 を使って変数の名前を明示 SELECT * FROM users WHERE id = :id
クロスサイトスクリプティングの脆弱性について
※前回のコードを抜粋
//脆弱性のあるコード
// ユーザーが入力した検索キーワードを取得
$search = isset($_GET['search']) ? $_GET['search'] : '';
<?php if (!empty($search)): ?>
<p>検索結果: <strong><?= $search ?></strong></p>
<?php endif; ?>
★攻撃手法
<script>alert("XSS")</script>
実行されるクエリ:
<p>検索結果: <strong><script>alert('XSS!')</script></strong></p>
この攻撃を行うとWebサイトにユーザーが提供したデータがそのままページに反映されて実行されてしまいます。つまり、ブラウザがこれをHTMLだと解釈して、JavaScriptを実行してしまうのです。
★修正箇所
- htmlspecialchars関数を使用する。
<p>検索結果: <strong><?= htmlspecialchars($search, ENT_QUOTES, 'UTF-8') ?></strong></p>
このようにすることで不正なデータや悪意のあるコードを無害化することができます。
※JavaScriptにはサニタイジング用の関数が用意されていないので以下の形でエスケープを行います。
function escapeHTML(str) {
return str
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
OSコマンドインジェクションの脆弱性について
※前回のコードを抜粋
//脆弱性のあるコード
$input = $_POST['message'];
// コマンドを実行
$output = shell_exec("echo " . $input);
echo "<pre>" . "以下の内容で送信しました" . $output . "
★攻撃手法
メッセージに;ls
と入力し送信
※OSコマンドインジェクションはプログラムに与える入力値にOSへの命令文(コマンド)を渡すことで発生してしまいます。
★修正箇所
一番良い方法はOSコマンドによる処理を避けることですが、どうしても使用したい場合は、以下の対策を実装します。
- escapeshellarg関数の使用
$input = escapeshellarg($_POST['message']);
これを実施すると
このようにシェルのメタ文字をエスケープすることができました。
<参考資料>