准备好的语句如何帮助我们防止SQL注入攻击?

维基百科说:

预处理语句对SQL注入具有弹性,因为 参数值,稍后使用不同的 协议,不需要正确转义。如果原始语句 模板不能从外部输入派生,SQL注入不能 发生。

我不太明白其中的原因。如何用简单的英语和一些例子来简单地解释?


当前回答

根本原因#1 -分隔符问题

Sql注入是可能的,因为我们使用引号来分隔字符串,也作为字符串的一部分,使得有时无法解释它们。如果我们有不能在字符串数据中使用的分隔符,sql注入就永远不会发生。解决分隔符问题可以消除sql注入问题。结构查询可以做到这一点。

根本原因#2 -人性,人们是狡猾的,一些狡猾的人是恶意的,所有的人都会犯错误

sql注入的另一个根本原因是人性。人,包括程序员,都会犯错误。当您在结构化查询中犯错误时,它不会使您的系统容易受到sql注入的攻击。如果不使用结构化查询,错误可能会产生sql注入漏洞。

结构化查询如何解决SQL注入的根本原因

结构化查询解决了分隔符问题,方法是将sql命令放在一个语句中,将数据放在一个单独的编程语句中。编程语句创建所需的分离。

Structured queries help prevent human error from creating critical security holes. With regard to humans making mistakes, sql injection cannot happen when structure queries are used. There are ways of preventing sql injection that don't involve structured queries, but normal human error in that approaches usually leads to at least some exposure to sql injection. Structured Queries are fail safe from sql injection. You can make all the mistakes in the world, almost, with structured queries, same as any other programming, but none that you can make can be turned into a ssstem taken over by sql injection. That is why people like to say this is the right way to prevent sql injection.

所以,你已经了解了sql注入的原因,以及在使用sql注入时不可能使用sql注入的本质结构化查询。

其他回答

其思想非常简单——查询和数据分别发送到数据库服务器。 这是所有。

SQL注入问题的根源在于代码和数据的混合。

事实上,我们的SQL查询是一个合法的程序。 我们正在动态地创建这样一个程序,动态地添加一些数据。因此,数据可能会干扰程序代码,甚至改变它,正如每个SQL注入示例所显示的那样(所有PHP/Mysql中的示例):

$expected_data = 1;
$query = "SELECT * FROM users where id=$expected_data";

会产生一个常规查询吗

SELECT * FROM users where id=1

而这段代码

$spoiled_data = "1; DROP TABLE users;"
$query        = "SELECT * FROM users where id=$spoiled_data";

会产生恶意序列吗

SELECT * FROM users where id=1; DROP TABLE users;

它可以工作,因为我们直接将数据添加到程序主体中,它成为程序的一部分,因此数据可能会改变程序,并且根据传递的数据,我们将有一个常规输出或用户删除的表。

而对于准备好的语句,我们不改变我们的程序,它保持不变 这就是重点。

我们首先向服务器发送一个程序

$db->prepare("SELECT * FROM users where id=?");

其中数据被一些称为参数或占位符的变量所取代。

注意,完全相同的查询被发送到服务器,其中没有任何数据!然后我们将数据与第二个请求一起发送,本质上与查询本身分离:

$db->execute($data);

所以它不会改变我们的程序,造成任何伤害。 很简单,不是吗?

我唯一需要补充的是,每个手册中都省略了:

预准备语句只能保护数据文字,但不能与任何其他查询部分一起使用。 因此,一旦我们必须添加一个动态标识符——例如,一个字段名——预处理语句就无法帮助我们。这件事我最近已经解释过了,我就不再重复了。

根本原因#1 -分隔符问题

Sql注入是可能的,因为我们使用引号来分隔字符串,也作为字符串的一部分,使得有时无法解释它们。如果我们有不能在字符串数据中使用的分隔符,sql注入就永远不会发生。解决分隔符问题可以消除sql注入问题。结构查询可以做到这一点。

根本原因#2 -人性,人们是狡猾的,一些狡猾的人是恶意的,所有的人都会犯错误

sql注入的另一个根本原因是人性。人,包括程序员,都会犯错误。当您在结构化查询中犯错误时,它不会使您的系统容易受到sql注入的攻击。如果不使用结构化查询,错误可能会产生sql注入漏洞。

结构化查询如何解决SQL注入的根本原因

结构化查询解决了分隔符问题,方法是将sql命令放在一个语句中,将数据放在一个单独的编程语句中。编程语句创建所需的分离。

Structured queries help prevent human error from creating critical security holes. With regard to humans making mistakes, sql injection cannot happen when structure queries are used. There are ways of preventing sql injection that don't involve structured queries, but normal human error in that approaches usually leads to at least some exposure to sql injection. Structured Queries are fail safe from sql injection. You can make all the mistakes in the world, almost, with structured queries, same as any other programming, but none that you can make can be turned into a ssstem taken over by sql injection. That is why people like to say this is the right way to prevent sql injection.

所以,你已经了解了sql注入的原因,以及在使用sql注入时不可能使用sql注入的本质结构化查询。

当您创建准备好的语句并将其发送到DBMS时,它将存储为SQL查询以供执行。

稍后将数据绑定到查询,以便DBMS使用该数据作为执行(参数化)的查询参数。DBMS不会使用你绑定的数据作为已经编译的SQL查询的补充;只是数据而已。

这意味着使用准备好的语句执行SQL注入从根本上是不可能的。预处理语句的本质及其与DBMS的关系阻止了这一点。

我把答案通读了一遍,仍然觉得有必要强调阐明事先陈述的本质的关键点。考虑两种查询数据库的方法,其中涉及用户输入:

天真的方法

其中一个将用户输入与部分SQL字符串连接起来以生成SQL语句。在这种情况下,用户可以嵌入恶意SQL命令,然后将这些命令发送到数据库执行。

String SQLString = "SELECT * FROM CUSTOMERS WHERE NAME='"+userInput+"'"

例如,恶意用户输入可能导致SQLString等于"SELECT * FROM CUSTOMERS WHERE NAME='James';DROP TABLE CUSTOMERS;' "

由于恶意用户的存在,SQLString包含2条语句,其中第二条语句(“DROP TABLE CUSTOMERS”)将造成损害。

准备好的语句

在这种情况下,由于查询和数据分离,用户输入永远不会被视为SQL语句,因此永远不会被执行。正是由于这个原因,注入的任何恶意SQL代码都不会造成损害。所以“DROP TABLE CUSTOMERS”在上面的例子中永远不会被执行。

简而言之,通过用户输入引入的恶意代码将不会被执行!

在SQL Server中,使用准备好的语句绝对是防注入的,因为输入参数不构成查询。这意味着执行的查询不是动态查询。 SQL注入易受攻击语句的示例。

string sqlquery = "select * from table where username='" + inputusername +"' and password='" + pass + "'";

现在,如果inoutusername变量的值是类似于a'或1=1——,这个查询现在变成:

select * from table where username='a' or 1=1 -- and password=asda

其余部分在——之后被注释,所以它永远不会被执行和绕过,就像使用下面的预处理语句示例一样。

Sqlcommand command = new sqlcommand("select * from table where username = @userinput and password=@pass");
command.Parameters.Add(new SqlParameter("@userinput", 100));
command.Parameters.Add(new SqlParameter("@pass", 100));
command.prepare();

所以实际上你不能发送另一个参数,从而避免SQL注入…