みなさん、こんにちは。どんぶラッコです。
今日はSQLインジェクションというテーマについてみていきましょう。
この「SQLインジェクション」という言葉、みなさんも一度は耳にしたことがあるのではないでしょうか。
情報セキュリティについて勉強をしていると必ず出てくるワードです
インジェクションの動詞形でもあるインジェクトとは挿入する・突っ込む的な意味を持っています。IT用語としては、”データベースを作る”という意味合いも持ちます。
ではこのSQLインジェクションとはどういうことなのか、そしてどんな危険性を孕んでいるのか、一緒にみていきましょう。
SQLインジェクションとは?
SQLインジェクションを一言で言うと、SQL文を使ってデータベースのデータを吸い出そうとする行為です。
そもそも、Webアプリケーションをはじめとするアプリケーションの大半は、データベースを使ったデータのやりとりを行います。
データベースの情報を利用するときには、検索キーや入力パラメータをユーザから得て入力します。
例えば飛行機のアクセス便のデータベースがあったら、日付・便名・乗車人数などのパラメータを使ってデータベース内のデータを取得します。
さて、ここで問題になるのがこの入力フォームです。
めっちゃ素直なプログラマだった場合、この入力フォーム内の文字列をそのままリクエストとして送信するようにプログラムを書いてしまいます。
するとどんなことが起こってしまうでしょうか?
どんな文字列でも許容すると言うことは、データベースを操作するコマンドを入力すると、それも実行してしまうということが起こってしまうのです。
データベースを操作するための言語としてSQLがあります。
このSQL文をフォームにくっつけて送信することで、データベースの情報を抜き出すことができてしまうのです
実際の攻撃をみてみよう
では、実際の攻撃方法をみてみましょう。
例えば、乗客名簿を格納する passengers
というデータベースがあったとします。
そして、IDを入力するとそれに対応する乗客の情報を返してくれるシステムがあったとします。
コードはこんな感じです。
<?php
if (!empty($_POST["id"])) {
$id = $_POST["id"];
try{
$pdo = new PDO('sqlite:./database.sqlite');
$stmt = $pdo->prepare('SELECT * FROM passengers WHERE id = '.$id.';');
$stmt->execute();
}catch(PDOException $e){
echo $e;
}
$passengers = $stmt->fetchAll(PDO::FETCH_ASSOC);
$view = '<ul>';
foreach ($passengers as $passenger) {
$view .= '<li>ID:'.$passenger['id'].' '.$passenger['name'].' ('.$passenger['age'].'歳)</li>';
};
$view .= '</ul>';
}else{
$view = '';
}
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="form">
<form action="./index.php" method="post">
<input type="text" name="id" placeholder="ID番号を入力">
<input type="submit" value="送信">
</form>
</div>
<div class="result">
<?php echo $view; ?>
</div>
</body>
</html>
初めての方はなんのこっちゃ?と混乱すると思いますが、ポイントはここです。
$stmt = $pdo->prepare('SELECT * FROM passengers WHERE id = '.$id.';');
この 'SELECT * FROM passengers WHERE id = '.$id.';'
という部分がSQL文です。
日本語に直すと、
idが $id に一致するデータを passengers テーブルから引っ張り出してください!
ということです。
そして、この$idの部分が、入力フォームの値を受け取っている場所ですね。
$id = $_POST["id"];
では、このフォームに 0 OR TRUE;
と挿入してみましょう。
これは何が起こったのでしょうか?
先ほどの SQL文と今回挿入した文章を組み合わせてみましょう。
SELECT * FROM passengers WHERE id = 0 OR TRUE;
WHEREは条件式です。一番最初の例では id=2
に一致するデータを探してくれていました。そして、合致したデータがあったら「あったよー(正しいよー)」ということでTRUE
を、そうではないデータについてはFALSE
を返します。WHERE句は最終的に真偽値(TRUE か FALSE)を返し、それを基に合致したデータを検索できるのです。
しかし今回は OR TRUE
としたことで、検索の条件が 常にTRUEになってしまいました。つまり、どのデータも検索条件に合致してしまう状態になりました。
なので、全てのデータが表示できてしまいました
今回はPHPのPDOを使っている場合は、 bindValue()
の機能を使うとこの問題は回避することができます。
// $stmt = $pdo->prepare('SELECT * FROM passengers WHERE id = '.$id.';');
$stmt = $pdo->prepare('SELECT * FROM passengers WHERE id = :id;');
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
きちんとした対策を知っておくことが大事ですね!
参考: