2024 日本 PHP 研討會

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(字串 $unescaped_string, 資源 $link_identifier = NULL): 字串

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 等級的 PHP 錯誤。僅在存在有效的 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() 不會跳脫 %_。如果與 LIKEGRANTREVOKE 結合使用,這些字元在 MySQL 中是萬用字元。

另請參閱

新增註解

使用者貢獻的註解 6 則註解

183
feedr
14 年前
只是一個模仿原始 mysql_real_escape_string 的小函式,但它不需要有效的 mysql 連線。可以作為資料庫類別中的靜態函式來實作。希望它能幫助到某些人。

<?php
函式 mysql_escape_mimic($inp) {
如果(
is_array($inp))
返回
array_map(__METHOD__, $inp);

如果( !empty(
$inp) && is_string($inp)) {
返回
str_replace(陣列('\\', "\0", "\n", "\r", "'", '"', "\x1a"), 陣列('\\\\', '\\0', '\\n', '\\r', "\\'", '\\"', '\\Z'), $inp);
}

返回
$inp;
}
?>
31
nicolas
18 年前
請注意,mysql_real_escape_string 並沒有像文件中提到的那樣在 \x00、\n、\r 和 \x1a 前面加上反斜線,而是實際上將字元替換為 MySQL 查詢可接受的表示法(例如,\n 被替換為 '\n' 字面量)。(\, ' 和 " 則按照文件中說明進行跳脫)這並不會改變您使用這個函式的方式,但我認為了解這一點很重要。
22
Walter Tross
12 年前
更多資訊:
https://mysqldev.dev.org.tw/doc/refman/5.5/en/mysql-real-escape-string.html
(請在網址中替換您的 MySQL 版本)
8
sam at numbsafari dot com
12 年前
任何關於跳脫字元的討論,都應該提醒大家,基本上永遠不要使用外部輸入來產生直譯式程式碼。這適用於 SQL 陳述式,或任何您會呼叫任何種類「eval」函式的程式碼。

因此,不要使用這個問題重重的函式,請改用參數化預備陳述式。

坦白說,使用使用者提供的資料來組成 SQL 陳述式應該被視為專業上的疏失,您應該為沒有使用參數化預備陳述式而對您的雇主或客戶負責。

這是什麼意思?

這表示,不要像這樣建構 SQL 陳述式:

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

您應該使用 mysqli 的 prepare() 函式(https://php.dev.org.tw/manual/en/mysqli.prepare.php)來執行如下所示的陳述式:

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

注意:這並不表示您永遠不應該產生動態 SQL 陳述式。它的意思是,您永遠不應該使用使用者提供的資料來產生這些陳述式。任何使用者提供的資料都應該在陳述式準備好之後,作為參數傳遞給陳述式。

舉例來說,如果您正在構建一個小型框架,並希望根據請求 URI 將資料插入資料表,那麼不要直接將 $_SERVER['REQUEST_URI'] 值(或其任何部分)與您的查詢字串串接,這是非常重要的。相反地,您應該解析出 $_SERVER['REQUEST_URI'] 值中您需要的部分,並透過某種函數或關聯陣列將其映射到非使用者提供的數值。如果映射沒有產生任何數值,您就知道使用者提供的資料有問題。

未能遵循這一點已導致 Ruby On Rails 框架中許多 SQL 注入問題,即使它使用參數化預備語句也是如此。GitHub 曾因此遭到駭客入侵。因此,沒有任何語言可以免疫這個問題。這就是為什麼這是一個通用的最佳實務,而不是 PHP 特有的東西,以及為什麼您真的應該採用它。

此外,即使使用參數化預備語句,您仍然應該對使用者提供的資料進行某種驗證。這是因為使用者提供的資料通常會成為某些生成的 HTML 的一部分,而您需要確保使用者提供的資料不會在瀏覽器中造成安全問題。
-2
rohankumar dot 1524 at gmail dot com
3 年前
對於使用 `mysql_escape_string` 的舊專案,並將 PHP 版本升級到 7 或更高版本,有一個需求。基本上,這種情況發生在維護專案中,我們不知道函數在應用程式中使用了多少個檔案。我們可以使用 [mysqli.real-escape-string][1] 來代替該函數

如果您有一個典型的連線檔案,例如 `conn.php`

$conn = new mysqli($host, $user, $password, $db);
// 可能還有幾行用於處理 $conn
if (!function_exists('mysql_escape_string')) {
function mysql_escape_string($string){ // 如果 mysql_escape_string 不可用
return $conn->real_escape_string($string); // 使用 $conn 實例進行跳脫
}
}

[1]: https://php.dev.org.tw/manual/en/mysqli.real-escape-string.php
1
strata_ranger at hotmail dot com
14 年前
關於 SQL 注入的範例 #2 中有一個有趣的怪癖:AND 的優先順序高於 OR,因此注入的查詢實際上會執行為 WHERE (user='aidan' AND password='') OR ''='',因此它不會返回與任意使用者名稱(在本例中為 'aidan')相對應的資料庫記錄,而是實際上會返回所有資料庫記錄。沒有特定的順序。因此,攻擊者可能可以登入任何帳戶,但不一定能控制是哪個帳戶。

當然,潛在的攻擊者可以簡單地修改他們的參數來鎖定特定的目標使用者

<?php

// 例如,攻擊者的值
$_POST['username'] = '';
$_POST['password'] = "' OR user = 'administrator' AND '' = '";

// 格式錯誤的查詢
$query = "SELECT * FROM users WHERE user='$_POST[username]' AND password='$_POST[password]'";

echo
$query;

// 傳送到 MySQL 的查詢如下:
// SELECT * FROM users WHERE user='' AND password='' OR user='administrator' AND ''='';
// 這將允許任何人存取名為 'administrator' 的帳戶

?>
To Top