PHPのお勉強!

PHP TOP

mysql_real_escape_string

(PHP 4 >= 4.3.0, PHP 5)

mysql_real_escape_stringSQL 文中で用いる文字列の特殊文字をエスケープする

警告

この拡張モジュールは PHP 5.5.0 で非推奨になり、PHP 7.0.0 で削除されました。 MySQLi あるいは PDO_MySQL を使うべきです。詳細な情報は MySQL: API の選択 を参照ください。 この関数の代替として、これらが使えます。

説明

mysql_real_escape_string(string $unescaped_string, resource $link_identifier = NULL): string

現在の接続の文字セットで unescaped_string の特殊文字をエスケープし、 mysql_query() で安全に利用できる形式に変換します。バイナリデータを挿入しようとしている場合、 必ずこの関数を利用しなければなりません。

mysql_real_escape_string() は、MySQL のライブラリ関数 mysql_real_escape_string をコールしています。 これは以下の文字について先頭にバックスラッシュを付加します。 \x00, \n, \r, \, ', " そして \x1a.

データの安全性を確保するため、MySQL へクエリを送信する場合には (わずかな例外を除いて)常にこの関数を用いなければなりません。

警告

セキュリティ: デフォルトの文字セット

サーバーレベルで設定するなり API 関数 mysql_set_charset() を使うなりして、 文字セットを明示しておく必要があります。この文字セットが mysql_real_escape_string() に影響を及ぼします。詳細は 文字セットの概念 を参照ください。

パラメータ

unescaped_string

エスケープされる文字列。

link_identifier

MySQL 接続。指定されない場合、mysql_connect() により直近にオープンされたリンクが指定されたと仮定されます。そのようなリンクがない場合、引数を指定せずに mysql_connect() がコールした時と同様にリンクを確立します。リンクが見付からない、または、確立できない場合、E_WARNING レベルのエラーが生成されます。

戻り値

成功した場合にエスケープ後の文字列、失敗した場合に false を返します。

エラー / 例外

MySQL 接続が存在しない状態でこの関数を実行すると、 E_WARNING レベルのエラーが発生します。 この関数は、MySQL 接続が確立した状態でのみ実行するようにしましょう。

例1 単純な mysql_real_escape_string() の例

<?php
// 接続
$link = mysql_connect('mysql_host', 'mysql_user', 'mysql_password')
OR die(
mysql_error());

// クエリ
$query = sprintf("SELECT * FROM users WHERE user='%s' AND password='%s'",
mysql_real_escape_string($user),
mysql_real_escape_string($password));
?>

例2 mysql_real_escape_string() には接続が必須であることを示す例

この例では、MySQL 接続が存在しない状態でこの関数を実行したときにどうなるかを示します。

<?php
// まだ MySQL に接続していません

$lastname = "O'Reilly";
$_lastname = mysql_real_escape_string($lastname);

$query = "SELECT * FROM actors WHERE last_name = '$_lastname'";

var_dump($_lastname);
var_dump($query);
?>

上の例の出力は、 たとえば以下のようになります。

Warning: mysql_real_escape_string(): No such file or directory in /this/test/script.php on line 5
Warning: mysql_real_escape_string(): A link to the server could not be established in /this/test/script.php on line 5

bool(false)
string(41) "SELECT * FROM actors WHERE last_name = ''"

例3 SQL インジェクション攻撃の例

<?php
// $_POST['password'] をチェックしなければ、このような例でユーザーに望みどおりの情報を取得されてしまう
$_POST['username'] = 'aidan';
$_POST['password'] = "' OR ''='";

// データベース上のユーザーに一致するかどうかを調べる
$query = "SELECT * FROM users WHERE user='{$_POST['username']}' AND password='{$_POST['password']}'";
mysql_query($query);

// MySQL に送信されたクエリは、
echo $query;
?>

MySQL に送信されたクエリは次のとおり:

SELECT * FROM users WHERE user='aidan' AND password='' OR ''=''

これでは、パスワードを知らなくても誰でもログインできてしまいます。

注意

注意:

mysql_real_escape_string() を利用する前に、MySQL 接続が確立されている必要があります。もし存在しなければ、 E_WARNING レベルのエラーが生成され、false が返されます。link_identifier が指定されなかった場合は、 直近の MySQL 接続が用いられます。

注意:

この関数を用いてデータをエスケープしなければ、クエリは SQL インジェクション攻撃 に対しての脆弱性を持ったものになります。

注意: mysql_real_escape_string()%_ をエスケープしません。 MySQL では、これらの文字を LIKE, GRANT, または REVOKE とともに用いることで、 ワイルドカードを表現します。

参考

add a note

User Contributed Notes 6 notes

