mysqli_real_escape_string - 100% 安全的示例

S. *_* F. 5 php mysqli sql-injection

我知道已经有人就这个话题提出了很多问题。我也知道要走的路是准备好的陈述。然而,我仍然没有完全理解以下是否或如何可能成为安全问题:

$mysqli = new mysqli("localhost", "root", "", "myDatabase");
$mysqli->set_charset("utf8");
$pw = mysqli_real_escape_string($mysqli,$_POST['pw']);
$username = mysqli_real_escape_string($mysqli,$_POST['username']);

$str = "SELECT * FROM users WHERE id='".$id."' AND username='".$username."'";
$result = $this -> mysqli -> query($qstr);

if($result->num_rows > 0){
    //user logged in
}
Run Code Online (Sandbox Code Playgroud)

我尝试了注入备忘单中的许多不同输入,但找不到任何通过查询的内容。例如,如果我输入任何带有“;”的内容 然后 $result 变成 false,因为据我所知,一个查询不能包含两个单独的语句。任何带有 ' 或 " 的输入都会被 mysqli_real_escape_string 清理。

您能向我解释一下上面的代码是如何被利用的吗?如果您有一个解释它的链接,我也非常乐意阅读它!

干杯

编辑:这个答案已经回答了:

绕过 mysql_real_escape_string() 的 SQL 注入

然而,这个问题是关于旧版本的 mysql 而不是 mysqli。其次,得票最多的答案陈述了以下可以绕过它的示例:

mysql_query('SET NAMES gbk');
$var = mysql_real_escape_string("\xbf\x27 OR 1=1 /*");
mysql_query("SELECT * FROM test WHERE name = '$var' LIMIT 1");
Run Code Online (Sandbox Code Playgroud)

但我并不完全理解这一点。第一行

mysql_query('SET NAMES gbk');
Run Code Online (Sandbox Code Playgroud)

不能从外部设置,对吗?这只是一个例子,如果有人在他的程序中设置了“gbk”。所以如果我用

$mysqli->set_charset("utf8");
Run Code Online (Sandbox Code Playgroud)

并且还使用了

id='".$id."' (single quotes around $id) 
Run Code Online (Sandbox Code Playgroud)

那我就100%安全了,对吗?

You*_*nse 5

您的孤立和简化的示例在技术上是安全的。

然而,它仍然存在两个问题:

  • 假设:问题的陈述是基于mysqli_real_escape_string()与任何安全问题相关的假设而做出的。这只是一个严重的错觉。这是一个字符串格式化函数,它可以保护您免受 SQL 注入,只是作为一个副作用。但这种保护既不是该功能的目标也不是目的。因此,它绝对不应该用于此目的。这种谬误将不可避免地导致您对该函数的误用(例如用它来“保护”数字)并最终允许 SQL 注入。
  • 您所提出的代码固有的可分离性。保护由部分组成:
    • 设置正确的编码
    • 转义特殊字符
    • 将转义值用引号引起来

事实上,其中一些强制性措施可能会被忘记,而且问题陈述再次只强调一个部分——逃跑。人们总是强调的是逃避,而其他两项措施却很少被提及。只要看看你的问题 - 你的意思是代码,但问的是一个函数。因此,对您提出的问题的任何字面答案都会给人留下致命的错误印象,但这mysqli_real_escape_string()没关系。

简而言之,问题的陈述有助于引发 PHP 相关的最危险的妄想:该函数可以防止 SQL 注入。

与这个复杂的三部分方程不同,准备好的陈述构成了一个不可分割的措施。你不能忘记其中一个部分。你不能滥用它。尝试mysqli_real_escape_string()保护标识符,它会悄悄地被忽视,直到实际注入发生。尝试为标识符准备一个准备好的语句 - 并得到一个错误。