存储过程是否可以防止SQL注入?


83

存储过程可以防止对PostgreSQL数据库进行SQL注入攻击吗?我进行了一些研究,发现即使仅使用存储过程,SQL Server,Oracle和MySQL也不会对SQL注入造成威胁。但是,PostgreSQL中不存在此问题。

PostgreSQL核心中的存储过程实现是否可以防止SQL注入攻击或其他?还是即使我们仅使用存储过程,PostgreSQL也容易受到SQL注入的影响吗?如果是这样,请给我看一个例子(例如书籍,网站,纸张等)。


4
奇怪的是,这里最重要的答案是与SQL Server有关的OT,而问题与Postgres有关。这是Postgres的相关答案:dba.stackexchange.com/questions/49699/…。还有其他几个,请尝试搜索:dba.stackexchange.com/…–
欧文·

@ErwinBrandstetter最初的问题没有(由OP)用postgres标记,并且-仍然-提到了其他几个DBMS。我想这就是将各种答案集中在其他DBMS上的原因。我建议您再增加一个关注Postgres的对象。
ypercubeᵀᴹ

@ypercubeᵀᴹ:我会在这里补充时间。同时,我将dba.stackexchange.com/questions/49699/…更新为更加清晰和全面。
Erwin Brandstetter,

Answers:


71

不可以,存储过程不会阻止SQL注入。这是一个不幸的允许SQL注入的存储过程的实际示例(来自内部应用程序,创建者在我工作的地方创建):

此sql服务器代码:

CREATE PROCEDURE [dbo].[sp_colunmName2]   
    @columnName as nvarchar(30),
    @type as nvarchar(30), 
    @searchText as nvarchar(30)           
AS
BEGIN
    DECLARE @SQLStatement NVARCHAR(4000)
    BEGIN
        SELECT @SQLStatement = 'select * from Stations where ' 
            + @columnName + ' ' + @type + ' ' + '''' + @searchText + '''' 
        EXEC(@SQLStatement)
    END      
END
GO

大致相当于postgres:

CREATE or replace FUNCTION public.sp_colunmName2 (
    columnName  varchar(30),
    type varchar(30), 
    searchText  varchar(30) ) RETURNS SETOF stations LANGUAGE plpgsql            
AS
$$
DECLARE SQLStatement VARCHAR(4000);
BEGIN
    SQLStatement = 'select * from Stations where ' 
            || columnName || ' ' || type || ' ' || ''''|| searchText || '''';
    RETURN QUERY EXECUTE  SQLStatement;
END
$$;

开发人员的想法是创建一个通用的搜索过程,但结果是WHERE子句可以包含用户想要的任何内容,从而允许小的Bobby Tables进行访问。

使用SQL语句还是存储过程都没有关系。重要的是您的SQL是使用参数还是串联字符串。参数防止SQL注入;串联字符串允许SQL注入。


46

SQL注入攻击是将不受信任的输入直接附加到查询中的攻击,从而使用户可以有效地执行任意代码,如本规范XKCD漫画所示。

这样,我们得到如下情况:

userInput = getFromHTML#“ Robert')删除表学生;-”

查询=“从学生名=”的学生中选择* * + userInput

通常,存储过程是抵御SQL注入攻击的良好防御方法,因为从未解析传入的参数。

在存储过程中,在大多数数据库(和程序中,请不要忘记预编译的查询算作存储过程)如下所示:

 

创建存储的程序foo(
从学生名=:1的学生中选择*
);

然后,当程序需要访问时,它将调用foo(userInput)并愉快地检索结果。

存储过程并不是针对SQL注入的不可思议的防御措施,因为人们完全有能力编写不良的存储过程。但是,如果您了解SQL注入的工作方式,那么预编译的查询(无论是存储在数据库中还是存储在程序中)将更加难以打开安全漏洞

您可以阅读有关SQL注入的更多信息:


29

是的,在某种程度上。
仅存储过程不会阻止SQL注入。

首先让我谈谈OWASP中的 SQL注入

SQL注入攻击包括通过从客户端到应用程序的输入数据插入或“注入” SQL查询。成功的SQL注入漏洞可以从数据库读取敏感数据,修改数据库数据(插入/更新/删除),对数据库执行管理操作(例如关闭DBMS),恢复存在于DBMS文件中的给定文件的内容系统,并在某些情况下向操作系统发出命令。SQL注入攻击是一种注入攻击,其中SQL命令被注入到数据平面输入中以实现预定义的SQL命令的执行。

即使使用存储过程,也必须清理用户输入并且不要串联SQL语句。

杰夫·阿特伍德(Jeff Attwood)在“ 给我参数化的SQL,否则让我死亡 ”中解释了串联SQL的后果。