up
183
feedr
14 years ago
Just a little function which mimics the original mysql_real_escape_string but which doesn't need an active mysql connection. Could be implemented as a static function in a database class. Hope it helps someone.

<?php
function mysql_escape_mimic($inp) {
if(
is_array($inp))
return
array_map(__METHOD__, $inp);

if(!empty(
$inp) && is_string($inp)) {
return
str_replace(array('\\', "\0", "\n", "\r", "'", '"', "\x1a"), array('\\\\', '\\0', '\\n', '\\r', "\\'", '\\"', '\\Z'), $inp);
}

return
$inp;
}
?>
up
31
nicolas
18 years ago
Note that mysql_real_escape_string doesn't prepend backslashes to \x00, \n, \r, and and \x1a as mentionned in the documentation, but actually replaces the character with a MySQL acceptable representation for queries (e.g. \n is replaced with the '\n' litteral). (\, ', and " are escaped as documented) This doesn't change how you should use this function, but I think it's good to know.
up
22
Walter Tross
12 years ago
For further information:
http://dev.mysql.com/doc/refman/5.5/en/mysql-real-escape-string.html
(replace your MySQL version in the URL)
up
8
sam at numbsafari dot com
12 years ago
No discussion of escaping is complete without telling everyone that you should basically never use external input to generate interpreted code. This goes for SQL statements, or anything you would call any sort of "eval" function on.

So, instead of using this terribly broken function, use parametric prepared statements instead.

Honestly, using user provided data to compose SQL statements should be considered professional negligence and you should be held accountable by your employer or client for not using parametric prepared statements.

What does that mean?

It means instead of building a SQL statement like this:

"INSERT INTO X (A) VALUES(".$_POST["a"].")"

You should use mysqli's prepare() function (http://php.net/manual/en/mysqli.prepare.php) to execute a statement that looks like this:

"INSERT INTO X (A) VALUES(?)"

NB: This doesn't mean you should never generate dynamic SQL statements. What it means is that you should never use user-provided data to generate those statements. Any user-provided data should be passed through as parameters to the statement after it has been prepared.

So, for example, if you are building up a little framework and want to do an insert to a table based on the request URI, it's in your best interest to not take the $_SERVER['REQUEST_URI'] value (or any part of it) and directly concatenate that with your query. Instead, you should parse out the portion of the $_SERVER['REQUEST_URI'] value that you want, and map that through some kind of function or associative array to a non-user provided value. If the mapping produces no value, you know that something is wrong with the user provided data.

Failing to follow this has been the cause of a number of SQL-injection problems in the Ruby On Rails framework, even though it uses parametric prepared statements. This is how GitHub was hacked at one point. So, no language is immune to this problem. That's why this is a general best practice and not something specific to PHP and why you should REALLY adopt it.

Also, you should still do some kind of validation of the data provided by users, even when using parametric prepared statements. This is because that user-provided data will often become part of some generated HTML, and you want to ensure that the user provided data isn't going to cause security problems in the browser.
up
-2
rohankumar dot 1524 at gmail dot com
3 years ago
There is requirement for old projects which are using `mysql_escape_string`, and upgrading the PHP version to 7 and above. Basically this happens in maintenance projects where we don't know how many files the functions are used in application. We can use [mysqli.real-escape-string][1] for the function:

If you have a typical connection file like `conn.php`

$conn = new mysqli($host, $user, $password, $db);
// may be few more lines to handle the $conn
if (!function_exists('mysql_escape_string')) {
function mysql_escape_string($sting){ // if mysql_escape_string not available
return $conn->real_escape_string($string); // escape using the $conn instance
}
}

[1]: https://www.php.net/manual/en/mysqli.real-escape-string.php
up
1
strata_ranger at hotmail dot com
15 years ago
There's an interesting quirk in the example #2 about SQL injection: AND takes priority over OR, so the injected query actually executes as WHERE (user='aidan' AND password='') OR ''='', so instead of returning a database record corresponding to an arbitrary username (in this case 'aidan'), it would actually return ALL database records. In no particular order. So an attacker might be able to log in as any account, but not necessarily with any control over which account it is.

Of course a potential attacker could simply modify their parameters to target specific users of interest:

<?php

// E.g. attacker's values
$_POST['username'] = '';
$_POST['password'] = "' OR user = 'administrator' AND '' = '";

// Malformed query
$query = "SELECT * FROM users WHERE user='$_POST[username]' AND password='$_POST[password]'";

echo
$query;

// The query sent to MySQL would read:
// SELECT * FROM users WHERE user='' AND password='' OR user='administrator' AND ''='';
// which would allow anyone to gain access to the account named 'administrator'

?>