假设所有用户提供的参数都作为查询绑定参数传递了,准备好的语句实际上100%安全地抵御SQL注入吗?我经常告诉人们,准备好的语句是SQL注入安全措施的查克·诺里斯(或乔恩·斯凯特)。 %安全”。我对它们的理解是,它们将查询语言和参数完全隔离到服务器的前门,然后将它们视为独立的实体。

我在这个假设中正确吗?

评论

内核,数据库和其他组件并非不受所有可能的攻击的影响。由财务,政府,基础设施,军事和关键业务系统签约的安全团队经常需要多个安全层。仔细阅读一些错误列表,然后找出未修复的损坏后修补的漏洞。假设他们“全力以赴”就是否认软件的统计现实。使用组件的有限测试套件不可能测试对包含该组件的整个系统的所有可能的攻击。安全性不是绝对的0.0或1.0概率。

#1 楼

保证100%安全的SQL注入?不想从我这里得到它。

原则上,您的数据库(或与db交互的语言库)可能会以不安全的方式实现带有绑定参数的准备好的语句,容易受到某些影响一种高级攻击,例如利用缓冲区溢出或在用户提供的字符串中包含以零结尾的字符等。(您可以说这些类型的攻击不应称为SQL注入,因为它们本质上是不同的;但这只是语义)。

我从来没有听说过对现场真实数据库中的准备好的语句的任何攻击,强烈建议使用绑定参数来防止SQL注入。没有绑定参数或输入卫生条件,它很容易执行SQL注入。仅输入卫生设施,通常就可以在卫生设施周围发现一个隐蔽的漏洞。

使用绑定的参数,您可以在不依赖用户输入的情况下提前确定SQL查询执行计划,这将使SQL注入成为不可能(因为任何插入的引号,注释符号等仅插入已编译的SQL语句)。

反对使用预处理语句的唯一论据是您希望数据库根据实际查询来优化执行计划。在给出完整查询的情况下,大多数数据库足够聪明,可以制定最佳执行计划。例如,如果查询返回表的很大一部分,则它会希望遍历整个表以查找匹配项;而如果仅获取少量记录,则可以进行基于索引的搜索[1]。
首先,正如其他人指出的那样,每个支持预准备语句和绑定参数的关系数据库都不必在不查看绑定参数值的情况下预先编译预准备语句。通常,许多数据库都这样做,但是数据库在确定执行计划时也可以查看绑定参数的值。这不是问题,因为带有分开的绑定参数的预处理语句的结构使数据库很容易将SQL语句(包括SQL关键字)与绑定参数中的数据区分开(其中绑定参数中没有任何内容)解释为SQL关键字)。当通过字符串连接构造SQL语句时,这是不可能的,在该字符串连接中变量和SQL关键字会混合在一起。程序在进行该顶级调用时将安全地防止SQL注入。但是,如果您在应用程序的其他位置存在SQL注入漏洞(例如,在用户定义的函数中存储和运行在数据库中,而这些函数是您不安全编写的,用于通过字符串串联构造SQL查询)。

例如,如果您在您的应用程序中编写了伪代码,例如:但是,如果用户定义的数据库函数是用不安全的方式编写的(使用PL / pgSQL语法):通过字符串串联构造的SQL语句将SQL语句与包含用户定义变量的值的字符串混合在一起。

这表示除非您不安全(通过字符串串联构造SQL语句),否则更自然地以安全的方式编写用户定义,例如:

sql_stmt = "SELECT create_new_user(?, ?)"
params = (email_str, hashed_pw_str)
db_conn.execute_with_params(sql_stmt, params)


此外,如果您确实感到有必要在用户定义的函数中从字符串组成SQL语句,则即使在用户定义的函数中,仍可以用与stored_procedures / bound参数相同的方式从SQL语句中分离数据变量。例如在PL / pgSQL中:

CREATE FUNCTION create_new_user(email_str, hashed_pw_str) RETURNS VOID AS
$$
DECLARE
   sql_str TEXT;
BEGIN
     sql_str := 'INSERT INTO users VALUES (' || email_str || ', ' || hashed_pw_str || ');'
     EXECUTE sql_str;
END;
$$
LANGUAGE plpgsql;


因此,只要您不只是在其他地方做不安全的事情(即构造通过字符串连接的SQL语句。)

评论


安全方面略有偏离,但是SQL Server提供了选项(重新编译)来强制进行新的编译,并且在选择计划时它将使用当前参数值。尽管我不确定,但我希望其他RDBMS提供类似的功能。因此,没有理由不使用参数化查询!

– pipTheGeek
2012年5月21日17:52

那正是我想的。您可以保证几乎100%忽略了实现漏洞。