以下是每当我听到SQL注入时替代文字 我想到的有趣的动画片, 我想你明白了:-)

看看SQL注入预防作弊表,预防方法得到了很好的解释...


12

字符串连接是SQL注入的原因。使用参数设置可以避免这种情况。

存储过程在连接时强制执行无效的语法,从而增加了一层安全性,但是如果在存储过程中使用动态SQL,则存储过程就“不安全”。

因此,您上面的代码是由这些字符串的串联引起的

  • exec sp_GetUser '
  • x' AND 1=(SELECT COUNT(*) FROM Client); --
  • ' , '
  • monkey
  • '

幸运的是,这给出了无效的语法

参数化会给

exec sp_GetUser 'x'' AND 1=(SELECT COUNT(*) FROM Client); --' , 'monkey'

这意味着

  • @UserName = x' AND 1=(SELECT COUNT(*) FROM Client); --
  • @Password = monkey

现在,在上面的代码中,您将不会获得任何行,因为我假设您没有用户 x' AND 1=(SELECT COUNT(*) FROM Client); --

如果存储的过程看起来像这样(使用串联的动态SQL),则参数化的存储的proc调用仍将允许SQL注入

...
SET @sql = 'SELECT userName from users where userName = ''' + 
               @UserName + 
               ''' and userPass = ''' +
               @Password +
               ''''
EXEC (@sql)
....

因此,如所示,字符串串联是SQL注入的主要敌人

存储过程确实会增加封装,事务处理,减少的权限等,但它们仍可能被滥用以进行SQL注入。

您可以在Stack Overflow上查看有关参数化的更多信息


10

“SQL注入攻击发生时用户输入的编码错误。典型地,所述用户输入一些数据的用户和她的查询发送,在IE值$_GET$_POST$_COOKIE$_REQUEST,或$_SERVER阵列。然而,用户输入也来自各种其他源,例如套接字,远程网站,文件等。因此,您应该真正将除常量之外的所有内容(例如'foobar')都视为用户输入。”

我最近一直在对该主题进行彻底的调查,并希望与其他人分享非常有趣的材料,从而使该帖子对每个人都更加完整和有益。



来自YouTube


来自维基百科


从OWASP


从PHP手册


来自Microsoft和Oracle


堆栈溢出


SQL注入扫描器


2

存储过程并不能神奇地阻止SQL注入,但是它们确实使防止注入变得容易得多。您所要做的只是类似以下内容(Postgres示例):

CREATE OR REPLACE FUNCTION my_func (
  IN in_user_id INT 
)
[snip]
  SELECT user_id, name, address FROM my_table WHERE user_id = in_user_id; --BAM! SQL INJECTION IMMUNE!!
[snip]

而已!仅在通过字符串串联(即动态SQL)形成查询时才会出现问题,即使在这种情况下,您也可以绑定!(取决于数据库。)

如何避免在动态查询中进行SQL注入:

步骤1)问问自己是否真的需要动态查询。如果只是为了设置输入而将字符串粘在一起,那么您可能做错了。(此规则有例外-一个例外是报告某些数据库上的查询,如果不强制每次执行都编译一个新查询,则可能会出现性能问题。但是请在跳入此问题之前对其进行研究。 )

步骤2)研究为特定RDBMS设置变量的正确方法。例如,Oracle使您可以执行以下操作(引用其文档):

sql_stmt := 'UPDATE employees SET salary = salary + :1 WHERE ' 
           || v_column || ' = :2';
EXECUTE IMMEDIATE sql_stmt USING amount, column_value; --INJECTION IMMUNE!!

在这里,您仍然没有连接输入。您安全地绑定了!万岁!

如果您的数据库不支持上述功能(希望它们仍然没有问题,但我不会感到惊讶)-或者您仍然必须将输入连接起来(例如在“有时”将查询报告为我在上面暗示),那么您必须使用适当的转义功能。不要自己写。例如postgres提供quote_literal()函数。因此,您将运行:

sql_stmt := 'SELECT salary FROM employees WHERE name = ' || quote_literal(in_name);

这样,如果in_name像'[snip]或1 = 1'这样的变量((或“ 1 = 1”部分意味着选择所有行,允许用户查看他不应该的薪水!),那么quote_literal可以节省您的屁股生成结果字符串:

SELECT salary FROM employees WHERE name = '[snip] or 1=1'

找不到结果(除非您有一些名字很奇怪的员工。)

这就是要旨!现在,让我留下Oracle专家Tom Kyte关于SQL Injection主题的经典文章的链接,以阐明这一点:Linky


别忘了提到quote_ident()-但是通常,编写防注入动态SQL的最简单方法是使用标识符和文字format()的占位符。这样,SQL比使用和函数的等效版本更具可读性%I%L||quote_....()
a_horse_with_no_name
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.