最終更新日:
公開日:
レシピ
データベース MySQL / MariaDB
mysqliで文字列をエスケープする
データベースに渡すパラメータは不正なSQLの実行を防ぐために「エスケープ」と呼ばれる事前処理を行います。今回はmysqliに用意された関数を使ってエスケープする方法を解説します。
この記事のポイント
- 不正なSQLを実行しないためにデータは必ずエスケープ処理をする必要がある
- エスケープ処理はmysqliクラスのreal_escape_stringメソッド、もしくはmysqli_real_escape_string関数を使って実装する
目次
なぜエスケープが必要なのか
そもそも、どうしてパラメータなどの文字列をどうしてエスケープする必要があるのでしょうか。
理由を簡単にいうと、システム開発(プログラミング)においては大原則として、入力されたデータは信用しないスタンスが必要だからです。
確かに、Webサイト利用者のほとんどは良い人かもしれません。
しかし、その中に悪意を持った人がいつ現れてもおかしくありません。
インターネットは非常に便利で、遠隔地からでも簡単に色々な場所へアクセスできてしまうため、日本語のサイトでも世界のどこから狙ってくるかは分かりません。
実際にサイトを運用した経験があれば、お問い合わせフォームやコメント機能に英語や中国語などの意味不明なデータが入力されたり、メールを受け取った経験があるのではないでしょうか。
悪意のある人たちは人為的に攻撃対象を選んでいるわけではなく、隙のありそうなところを無作為に探しています。
以上の理由から、可能な限りセキュリティの対策を講じる必要があります。
その1つが、今回扱う文字列のエスケープとなるわけです。
入力データ = 文字列のエスケープ処理
お問い合わせフォームなどのページにおいて、不特定多数の誰かが入力したデータに100%悪意がないと言い切ることはできません。
そこで、全ての入力データに対して一律に「無害化」の処理を行います。
この処理を「サニタイズ(消毒)」と言います。
例えば、次のようなデータベースのテーブルと、SQLのSELECT文を実行するコードがあったとします。
id | name |
---|---|
1 | 野菜グラノーラ |
2 | 果物グラノーラ |
3 | 穀物グラノーラ |
4 | オーガニックグラノーラ |
5 | レーズングラノーラ |
6 | プレーンシリアル |
7 | チョコシリアル |
granolaテーブルからデータを取得するSQL
$sql = "SELECT * FROM gc_granola WHERE name LIKE '%".$_POST["name"]."%'";
$res = $mysqli->query($sql);
入力された値「$_POST["name"]」から、データベースを検索してヒットしたデータを返すコードです。
Webサイトで記事を検索する機能をよく見かけますが、同じようなものをイメージしてください。
もし「シリアル」と入力された場合は次の出力結果となります
では次に、「シリ'」と入力された場合の出力結果です。
1回目と2回目の違いは、検索文字列の中に「'(シングルクォーテーション)」が含まれているかどうかです。
特に2回目のSQL文、「WHERE句」は次のような解釈で実行されています。
最後の2文字が不明ですね。
もし、商品名に「'」が含まれていたものを検索したい場合でも、今のままでは検索することができません。
しかも、入力データに「"(ダブルクォーテーション)」が含まれていたら、最悪の場合は次のようなエラー画面が表示されてしまいます。
エラー画面はシステム情報が表示されてしまうこともあるため、本来は公開するべきではありません。
このような予期しない動作が起こらないよう、入力データはエスケープして安全な状態で扱いましょう。
mysqliにはエスケープするためのメソッド(または関数)が用意されています。
先ほどのコードであれば、次のようにエスケープ処理を組み込むことができます。
エスケープ処理のコード例
$name = $mysqli->real_escape_string($_POST["name"]);
$sql = "SELECT * FROM gc_granola WHERE name LIKE '%" . $_POST["name"] . "%'";
$res = $mysqli->query($sql);
このようにエスケープ処理をした結果、入力データに「'」や「"」が含まれていても問題なく検索を実行することができるようになります。
オブジェクト型の書き方
オブジェクト型では、次のようなコードでエスケープ処理を実装することができます。
オブジェクト型のコード例
$mysqli = new mysqli( 'host_name', 'user_name', 'password', 'database_name');
if( $mysqli->connect_errno ) {
echo $mysqli->connect_errno . ' : ' . $mysqli->connect_error;
}
$mysqli->set_charset('utf8');
$string = "Test's";
var_dump($string);
var_dump($mysqli->real_escape_string($string));
こちらのコードを実行すると下記の出力が得られます。
コードの最後2行では、var_dump関数でエスケープする前の文字列と、エスケープ後の文字列を出力しています。
出力結果を見てみると、エスケープ後の文字列では「'(シングルクォーテーション)」の前に「\(バックスラッシュ)」が置かれ、エスケープされている様子が分かります。
エスケープをするにあたって、文字コードが明示的に指定されていることは非常に重要です。
忘れずに指定するようにしましょう。
上記コードではset_charsetメソッドを使って指定しています。
手続き型の書き方
続いて、手続き型でのコードをみていきます。
手続き型のコード例
$db_link = mysqli_connect( 'host_name', 'user_name', 'password', 'database_name');
if( mysqli_connect_errno($db_link) ) {
echo mysqli_connect_errno($db_link) . ' : ' . mysqli_connect_error($db_link);
}
mysqli_set_charset( $db_link, 'utf8');
$string = "Test's";
var_dump($string);
var_dump( mysqli_real_escape_string( $db_link, $string) );
出力結果はオブジェクト型と同じです。
Note
「mysql_real_escape_string」から「mysqli_real_escape_string」へ移行する場合は、パラメータの違いに注意してください。
「mysqli_real_escape_string」では、1つ目のパラメータにmysqliクラスのインスタンスを渡す必要があります。