提问人:sakura 提问时间:8/23/2022 最后编辑:Mark Rotteveelsakura 更新时间:8/26/2022 访问量:407
如何修改代码以避免SQL注入攻击?
How can I modify the code to avoid SQL-injection attack?
问:
我需要对两个字段和 .它们都可以是 .如果一个字段是 ,则搜索应基于另一个字段。businessName
businessAddress
null
null
具体来说,
- if 然后执行
businessName="name"
businessAddress="address"
select * from business where businessName like '%name%' and businessAddress like '%address%'
- if 然后执行
businessName=null
businessAddress="address"
select * from business where businessAddress like '%address%'
- if 然后执行
businessName=null
businessAddress=null
select * from business
我的代码:
StringBuilder sb = new StringBuilder("select * from business where 1=1 ");
if (businessName != null) {
sb.append("and businessName like '%" + businessName + "%' ");
}
if (businessAddress != null) {
sb.append("and businessAddress like '%" + businessAddress + "%' ");
}
try {
con = DBUtil.getConnection();
pst = con.prepareStatement(sb.toString());
rs = pst.executeQuery();
} ...
显然,它有受到SQL注入攻击的危险。我知道方法可以避免攻击,但是在验证之前,字段的数量是不确定的。prepareStatement.setString()
如何修改它?每种情况或代码的单独方法(如下所示)似乎很丑陋。
if(businessName!=null){
if(businessAddress!=null){
sql = ...;
}else {
sql = ...;
}
else{
...
答:
您不需要动态 SQL,可以在查询中使用绑定变量:
String query = "select * from business where businessName LIKE '%' || ? || '%' AND businessAddress LIKE '%' || ? || '%'";
(假设 ||
是 SQL 方言的字符串连接运算符。
然后使用准备好的语句并将 和 绑定到变量。businessName
businessAddress
PreparedStatement st = con.prepareStatement(query);
st.setString(1,businessName);
st.setString(2,businessAddress);
ResultSet rs = st.executeQuery();
(添加异常处理。
或者,如果您的 SQL 方言要求您与 then 分开处理:NULL
LIKE
String query = "select * from business
where (:name IS NULL OR businessName LIKE '%' || :name || '%')
AND (:addr IS NULL OR businessAddress LIKE '%' || :addr || '%')";
并使用命名绑定变量 和 (或使用传递两次值)。:name
:addr
?
评论
'%' || NULL || '%'
'%%'
'%' || NULL || '%'
NULL
UNKNOWN
FALSE
NULL
WHERE (:name IS NULL OR businessName LIKE '%' || :name || '%') ...
永远不要将值连接到这样的查询字符串中。执行查询时,请始终使用带参数的预准备语句,尤其是对于用户源值。
对于您的情况,一个简单的解决方案是为您添加的每个参数使用值列表,然后在执行之前设置为这些参数收集的值:
StringBuilder sb = new StringBuilder("select * from business where 1=1 ");
List<String> parameters = new ArrayList<>();
if (businessName != null) {
sb.append("and businessName like '%' || ? || '%' ");
parameters.add(businessName);
}
if (businessAddress != null) {
sb.append("and businessAddress like '%' || ? || '%' ");
parameters.add(businessAddress)
}
try (Connection con = DBUtil.getConnection();
PreparedStatement pst = con.prepareStatement(sb.toString())) {
int index = 1;
for (String parameter : parameters) {
pst.setString(index++, parameter);
}
try (ResultSet rs = pst.executeQuery()) {
// ...
}
}
如果您有不同类型的参数,请改用 and。List<Object>
setObject
MT0 答案中的解决方案也有效,但并非所有数据库系统都能很好地优化该类型的查询(特别是如果您有很多此类条件),这可能会影响性能。仅在少数情况下,MT0 的解决方案更具可读性,同时具有相同/相似的性能。
评论
'%' || ? || '%'
?
||
||
+
CONCAT
"%" + someVariable + "%"
?
除了 Mark Rotteveel 的回答之外,我认为还有其他方法可以解决您的问题。
1.转义用户的输入字符串,避免SQL注入。但这种方式并不是 100% 安全的。您可以使用 ESAPI 执行此操作,请参阅SQL_Injection_Prevention_Cheat_Sheet。
2.你可以使用 ORM 框架,比如 MyBatis。例如,MyBatis 有 dynamic-sql,它允许你根据不同的条件生成不同的 sql。此外,不要使用,不要将 sql 注入拒之门外。#{xxx}
${xxx}
评论