Gau*_*all 2 php mysql sql sql-injection
我已经多次看过Computerphile关于这个主题的视频(对于任何想要的人,这是链接:https : //www.youtube.com/watch?v=_jKylhJtPmI)。他提供了一些有关如何打击SQL Injection应用程序并使之更加有效的非常好的建议。这些是他的视频的重点:
mysql_real_escape_string(String s)功能。基本上,每个危险字符(/,", {, }, etc)的开头都带有一个斜线(/)。因此,基本上,这使字符串内的引号或斜杠无用。最好的办法是使用准备好的语句。因此,您基本上会说:
SELECT * FROM USERS WHERE username = ?
Run Code Online (Sandbox Code Playgroud)
稍后,您将问号替换为要输入的字符串作为用户名。这样做的好处是不会混淆PHP或任何其他容错语言,并使用这种简单且(有点怪异)优雅的解决方案仅用字符串替换此字符串,并告诉语言给出的只是字符串而已。仅此而已。
一切都很好,但是这部影片确实过时了。它于2013年问世,此后出现了许多新技术。因此,我尝试在互联网上搜索是否有任何新方法,或者是否有这种方法。但是问题是我要么找不到它,要么我发现一些超级混乱的东西。
因此,我的问题是:是否有更好的增强方法来对抗已引入的SQL注入,或者准备好的语句是否仍然是规范,以及它们是否容易受到任何形式的攻击?
在将动态数据与SQL查询结合的大多数示例中,参数绑定仍然是最佳解决方案。
你应该明白为什么。它不只是为您做字符串替换。你可以自己做。
之所以起作用,是因为它将动态值与SQL解析步骤分开。RDBMS在以下期间解析SQL语法prepare():
$stmt = $pdo->prepare("SELECT * FROM USERS WHERE username = ?");
Run Code Online (Sandbox Code Playgroud)
这点之后,RDBMS 知道的?只能是单个标量值。没什么 不是值列表,列名称,表达式,子查询,第二个SELECT查询的UNION等。
然后,在执行步骤中发送要绑定到该占位符的值。
$stmt->execute( [ "taraiordanov" ] );
Run Code Online (Sandbox Code Playgroud)
该值将发送到RDBMS服务器,并在查询中代替它,但仅作为一个值,然后可以执行查询。
这使您可以使用插入的不同值多次执行查询。即使SQL解析器只需要解析一次查询。它记住了如何将新值插入原始准备的SQL查询中,因此您可以execute()根据需要进行多次:
$stmt->execute( [ "hpotter" ] );
$stmt->execute( [ "hgranger" ] );
$stmt->execute( [ "rweasley" ] );
...
Run Code Online (Sandbox Code Playgroud)
准备好的陈述是最好的吗?对,他们是。该建议来自2013年并不重要,它仍然是正确的。实际上,有关SQL的功能可以追溯到更远的时间。
那么查询参数是防御SQL注入的万无一失的方法吗?是的,如果您需要在SQL中将变量组合为值,则可以。也就是说,您打算在查询中替换参数,否则将使用带引号的字符串文字,带引号的日期文字或数字文字。
但是,您可能还需要对查询执行其他操作。有时您需要根据应用程序中的条件逐个构建SQL查询。就像如果您要进行搜索username但有时还要在搜索last_login日期中添加一个词该怎么办?参数不能为搜索添加一个全新的词。
这是不允许的:
$OTHER_TERMS = "and last_login > '2019-04-01'";
$stmt = $pdo->prepare("SELECT * FROM USERS WHERE username = ? ?");
$stmt->execute( [ "taraiordanov", $OTHER_TERMS ] ); // DOES NOT WORK
Run Code Online (Sandbox Code Playgroud)
如果要允许用户请求对结果进行排序,又要让用户选择要对哪一列进行排序以及是升序还是降序排序,该怎么办?
$stmt = $pdo->prepare("SELECT * FROM USERS WHERE username = ? ORDER BY ? ?");
$stmt->execute( [ "taraiordanov", "last_login", "DESC" ] ); // DOES NOT WORK
Run Code Online (Sandbox Code Playgroud)
在这种情况下,你必须把查询条件的列名和语法到您的SQL字符串之前 prepare()。您只需要格外小心,不要让不受信任的输入污染查询中放置的动态部分。也就是说,请确保它基于您可以完全控制代码中的字符串值,而不是应用程序外部的任何内容(例如用户输入或文件或调用API的结果)。
重新评论:
马丁添加的想法有时称为白名单。我将以更易读的方式写出马丁的示例:
switch ($_GET['order']) {
case "desc":
$sqlOrder = "DESC";
break;
default:
$sqlOrder = "ASC";
break;
}
Run Code Online (Sandbox Code Playgroud)
我用Martin代替了Martin case "asc",default因为如果用户输入是其他任何东西 -甚至是恶意的东西-唯一可能发生的情况是,任何其他输入将默认为SQL顺序ASC。
这意味着只有两种可能的结果,ASC或DESC。一旦您的代码完全控制了可能的值,并且知道两个值都是安全的,则可以将值内插到SQL查询中。
总之:始终保持在你的心中的假设,$_GET并$_POST可能包含恶意内容。客户很容易将他们想要的任何东西放入请求中。它们不受HTML表单中值的限制。
牢记这一假设,防御性地编写代码。
另一个技巧:很多人认为在客户端输入$_GET和$_POST你需要防止的唯一输入。这不是真的!任何输入源都可能包含有问题的内容。例如,读取文件并在SQL查询中使用该文件,或调用API。
如果使用不正确,即使以前安全地插入到数据库中的数据也可以引入SQL注入。