お役立ち情報

【今更聞けない】SQLインジェクションとは?実例を使いながら解説します

みなさん、こんにちは。どんぶラッコです。

今日はSQLインジェクションというテーマについてみていきましょう。

この「SQLインジェクション」という言葉、みなさんも一度は耳にしたことがあるのではないでしょうか。

情報セキュリティについて勉強をしていると必ず出てくるワードです

グランくん
グランくん
なんとなくよくないイメージがあるなあ
どんぶラッコ
どんぶラッコ
その感覚は間違ってないよ!悪意ある攻撃手法の一つとして取り上げられることが多いね

インジェクションの動詞形でもあるインジェクトとは挿入する・突っ込む的な意味を持っています。IT用語としては、”データベースを作る”という意味合いも持ちます。

ではこのSQLインジェクションとはどういうことなのか、そしてどんな危険性を孕んでいるのか、一緒にみていきましょう。

SQLインジェクションとは?

SQLインジェクションを一言で言うと、SQL文を使ってデータベースのデータを吸い出そうとする行為です。

そもそも、Webアプリケーションをはじめとするアプリケーションの大半は、データベースを使ったデータのやりとりを行います。

データベースの情報を利用するときには、検索キーや入力パラメータをユーザから得て入力します。

例えば飛行機のアクセス便のデータベースがあったら、日付・便名・乗車人数などのパラメータを使ってデータベース内のデータを取得します。

さて、ここで問題になるのがこの入力フォームです。

めっちゃ素直なプログラマだった場合、この入力フォーム内の文字列をそのままリクエストとして送信するようにプログラムを書いてしまいます。

するとどんなことが起こってしまうでしょうか?

どんな文字列でも許容すると言うことは、データベースを操作するコマンドを入力すると、それも実行してしまうということが起こってしまうのです。

データベースを操作するための言語としてSQLがあります。

このSQL文をフォームにくっつけて送信することで、データベースの情報を抜き出すことができてしまうのです

グランくん
グランくん
ああ、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);

きちんとした対策を知っておくことが大事ですね!

参考:

https://qiita.com/kurodenwa/items/8807e79515c0e2b4dad9

ABOUT ME
どんぶラッコ
ECコンサルタント、システムエンジニアを経て、quintet株式会社CTOに就任。普段はNuxt.jsやLaravelを使用しています。

\面白いと思ったら/

記事のシェア & Twitter のフォロー をお願いします!

@proglearn
RELATED POST

COMMENT

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です