略微加速

PHP官方手册 - 互联网笔记

PHP - Manual: mysql_real_escape_string

2024-11-23

mysql_real_escape_string

(PHP 4 >= 4.3.0, PHP 5)

mysql_real_escape_string 转义 SQL 语句中使用的字符串中的特殊字符,并考虑到连接的当前字符集

警告

本扩展自 PHP 5.5.0 起已废弃,并在自 PHP 7.0.0 开始被移除。应使用 MySQLiPDO_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传送查询前,必须调用这个函数(除了少数例外情况)。

警告

安全提示: 默认字符集

The character set must be set either at the server level, or with the API function mysql_set_charset() for it to affect mysql_real_escape_string(). See the concepts section on character sets for more information.

参数

unescaped_string

The string that is to be escaped.

link_identifier

MySQL 连接。如不指定连接标识,则使用由 mysql_connect() 最近打开的连接。如果没有找到该连接,会尝试不带参数调用 mysql_connect() 来创建。如没有找到连接或无法建立连接,则会生成 E_WARNING 级别的错误。

返回值

Returns the escaped string, or false on error.

错误/异常

Executing this function without a MySQL connection present will also emit E_WARNING level PHP errors. Only execute this function with a valid MySQL connection present.

范例

示例 #1 mysql_real_escape_string() 例子

<?php
// Connect
$link mysql_connect('mysql_host''mysql_user''mysql_password')
    OR die(
mysql_error());

// Query
$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() requires a connection example

This example demonstrates what happens if a MySQL connection is not present when calling this function.

<?php
// We have not connected to 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 An example SQL Injection Attack

<?php
// We didn't check $_POST['password'], it could be anything the user wanted! For example:
$_POST['username'] = 'aidan';
$_POST['password'] = "' OR ''='";

// Query database to check if there are any matching users
$query "SELECT * FROM users WHERE user='{$_POST['username']}' AND password='{$_POST['password']}'";
mysql_query($query);

// This means the query sent to MySQL would be:
echo $query;
?>

The query sent to MySQL:

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

This would allow anyone to log in without a valid password.

注释

注意:

A MySQL connection is required before using mysql_real_escape_string() otherwise an error of level E_WARNING is generated, and false is returned. If link_identifier isn't defined, the last MySQL connection is used.

注意:

If magic_quotes_gpc is enabled, first apply stripslashes() to the data. Using this function on data which has already been escaped will escape the data twice.

注意:

If this function is not used to escape data, the query is vulnerable to SQL Injection Attacks.

注意: mysql_real_escape_string() does not escape % and _. These are wildcards in MySQL if combined with LIKE, GRANT, or REVOKE.

参见

add a noteadd a note

User Contributed Notes 10 notes

up
171
feedr
11 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
23
Walter Tross
10 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
28
nicolas
16 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
9
sam at numbsafari dot com
9 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
0
rohankumar dot 1524 at gmail dot com
10 months 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
-4
strata_ranger at hotmail dot com
12 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'

?>
up
-14
plgs at ozemail dot com dot au
12 years ago
Don't forget that if you're using Mysqli (ie, the "improved" Mysql extension) then you need to use the corresponding mysqli function mysqli_real_escape_string().  The parameter order is also different.
up
-8
jonnie
5 years ago
To Quote Sam at Numb Safari

[ "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." ]

Sam is right........

However I do not think it is sensible to stop all sanitising and simply pass the task on to parametric prepared statements.

A particular developer working in a particular situation will always know more about valid input (specific to that context).

If you ask a user to pass in a value you have already given them and you know that all such values start AB****** and the string should be of length 7 or 11 but never any other length then you have the basis of a good pre-sanitiser - different allowable lengths of a string might indicate legacy data.

I would never want to simply pass the rubbish that a malicious user may have passed in through a form to the parametric prepared statements, I would always want to do my own sanity checks first and in some cases these may err on the side of caution and simply choose to abort the Database op completely.

That way my DB does not get clogged up with unsafe statements made safe - it simply does not get clogged up which is better.

Security in layers - sanitisation and validation should still be considered in every situation BEFORE using prepared statements.

In addition as far as I can read into the official doc
==============================================

"Escaping and SQL injection

Bound variables are sent to the server separately from the query and thus cannot interfere with it. The server uses these values directly at the point of execution, after the statement template is parsed. Bound parameters do not need to be escaped as they are never substituted into the query string directly"

That suggests to me that danger is avoided in the internals by alternative handling not by nullification.

This means that a large project with incomplete conversion to prepared statements, legacy code in different parts of an organisation or servers talking to one another could all pass on the bad news from an immune location or situation to one that is not immune.

As long as the sanitisation is competently performed without incurring additional risks then personally I would stick with certain layers of sanitisation and then call the prepared statements.
up
-7
Aljo
4 years ago
@feedr
I elaborated his note as following:
$string = "asda\0sd\x1aas\\\\\\\\dasd\'asdasd\na\'\'sdasdad";
$array1 = array('\\\\\\\\', '\0', '\n', '\r', "'", '"', '\x1a');
$array2 = array('\\\\\\\\\\\\\\\\', '\\\0', '\\\n', '\\\r', "\\\'", '\\\"', '\\\Z');
echo($string);
echo(PHP_EOL);
for( $i=0; $i<count($array1); $i++ ) {
    if ($i==0)
    $p = '/(?<!\\\\)'.$array1[$i].'(?!\\\\)/';
    else
    $p = '/(?<!\\\\)'.$array1[$i].'/';
    echo($i);
    echo($p);
    echo( $array2[$i]);
    $string = preg_replace($p, $array2[$i], $string);
    echo("\t");
    echo($string);
    echo(PHP_EOL);
}
echo(PHP_EOL);
echo($string);
up
-27
presto dot dk at gmail dot com
12 years ago
If you want to make sure that the ID you're using to do a query is a number, use sprint() of (int) or intval(), but don't use mysql_real_escape_string.

There is no difference between ISO-8859-1's number 10 and UTF-8's number 10.

官方地址:https://www.php.net/manual/en/function.mysql-real-escape-string.php

北京半月雨文化科技有限公司.版权所有 京ICP备12026184号-3