存储过程可以防止对PostgreSQL数据库进行SQL注入攻击吗?我进行了一些研究,发现即使仅使用存储过程,SQL Server,Oracle和MySQL也不会对SQL注入造成威胁。但是,PostgreSQL中不存在此问题。
PostgreSQL核心中的存储过程实现是否可以防止SQL注入攻击或其他?还是即使我们仅使用存储过程,PostgreSQL也容易受到SQL注入的影响吗?如果是这样,请给我看一个例子(例如书籍,网站,纸张等)。
存储过程可以防止对PostgreSQL数据库进行SQL注入攻击吗?我进行了一些研究,发现即使仅使用存储过程,SQL Server,Oracle和MySQL也不会对SQL注入造成威胁。但是,PostgreSQL中不存在此问题。
PostgreSQL核心中的存储过程实现是否可以防止SQL注入攻击或其他?还是即使我们仅使用存储过程,PostgreSQL也容易受到SQL注入的影响吗?如果是这样,请给我看一个例子(例如书籍,网站,纸张等)。
Answers:
不可以,存储过程不会阻止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注入。
SQL注入攻击是将不受信任的输入直接附加到查询中的攻击,从而使用户可以有效地执行任意代码,如本规范XKCD漫画所示。
这样,我们得到如下情况:
userInput = getFromHTML#“ Robert')删除表学生;-” 查询=“从学生名=”的学生中选择* * + userInput
通常,存储过程是抵御SQL注入攻击的良好防御方法,因为从未解析传入的参数。
在存储过程中,在大多数数据库(和程序中,请不要忘记预编译的查询算作存储过程)如下所示:
创建存储的程序foo( 从学生名=:1的学生中选择* );
然后,当程序需要访问时,它将调用foo(userInput)
并愉快地检索结果。
存储过程并不是针对SQL注入的不可思议的防御措施,因为人们完全有能力编写不良的存储过程。但是,如果您了解SQL注入的工作方式,那么预编译的查询(无论是存储在数据库中还是存储在程序中)将更加难以打开安全漏洞。
您可以阅读有关SQL注入的更多信息:
是的,在某种程度上。
仅存储过程不会阻止SQL注入。
首先让我谈谈OWASP中的 SQL注入
SQL注入攻击包括通过从客户端到应用程序的输入数据插入或“注入” SQL查询。成功的SQL注入漏洞可以从数据库读取敏感数据,修改数据库数据(插入/更新/删除),对数据库执行管理操作(例如关闭DBMS),恢复存在于DBMS文件中的给定文件的内容系统,并在某些情况下向操作系统发出命令。SQL注入攻击是一种注入攻击,其中SQL命令被注入到数据平面输入中以实现预定义的SQL命令的执行。
即使使用存储过程,也必须清理用户输入并且不要串联SQL语句。
杰夫·阿特伍德(Jeff Attwood)在“ 给我参数化的SQL,否则让我死亡 ”中解释了串联SQL的后果。
以下是每当我听到SQL注入时 我想到的有趣的动画片, 我想你明白了:-)
看看SQL注入预防作弊表,预防方法得到了很好的解释...
字符串连接是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上查看有关参数化的更多信息
“SQL注入攻击发生时用户输入的编码错误。典型地,所述用户输入一些数据的用户和她的查询发送,在IE值$_GET
,$_POST
,$_COOKIE
,$_REQUEST
,或$_SERVER
阵列。然而,用户输入也来自各种其他源,例如套接字,远程网站,文件等。因此,您应该真正将除常量之外的所有内容(例如'foobar'
)都视为用户输入。”
我最近一直在对该主题进行彻底的调查,并希望与其他人分享非常有趣的材料,从而使该帖子对每个人都更加完整和有益。
存储过程并不能神奇地阻止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_....()