我有人指出,使用私有方法处理单个类所做的所有查询的查询执行会增加SQL注入攻击的风险。
此方法的示例可能如下所示(如下所示)。我省略了一些细节,以免分散任何人的注意力。
如果您想谈谈实施,请随时在评论中。安全审查没有评论该方法的内容,但主要是它不应该是自己的方法。
请注意,queryText是从包含预准备语句的SQL文本的受保护静态最终字符串生成的。准备好的语句文本中的?是使用PreparedStatement的setString(或set whatever)方法设置的。在预准备语句中设置的变量尽可能强类型地进入调用方法。
然后将queryText传递给私有方法。
private ResultSet executeQuery(PreparedStatement stmt) throws SQLException {
// Declare result set variable
try{
try{
// execute statement and store in variable
}
catch(SQLException se){
// log, close connection, do any special processing, rethrow se
}
}
finally{
// This finally block is here to ensure the connection closes if
// some special processing (not shown) in the other try generates a runtime exception
// close connection and statement properly
}
// return result set
}
推荐的替代方法是在每个执行查询的方法中基本内联相同的代码。
我没有将此发布到security.stackexchange.com,因为我认为它符合特定的安全编程问题。
我认为没有理由将这个代码(从私有方法)复制到许多类中会增加任何保护。会吗?
谢谢
答案 0 :(得分:1)
如果此方法执行的查询具有SQL注入所需的成分,那么无论private/public
方法如何,它都会受到影响。
这个私有方法将由一些公共方法调用,该方法从用户(或)数据库获取输入。如果该输入是恶意的,则私有方法无法阻止它执行。
不要使用原始SQL字符串,总是更好地使用预准备语句。
答案 1 :(得分:1)
拥有一个执行查询的中心(不重复)位置是良好的想法。从代码可维护性和安全性角度来看都是如此。为什么可能的代码多次出现问题?这只意味着你必须多次维护它!
对我来说似乎很重要(以及通过编辑问题而改变的)是它应该尽可能地用它来执行手工构建的SQL字符串。
例如,您可以使用自定义枚举替换任何String
参数(您最初使用PreparedStatement
替换public enum SQLQuery {
QUERY1("SELECT foo FROM BAR", 0),
QUERY2("SELECT foo from BAR where baz = ?"; 1);
private final String sql;
private final int argumentCount;
private SQLQuery(final String sql, final int argumentCount) {
this.sql = sql;
this.argumentCount = argumentCount;
}
public String getSQL() {
return sql;
}
public int getArgumentCount() {
return argumentCount;
}
}
):
public ResultSet executeQuery(SQLQuery query, Object... arguments) {
// implementation left as an exercise for the reader
}
然后你可以这样编写你的方法:
String
通过这种方式,您可以非常确保您(或您团队中的任何其他人)不会意外地将自建setObject()
传递到您的方法中。
如果有必要,可以扩展此方法以处理不同的参数类型,但在许多情况下使用SQLQuery
工作正常。
为了提高模块性,您可以从该枚举中提取接口,并允许多个枚举定义查询(例如,如果项目中有单独的模块)。但这有一个缺点,即恶意(或无能)的开发人员可以使用{{1}}的动态非枚举实现来将他们手动构建的SQL字符串放入该方法中。