💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
## **magic_quotes_sybase** 作用:影响 addslashes() 函数。 PHP 5.3.0 起*废弃*并将自 PHP 5.4.0 起*移除* magic_quotes_sybase=0 时(默认),在启用magic_quotes_gpc或magic_quotes_runtime时addslashes 将对 ' " \ 进行 \ 转义操作; magic_quotes_sybase=1 时,在启用magic_quotes_gpc或magic_quotes_runtime时addslashes 将对` ' `转义成两个单引号`''`。 >[danger]注意,当magic_quotes_sybase=On时,它完全覆盖了magic_quotes_gpc。在这种情况下,即使启用了magic_quotes_gpc,也不会转义双引号、反斜杠或NUL ``` //http://www.test.com/?name=' echo $_GET['name'];//'' //将会覆盖magic_quotes_gpc的设置,注意'转义为'' \和"没有转义 //http://www.test.com/?name='@"@\ //http://www.test.com/?name=%27@%22@\ echo $_GET['name'];//''@"@\ ``` ## **magic_quotes_gpc** 作用:当php.ini中magic_quotes_gpc=on时,对php服务器端接收的 GET POST COOKIE 的值执行 addslashes() 操作即自动转换`'和\和"和NULL`(php5.4移除此选项) 作用范围是:WEB客户服务端。 作用时间:请求开始时,例如当脚本运行时。 `get_magic_quotes_gpc()`可以获取当前 magic_quotes_gpc 的配置选项设置(5.4移除后始终返回false) ``` //http://www.test.com/?name='@"@\ //http://www.test.com/?name=%27@%22@\ echo $_GET['name'];//\'@\"@\\ //注意不会转义QUERY_STRING php4是可以转义_SERVER的php5取消了,所以我们需要addslashes()转义 $_SERVER echo $_SERVER['QUERY_STRING'];//name=%27@%22@\ ``` ## **magic_quotes_runtime** 作用:对通过 fread()、file_get_contents() 返回的文本执行 addslashes() 操作,对执行sql查询的结果执行 addslashes() 操作。 自 PHP 5.3.0 起*废弃*并将自 PHP 5.4.0 起*移除* 作用范围:从文件中读取的数据或执行 exec() 的结果或是从SQL查询中得到的。 作用时间:每次当脚本访问运行状态中产生的数据。 `get_magic_quotes_runtime()`可以获取magic_quotes_runtime配置的值 `set_magic_quotes_runtime()`可以在脚本中设置magic_quotes_runtime配置的值 如果启用了`magic_quotes_runtime`,大多数返回任何形式外部数据的函数,包括数据库和文本段将会用反斜线转义引号。 如果启用了[magic\_quotes\_sybase](https://www.php.net/manual/zh/sybase.configuration.php#ini.magic-quotes-sybase),单引号会被单引号转义而不是反斜线 ``` 受 magic_quotes_runtime 影响的函数(不包括 PECL 里的函数): get_meta_tags() file_get_contents() file() fgets() fwrite() fread() fputcsv() stream_socket_recvfrom() exec() system() passthru() stream_get_contents() bzread() gzfile() gzgets() gzwrite() gzread() exif_read_data() dba_insert() dba_replace() dba_fetch() ibase_fetch_row() ibase_fetch_assoc() ibase_fetch_object() mssql_fetch_row() mssql_fetch_object() mssql_fetch_array() mssql_fetch_assoc() mysqli_fetch_row() mysqli_fetch_array() mysqli_fetch_assoc() mysqli_fetch_object() pg_fetch_row() pg_fetch_assoc() pg_fetch_array() pg_fetch_object() pg_fetch_all() pg_select() sybase_fetch_object() sybase_fetch_array() sybase_fetch_assoc() SplFileObject::fgets() SplFileObject::fgetcsv() SplFileObject::fwrite() ``` ``` // 如果启用了魔术引号 即magic_quotes_gpc=on时 echo $_POST['lastname'];             // O\'reilly echo addslashes($_POST['lastname']); // O\\\'reilly ``` 由于5.4移除了magic_quotes_gpc配置get_magic_quotes_gpc始终返回false ``` // 适用各个 PHP 版本的用法 if (get_magic_quotes_gpc()) { //删除由 addslashes() 函数添加的反斜杠 $lastname = stripslashes($_POST['lastname']); } else { $lastname = $_POST['lastname']; } ``` 如果使用 MySQL ``` if (get_magic_quotes_gpc()) { //删除由 addslashes() 函数添加的反斜杠 $lastname = stripslashes($_POST['lastname']); } else { $lastname = $_POST['lastname']; } $lastname = mysql_real_escape_string($lastname); echo $lastname; // O\'reilly $sql = "INSERT INTO lastnames (lastname) VALUES ('$lastname')"; ``` **数据库字符集设为GBK时,0xbf27本身不是一个有效的GBK字符,但经过  addslashes()  转换后变为0xbf5c27,前面的0xbf5c是个有效的GBK字符,所以0xbf5c27会被当作一个字符0xbf5c和一个单引号来处理,结果漏洞就触发了** mysql\_real\_escape\_string() 也存在相同的问题,只不过相比  addslashes() 它考虑到了用什么字符集来处理,因此可以用相应的字符集来处理字符 当mysql_real_escape_string检测到的编码方式跟client设置的编码方式(big5/bgk)不一致时,mysql_real_escape_string跟addslashes是没有区别的。比如: ``` [client] default-character-set=latin1 mysql_query("SET CHARACTER SET 'gbk'", $mysql_conn); 这种情况下mysql_real_escape_string 是基于 latin1工作的,是不安全的 [client] default-character-set=gbk mysql_query("SET CHARACTER SET 'gbk'", $mysql_conn); 这种情况下,mysql_real_escape_string 基于 gbk 工作,是正常的 ``` 实例: ``` echo "PHP version: ".PHP_VERSION."\n";//PHP version: 5.2.5 mysql_connect('servername','username','password'); mysql_select_db("test"); mysql_query("SET NAMES GBK"); //使用cahr处理转换后的0xbf5c27字符 chr(0xbf)为¿ chr(0x27)' //模拟$_POST数据 ¿' OR username =username # $_POST['username'] = chr(0xbf).chr(0x27).' OR username = username /*'; $_POST['username'] = chr(0xbf).chr(0x27).' OR username = username #'; $_POST['password'] = 'guess'; $username = addslashes($_POST['username']); $password = addslashes($_POST['password']); $sql = "SELECT * FROM users WHERE username = '$username' AND password = '$password'"; $result = mysql_query($sql) or trigger_error(mysql_error().$sql); var_dump(mysql_num_rows($result));//int(3) 有结果有注入风险 var_dump(mysql_client_encoding());//latin1 $username = mysql_real_escape_string($_POST['username']); $password = mysql_real_escape_string($_POST['password']); $sql = "SELECT * FROM users WHERE username = '$username' AND password = '$password'"; $result = mysql_query($sql) or trigger_error(mysql_error().$sql); var_dump(mysql_num_rows($result));//int(3) 有结果有注入风险 var_dump(mysql_client_encoding());//latin1 mysql_set_charset("GBK"); $username = mysql_real_escape_string($_POST['username']); $password = mysql_real_escape_string($_POST['password']); $sql = "SELECT * FROM users WHERE username = '$username' AND password = '$password'"; $result = mysql_query($sql) or trigger_error(mysql_error().$sql); var_dump(mysql_num_rows($result));//int(0) var_dump(mysql_client_encoding());//string(3) "gbk" 结果: PHP version: 5.2.5 int(3) string(6) "latin1" int(3) string(6) "latin1" int(0) string(3) "gbk" ``` 可以看出来不论是使用addslashes还是mysql_real_escape_string,我都可以利用编码的漏洞来实现输入任意密码就能登录服务器的注入攻击!!!!(攻击的原理我就不多说了,感兴趣的同学可以研究下字符编码中单字节和多字节的问题) # **完美解决方案** 由上可知 mysql_real_escape_string() 、 addslashes()、和str_replace(替换单引号)是不能解决sql注入问题的 编写代码是要特别小心获取变量,如:$\_GET $\_POST $\_COOKIE $\_SERVER 完美解决sql注入的方案就是使用拥有Prepared Statement机制的PDO和MYSQLi来代替mysql_query(注:mysql_query自 PHP 5.5.0 起已废弃,并在将来会被移除): ``` //PDO: $pdo = new PDO('mysql:dbname=dbtest;host=127.0.0.1;charset=utf8', 'user', 'pass'); $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $stmt = $pdo->prepare('SELECT * FROM employees WHERE name = :name'); $stmt->execute(array('name' => $name)); foreach ($stmt as $row) { // do something with $row } //MYSQLi: $stmt = $dbConnection->prepare('SELECT * FROM employees WHERE name = ?'); $stmt->bind_param('s', $name); $stmt->execute(); $result = $stmt->get_result(); while ($row = $result->fetch_assoc()) { // do something with $row } ``` 坚持过滤输入和转义输出 addcslashes($input, $charlist)、mysql_real_escape_string() htmlspecialchars($input)、strip_tags($input) 富文本时使用[HTMLPurifier 富文本过滤器](http://htmlpurifier.org/) 数据库报错信息泄露防范 1. 把php.ini文件 display\_errors = Off 数据库查询函数前面加一个@字符