–多项式
2012年5月21日在18:13

@pipTheGeek-有趣。我将postgresql用作我的首选rdbms(免费软件),但找不到此选项,而MySQL特别表示它们不支持“ WITH RECOMPILE”。我认为,如果您先编写一个存储过程,然后执行一条准备好的语句来调用它,则可以在postgres中执行类似的操作-但不确定它将重新编译执行计划(需要检查)。

– jimbob博士
2012年5月21日在18:34

抱歉,我今天有点闷。横向SQL注入(databasesecurity.com/dbsec/lateral-sql-injection.pdf)如何适应这种情况?

–布鲁斯·埃迪格(Bruce Ediger)
2012年5月22日15:45

@BruceEdiger-如果您使用带有绑定参数的预备语句,则不会在链接中进行横向SQL注入。在伪代码中,如果定义了sql_str =“从创建的所有对象中选择名称=?”然后执行了prepare_stmt = db_cursor.prepare(sql_str),prepare_stmt.execute_with_param(SYSDATE)的操作,就无法使用SYSDATE注入代码来更改执行计划,因为执行计划是在prepare()步骤中确定的,而与SYSDATE的值无关。 (除非存在另一个漏洞)。横向缺陷是在进行幼稚的字符串处理以构造SQL语句。

– jimbob博士
2012年5月22日16:10

#2 楼

100%安全吗?差远了。绑定参数(以语句方式或以其他方式准备)有效地可以100%防止一类SQL注入漏洞(假设没有数据库错误和合理的实现)。他们决不会阻止其他阶级。请注意,PostgreSQL(我的首选数据库)具有将参数绑定到即席语句的选项,如果您不需要这些语句的某些功能,则可以节省与准备好的语句的往返行程。

您必须弄清楚许多大型,复杂的数据库本身就是程序。这些程序的复杂性相差很大,对于内部编程例程,必须注意SQL注入。这样的例程包括触发器,用户定义的函数,存储过程等。这些事物在应用程序级别之间如何交互并不总是很明显,因为许多优秀的dba在应用程序访问级别和存储级别之间提供了某种程度的抽象。然后,至少在PostgreSQL中,为了计划而查看数据。该计划已执行。使用准备好的语句,可以保存计划,因此您可以反复使用不同的数据重新执行同一计划(这可能是您想要的,也可能不是您想要的)。但是要点是,对于绑定的参数,参数无法将任何内容注入到解析树中。因此,此类SQL注入问题已得到妥善解决。 。这些带来了新的问题。如果其中包含任何动态SQL,则必须担心那里存在SQL注入。他们写入的表可能具有自己的触发器,依此类推。类似地,一个函数调用可能会调用另一个查询,后者可能会调用另一个函数调用,依此类推。每一个都是独立于主树计划的。

这意味着,如果我使用诸如foo'; drop user postgres; --之类的绑定参数运行查询,则它无法直接暗示顶级查询树并导致其添加另一个命令来删除postgres用户。但是,如果此查询是否直接调用另一个函数,则很可能在某个地方,某个函数容易受到攻击,而postgres用户将被丢弃。绑定参数对辅助查询不提供任何保护。这些辅助查询需要确保它们也尽可能使用绑定的参数,而在其他情况下,则需要使用适当的引用例程。 >关于在堆栈溢出问题上明显存在此问题的问题,请参阅https://stackoverflow.com/questions/37878426/conditional-where-expression-in-dynamic-query/37878574#37878574

另外一个有问题的版本(由于对实用程序语句的限制),位于https://stackoverflow.com/questions/38016764/perform-create-index-in-plpgsql-doesnt-run/38021245#38021245

评论


这就是为什么我更喜欢保持数据库尽可能简单。

– Cees Timmerman
16年7月6日在12:15

“请注意,PostgreSQL(我的首选数据库)具有将参数绑定到临时语句的选项,如果您不需要某些准备好的语句,则可以节省往返于准备好的语句的往返时间。”您能否分享记录在哪里?我试图找到它,但没有成功。寻找的原因:我想将其添加到github.com/mauricio/postgresql-async中,以能够绑定参数而无需在服务器上分配准备好的语句。多谢!

–多米尼克·多恩(Dominik Dorn)
17年3月20日在19:03

该文档有点难以理解。请参阅postgresql.org/docs/9.6/static/protocol-overview.html和“未命名的门户”的概念。使用PQExecParams()函数可以在libpq源代码中找到一个不错的地方。看起来这是内部处理的(至少对我而言),就像未命名的准备好的语句一样,同时发送了绑定参数(后来的消息,相同的数据包?)

–克里斯·特拉弗斯(Chris Travers)
17年3月21日在8:03