可能是我对存储过程不太清楚。有人可以通过使用 MySql 的简单示例向我解释存储过程如何防止 SQL 注入。
存储过程如何防止 SQL 注入?
存储过程是参数化查询的一种形式。导致 SQL 注入的根本问题是数据被视为查询语言。
$query = "SELECT * FROM users WHERE username = '$username' AND password = '$password'";
在这个例子中,如果我设置$password
为foo' OR 'x'='x
,我们会得到:
SELECT * FROM users WHERE username = 'blah' AND password = 'foo' OR 'x'='x'
由于字符 'x' 始终等于字符 'x',因此无论用户/密码是否正确,此查询都将始终返回行。数据库无法知道您不是故意的,因为它只被赋予了一个没有上下文的字符串。
为了防止这种情况,您必须能够知道查询和数据之间的区别。存储过程通过预先编写查询来解决这个问题,并带有参数标记,以便以后可以将数据传递给它们。例如:
SELECT * FROM users WHERE username = ? AND password = ?
数据库驱动程序发送这个存储过程的名称(或者,在标准参数化查询中,只是查询文本本身)和参数列表,作为协议中不同的独立实体。这意味着数据库服务器可以安全地将查询字符串解析为查询语言,并将参数单独视为数据,没有任何歧义。
不久前,我还写了一个更长的答案,以更详细的方式解释了所有这些,如果这对您有用的话。
存储过程不能免受 SQL 注入的影响。如此处所述:
只要可以在存储过程中创建动态 SQL,就很容易受到 SQL 注入的攻击。
MySQL从5.0.13开始,具有动态 SQL 功能:
https://stackoverflow.com/questions/190776/how-to-have-dynamic-sql-in-mysql-stored-procedure
因此容易受到 SQL 注入的攻击。
在SQL server中,这是一个示例:
http://www.sqlinjection.net/advanced/stored-procedure/
VULNERABLE STORED PROCEDURE USING EXEC STATEMENT.
CREATE PROCEDURE getDescription
@vname VARCHAR(50)
AS
EXEC('SELECT description FROM products WHERE name = '''+@vname+ '''')
RETURN
另一个例子:
VULNERABLE STORED PROCEDURE USING DYNAMIC CURSOR.
CREATE PROCEDURE printDescriptions
@vname VARCHAR(100)
AS
DECLARE @vdesc VARCHAR(1000)
DECLARE @vsql VARCHAR(4000)
SET @vsql = 'SELECT description FROM products WHERE name='''+@vname+''''
DECLARE cur CURSOR FOR EXEC @vsql
OPEN cur
FETCH NEXT FROM cur INTO @vdesc
WHILE @@FETCH_STATUS = 0
BEGIN
PRINT @vdesc
FETCH NEXT FROM cur INTO @vdesc
END
CLOSE cur
DEALLOCATE cur
RETURN
在Oracle中,示例如下:
http://software-security.sans.org/developer-how-to/fix-sql-injection-in-oracle-database-code
https://www.blackhat.com/presentations/bh-europe-04/bh-eu-04-litchfield.pdf
David Litchfield 在 Blackhat Europe 的上述演示有更严重的问题,从 SQL 注入它可以导致权限提升 - 因此如果存储过程由 DBA 创建(例如,所有 Oracle 系统对象),普通 Oracle 用户可以作为 DBA 运行)。
SQL 数据库通过几个步骤处理一条语句。首先对 SQL 语句的测试进行解析,然后对其进行优化和编译。完成后,数据库现在有一个可以运行给定 SQL 语句的内部软件。
存储过程是预编译的。换句话说,数据库在您使用它之前创建了该内部软件。在这种情况下,只有程序代码被解释,而不受参数的任何影响。
如果您将完整的 SQL 语句包含参数传递给数据库,它会处理上述步骤。
例如 ...
SELECT * FROM myTable WHERE id=1
或者你给类似的东西......
SELECT * FROM myTable WHERE id=1;DROP TABLE myTable
通常没有人会在他的程序代码中编写像第二个这样的语句,但是如果您从 Web 请求中获取例如直接参数,则可能会产生这样的语句。
var sqlString="SELECT * FROM myTable WHERE id=";
sqlString = sqlString+request.getParameter("id");
// database parse, compile and optimize
var result=database.doQuery(sqlString);
如果您使用存储过程或准备好的语句。解析和编译过程已经在数据库中完成。所有解释都取决于您的程序代码。当你调用它时,数据库只将给定的参数插入到预编译的代码中,它现在接受它的数据类型。
var sqlString = "call queryMyTable(?)";
// get the precompiled statement from database
var statement = database.createStatement(sqlString);
// inject the parameter
statement.setParameter(1,request.getParameter("id"));
// if 'id' is a number it works fine ...
// but if 'id' is '1;DROP TABLE myTable' you will got a type cast error and the risk of SQL injection is banned
var result = statement.execute();
存储过程和准备好的语句在安全性方面是平等的。
另一种思考方式是明确并增加已经给出的答案。
SELECT * FROM users WHERE username = 'blah' AND password = 'foo' OR 'x'='x'
如果没有准备好的语句,OR
“foo”之后的将被视为代码
现在,从数据库的角度来看,使用准备好的语句,尝试的密码是:
'foo' OR 'x'='x'
IE OR
'foo' 之后的(以及其他所有内容)被视为数据