我们有一个Java应用程序,它与多个SQL Server数据库进行通信 框。这些数据库的数量和名称各不相同。总的来说,我们几乎只使用CallableStatement存储过程来访问数据库。我们非常擅长避免SQL注入和使用绑定变量。
唯一关注的问题是数据库名称本身被连接到我们传递给CallableStatement的SQL中:
"{call [" + dbName + ".dbo." + procName + "(?, ?, ?)}"
procName使用模板方法模式硬编码到子类中,因此保证字符串安全。
dbName是在外部定义的。我已经尝试将dbName设置为各种模式以逃避语法并在我的开发环境中利用它并且不成功。
我已将其设置为以下内容以生成以下SQL调用(更改了表和proc名称以保护无辜者):
securitytest].nx_proc()};delete from poor_victim_table;
变为
{call [securitytest].nx_proc()};delete from poor_victim_table;].dbo.proper_proc_name()}
和
securitytest].nx_proc()};exec('delete from poor_victim_table');
变为
{call [securitytest].nx_proc()};exec('delete from poor_victim_table');].dbo.proper_proc_name(?,?,?,?,?,?,?)}
Incorrect syntax near ')'.
和poor_victim_table
中的结果仍有行。我使用了truncate table
,drop table
和drop database
,当它们不起作用时,我切换到简单的delete
来排除安全设置。
如果我使用带有绑定参数的proc,我总是会得到预期参数的数量与提供的参数(例如The index 1 is out of range.
)不匹配。
securitytest]};exec('delete from poor_victim_table');
变为
{call [securitytest]};exec('delete from poor_victim_table');].dbo.proper_proc_name(?,?,?,?,?,?,?)}
所有道路似乎都会导致运行时错误,并且SQL无法执行。当然,这很棒。但我想确保它失败,因为它不能成功而不会失败,因为我没有尝试正确的组合。
流行的观点/都市神话是使用存储过程会让你免受SQL注入的影响,但在安全方面,我宁愿不相信绝对的语句。
经过一段时间的研究后,我想出的最好的是这个stackoverflow问题:SQL injection - no danger on stored procedure call (on iSeries)?。它似乎支持使用CallableStatement,因为它可以保护您免受SQL注入,除非您的proc代码本身使输出参数中的动态SQL 。
所以,我对社区的问题是,假设proc中的SQL代码是安全的,在JDBC中使用CallableStatement是否真的可以防止SQL注入?或者SQL Server驱动程序是否以阻止它的方式解析字符串,但其他驱动程序可能不会?还是我不够努力?
如果安全,该保证是如何制定的?是否由于使用{ call blah(?) }
的抽象语法不是真正的SQL,而是被转换为SQL?
答案 0 :(得分:2)
你应该是安全的,但就像你一样,我不相信,特别是如果你使用不同的JDBC驱动程序等连接到不同的数据库。
如果我是你,在你发布的声明之前,我一定要检查dbName是否包含除字母,数字和下划线之外的任何内容。这应该允许所有有效的dbNames,并防止各种混乱。
答案 1 :(得分:0)
检查你的数据库连接url,我认为这是引用静态数据库,所以如果你在可调用语句中写db命令,这会在db名称改变时产生问题,你的代码就像死掉(大多数地方要改变),所以不要在查询中使用db name,但您可以为不同的数据库连接或不同的帮助程序类创建不同的对象。
答案 2 :(得分:0)
流行观点/都市神话是使用存储过程 使您不受SQL注入的影响,但我宁可不信任绝对 关于安全性的声明。
并且您不信任这种笼统的声明也是很好的选择。但是,该声明必须是合格/合格的。
请参见,存储过程编写正确时,可以避免SQL注入。它允许您控制允许哪种类型的SQL语句。您可以配置生产数据库,以使其不允许执行DML,而仅允许执行存储过程(因此,可以避免意外或有意地使您避免执行可怕的笛卡尔关节的人。)
但是,除了编写好的存储过程之外,调用方还承担验证和清理输入的责任,而JDBC为您提供了一种通过"bind" parameters进行操作的方式。
CallableStatement
继承自PreparedStatement
,后者提供了用于绑定参数的方法。绑定方法对传入的参数值进行转义,从而使注入变得几乎不可能。
想像一下存储过程(以及可调用的和准备好的语句)。您可以很好地用它锤打指甲或压碎拇指。