假设我有这样的代码:

$dbh = new PDO("blahblah");

$stmt = $dbh->prepare('SELECT * FROM users where username = :username');
$stmt->execute( array(':username' => $_REQUEST['username']) );

PDO文档说:

预处理语句的参数不需要加引号;司机会帮你处理的。

这真的是避免SQL注入所需要做的一切吗?真的那么简单吗?

如果MySQL有影响,你可以假设它是MySQL。另外,我对使用预处理语句对抗SQL注入真的很好奇。在这种情况下,我不关心XSS或其他可能的漏洞。


当前回答

准备好的语句/参数化查询足以防止SQL注入,但只有在应用程序中的每个查询都一直使用时才可以。

如果在应用程序的其他地方使用未经检查的动态SQL,它仍然容易受到二阶注入的攻击。

二级注入意味着数据在被包含在查询中之前已经在数据库中循环了一次,并且很难实现。AFAIK,你几乎从未见过真正的工程二级攻击,因为攻击者通常更容易通过社会工程进入,但你有时会因为额外的良性“角色”或类似的东西而出现二级漏洞。

当您可以使一个值存储在数据库中,然后在查询中用作文字时,就可以实现二阶注入攻击。作为一个例子,假设你在一个网站上创建一个帐户时输入了以下信息作为你的新用户名(假设这个问题是MySQL DB):

' + (SELECT UserName + '_' + Password FROM Users LIMIT 1) + '

If there are no other restrictions on the username, a prepared statement would still make sure that the above embedded query doesn't execute at the time of insert, and store the value correctly in the database. However, imagine that later the application retrieves your username from the database, and uses string concatenation to include that value a new query. You might get to see someone else's password. Since the first few names in users table tend to be admins, you may have also just given away the farm. (Also note: this is one more reason not to store passwords in plain text!)

We see, then, that if prepared statements are only used for a single query, but neglected for all other queries, this one query is not sufficient to protect against sql injection attacks throughout an entire application, because they lack a mechanism to enforce all access to a database within an application uses safe code. However, used as part of good application design — which may include practices such as code review or static analysis, or use of an ORM, data layer, or service layer that limits dynamic sql — **prepared statements are the primary tool for solving the Sql Injection problem.** If you follow good application design principles, such that your data access is separated from the rest of your program, it becomes easy to enforce or audit that every query correctly uses parameterization. In this case, sql injection (both first and second order) is completely prevented.


* MySql/PHP(很久很久以前)在处理涉及宽字符的参数时是愚蠢的,在这里的另一个高投票的答案中,有一个罕见的情况,可以允许注入通过参数化查询。

其他回答

不,他们不总是这样。

这取决于您是否允许在查询本身中放置用户输入。例如:

$dbh = new PDO("blahblah");

$tableToUse = $_GET['userTable'];

$stmt = $dbh->prepare('SELECT * FROM ' . $tableToUse . ' where username = :username');
$stmt->execute( array(':username' => $_REQUEST['username']) );

将容易受到SQL注入的攻击,在本例中使用准备好的语句将不起作用,因为用户输入被用作标识符,而不是数据。正确的答案是使用某种过滤/验证,比如:

$dbh = new PDO("blahblah");

$tableToUse = $_GET['userTable'];
$allowedTables = array('users','admins','moderators');
if (!in_array($tableToUse,$allowedTables))    
 $tableToUse = 'users';

$stmt = $dbh->prepare('SELECT * FROM ' . $tableToUse . ' where username = :username');
$stmt->execute( array(':username' => $_REQUEST['username']) );

注意:你不能使用PDO来绑定DDL(数据定义语言)之外的数据,也就是说这不起作用:

$stmt = $dbh->prepare('SELECT * FROM foo ORDER BY :userSuppliedData');

上述原因是由于DESC和ASC不是数据。PDO只能对数据进行转义。其次,你甚至不能在它周围加上引号。允许用户选择排序的唯一方法是手动筛选并检查它是DESC还是ASC。

