准备好的语句/参数化查询足以防止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(很久很久以前)在处理涉及宽字符的参数时是愚蠢的,在这里的另一个高投票的答案中,有一个罕见的情况,可以允许注入通过参数化查询。