SQLインジェクション

こんにちは!作者のActiveTKです。

皆さんはSQLインジェクションを知っていますか?

データベースを扱うとすぐに理解出来るのですが、データベースを使用した事が無いと理解が難しいです。

なので、今回はデータベースを使用した事のない方向けに書きます。「そんなの常識だよw」という方はブラウザバックして頂いて結構です。

1. データベースって何?

「フィールド」と「レコード」からなるエクセルのような物です。

ファイルと似ていますが、管理がしやすいという特徴があります。

以下、PHPでデータベースを利用するサンプルです。

<?php
$dbtest = new PDO("mysql:dbname={データベース名};host={ホスト名};charset=UTF8, "ユーザー名", "パスワード");
$res = $dbtest->query("select * from users");
$kekka = $res->fetchAll();
var_dump($kekka);

これを実行すると、usersというテーブルの中身を一覧表示出来ます。

queryの部分には「select * from users」と書かれています。

SQLサーバー上でこのSQL文が実行されたという事です。

select文では、データを表示する事が出来ます。

select {取得したい物} from {テーブル名} where {条件}

今回はusersというテーブルを使用するのでテーブル名にusersを当てはめます。

次に取得したい物と条件の部分です。

今回のように、テーブルの中身を一覧表示する事は滅多に無く、基本的に「ユーザー名(username)が○○のアカウントのパスワード(password)を取得」のような文を実行します。

それをSQL文にすると、

select password from users where username = '○○';

という文を実行する事になります。

2. SQLインジェクション

先ほどまでは、「決まった文」を実行していましたが、「入力されたユーザー名のパスワードを取得」という文を実行したい場合、

// $userに$_POST["username"]の値が入っている前提
select password from users where username = '{$user}';

という文を実行する事になります。

しかし、ユーザー名として「’ or 1 = 1 –」と入力されたらどうなるでしょうか。

(「–」で以降をコメントアウト)

select password from users where username = '' or 1 = 1 --'

という文が実行されます。

すると、1=1なので失敗値(false)は返ってきません。

「ユーザー名が合っていた」という処理が実行されてしまいます。

パスワードでも同じです。

しかし、問題はここからです。

不正にログインされても、被害は限定的ですが、

ほとんどのデータベースでは「;」を使用する事で複数のSQL文を実行する事が出来ます。

これを悪用して、「’ or 1 = 1; select * from users; –」と入力されたらどうなるでしょう。

select password from users where username = '' or 1 = 1;
select * from users;
--'

というSQL文が実行されて、テーブル内を一覧表示されてしまいます。(但し、結果をechoなどで表示していなければ見る事は不可です)

他にも、「’ or 1 = 1; update users set password = ‘1234’ where username = ‘ActiveTK’; –」と入力されたらどうなるでしょう。

update文を使用すると、値を上書きする事が出来ます。

select password from users where username = '' or 1 = 1;
update users set password = '1234' where username = 'ActiveTK';
-- '

という文がSQLサーバー上で実行されます。

このupdate文では、ActiveTKのパスワードを1234に設定しています。これにより、ユーザー名さえ分かっていればパスワードを書き換えて不正にログインする事が出来てしまいます。

3. 対策

とりあえず、ユーザーが入力した値を直接SQL文に加えないことが大切です。executeという関数を使うと、「?」を機械的に置き換える事が出来ます。

$dbh = new PDO("mysql:dbname={データベース名};host={ホスト名};charset=UTF8, "ユーザー名", "パスワード");
$stmt = $dbh->prepare('select password from users where username = ?');
$stmt->execute([$_POST['username']]); // 機械的に置き換える
$row = $stmt->fetch(PDO::FETCH_ASSOC);

もしくは、ホワイトリスト方式で英数字のみに制限するのも手です。

// 英数字判定関数(PHP)
function hankaku($text) {
    // 英数字のみ
    if (preg_match("/^[a-zA-Z0-9]+$/",$text)) return true;
    // それ以外あり
    else return false;
  }

最後まで目を通して頂き、ありがとうございました。

コメントする

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