然而,如果你要防止sql注入前端,使用html或js检查,你必须考虑前端检查是“可绕过的”。

你可以禁用js或者使用前端开发工具编辑模式(现在firefox或chrome都内置了)。

因此,为了防止SQL注入,在控制器内部清洗输入日期后端是正确的。

我想建议您使用filter_input()原生PHP函数来净化GET和INPUT值。

如果您想进一步提高安全性,对于合理的数据库查询,我建议您使用正则表达式来验证数据格式。 在这种情况下,Preg_match()将帮助您! 但要小心!正则表达式引擎不是那么轻。仅在必要时使用它,否则应用程序性能将下降。

安全是有成本的,但不要浪费你的业绩!

简单的例子:

如果你想再次检查从GET接收到的值是否是一个小于99的数字 如果(! preg_match (' / [0 - 9] {1, 2 }/')){...} 是更重的

if (isset($value) && intval($value)) <99) {...}

所以,最终的答案是:“不!PDO Prepared Statements不阻止所有类型的sql注入”;它不防止意外的值,只是防止意外的连接

是的,这就足够了。注入型攻击的工作方式是通过某种方式让一个解释器(数据库)来计算一些东西,这些东西应该是数据,就像它是代码一样。这只有在你把代码和数据混合在同一种介质中(例如。当您将查询构造为字符串时)。

参数化查询通过分别发送代码和数据来工作,因此永远不可能在其中找到漏洞。

不过,您仍然容易受到其他注射类型的攻击。例如,如果在html页面中使用数据,则可能受到XSS类型攻击。

准备好的语句/参数化查询足以防止SQL注入,但只有在应用程序中的每个查询都一直使用时才可以。

如果在应用程序的其他地方使用未经检查的动态SQL,它仍然容易受到二阶注入的攻击。

二级注入意味着数据在被包含在查询中之前已经在数据库中循环了一次,并且很难实现。AFAIK,你几乎从未见过真正的工程二级攻击,因为攻击者通常更容易通过社会工程进入,但你有时会因为额外的良性“角色”或类似的东西而出现二级漏洞。

当您可以使一个值存储在数据库中,然后在查询中用作文字时,就可以实现二阶注入攻击。作为一个例子,假设你在一个网站上创建一个帐户时输入了以下信息作为你的新用户名(假设这个问题是MySQL DB):

' + (SELECT UserName + '_' + Password FROM Users LIMIT 1) + '

If there are no other restrictions on the username, a prepared statement would still make sure that the above embedded query doesn't execute at the time of insert, and store the value correctly in the database. However, imagine that later the application retrieves your username from the database, and uses string concatenation to include that value a new query. You might get to see someone else's password. Since the first few names in users table tend to be admins, you may have also just given away the farm. (Also note: this is one more reason not to store passwords in plain text!)

We see, then, that if prepared statements are only used for a single query, but neglected for all other queries, this one query is not sufficient to protect against sql injection attacks throughout an entire application, because they lack a mechanism to enforce all access to a database within an application uses safe code. However, used as part of good application design — which may include practices such as code review or static analysis, or use of an ORM, data layer, or service layer that limits dynamic sql — **prepared statements are the primary tool for solving the Sql Injection problem.** If you follow good application design principles, such that your data access is separated from the rest of your program, it becomes easy to enforce or audit that every query correctly uses parameterization. In this case, sql injection (both first and second order) is completely prevented.


* MySql/PHP(很久很久以前)在处理涉及宽字符的参数时是愚蠢的,在这里的另一个高投票的答案中,有一个罕见的情况,可以允许注入通过参数化查询。

就我个人而言,我总是首先在数据上运行某种形式的卫生,因为你永远不能相信用户输入,然而,当使用占位符/参数绑定时,输入的数据被单独发送到服务器的sql语句,然后绑定在一起。这里的关键是,这将提供的数据绑定到特定类型和特定用途,并消除了更改SQL语句逻辑的任何机会。