[英]How can I modify the code to avoid SQL-injection attack?
I need to implement a fuzzy search for two fields, businessName
and businessAddress
.我需要对两个字段
businessName
和businessAddress
实现模糊搜索。 Both of them can be null
.它们都可以是
null
。 If one field is null
, search should be based on the other field.如果一个字段是
null
,则应基于另一字段进行搜索。
To be specific,再具体一点,
businessName="name"
and businessAddress="address"
then execute select * from business where businessName like '%name%' and businessAddress like '%address%'
businessName="name"
和businessAddress="address"
然后执行select * from business where businessName like '%name%' and businessAddress like '%address%'
businessName=null
and businessAddress="address"
then execute select * from business where businessAddress like '%address%'
businessName=null
和businessAddress="address"
然后执行select * from business where businessAddress like '%address%'
businessName=null
and businessAddress=null
then execute select * from business
businessName=null
和businessAddress=null
然后select * from business
My code:我的代码:
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();
} ...
Apparently it's in danger of SQL-injection attack.显然它有受到 SQL 注入攻击的危险。 I know method
prepareStatement.setString()
can avoid attack, but number of fields is uncertain before verification.我知道方法
prepareStatement.setString()
可以避免攻击,但是在验证之前字段的数量是不确定的。
How can I modify it?我该如何修改它? Separate method for each case or code like below seem ugly.
每个案例的单独方法或如下代码看起来很难看。
if(businessName!=null){
if(businessAddress!=null){
sql = ...;
}else {
sql = ...;
}
else{
...
Never, ever, concatenate values into a query string like that.永远不要将值连接到这样的查询字符串中。 Always use prepared statements with parameters when executing queries, especially with user-sourced values.
执行查询时,始终使用带参数的预处理语句,尤其是使用用户来源的值。
A simple solution for your case is to use a list of values for each parameter you add, and then set the values collected for those parameters before execute:对于您的情况,一个简单的解决方案是为您添加的每个参数使用值列表,然后在执行之前设置为这些参数收集的值:
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()) {
// ...
}
}
If you have parameters of varying types, use a List<Object>
and setObject
instead.如果您有不同类型的参数,请改用
List<Object>
和setObject
。
The solution in the answer by MT0 also works, but not all database systems optimize that type of query well (especially if you have a lot of such conditions), which might affect performance. MT0 答案中的解决方案也有效,但并非所有数据库系统都能很好地优化该类型的查询(特别是如果您有很多这样的条件),这可能会影响性能。 For only a few conditions, the solution by MT0 is more readable, while having same/similar performance.
仅在少数情况下,MT0 的解决方案更具可读性,同时具有相同/相似的性能。
You do not need dynamic SQL and can use bind variables in the query:您不需要动态 SQL 并且可以在查询中使用绑定变量:
String query = "select * from business where businessName LIKE '%' || ? || '%' AND businessAddress LIKE '%' || ? || '%'";
(Assuming ||
is the string concatenation operator for your SQL dialect.) (假设
||
是您的 SQL 方言的字符串连接运算符。)
And then use a prepared statement and bind businessName
and businessAddress
to the variables.然后使用准备好的语句并将
businessName
和businessAddress
绑定到变量。
PreparedStatement st = con.prepareStatement(query);
st.setString(1,businessName);
st.setString(2,businessAddress);
ResultSet rs = st.executeQuery();
(Add exception handling.) (添加异常处理。)
Or, if your SQL dialect requires you to handle NULL
separately from the LIKE
then:或者,如果您的 SQL 方言要求您将
NULL
与LIKE
分开处理,则:
String query = "select * from business
where (:name IS NULL OR businessName LIKE '%' || :name || '%')
AND (:addr IS NULL OR businessAddress LIKE '%' || :addr || '%')";
and use named bind variables :name
and :addr
(or use ?
pass the values twice).并使用命名绑定变量
:name
和:addr
(或使用?
传递值两次)。
Except Mark Rotteveel' answer, I think there are 2 additional methods to solve your question.除了 Mark Rotteveel 的回答,我认为还有另外两种方法可以解决您的问题。
Escape user's input string to avoid sql-injection.转义用户的输入字符串以避免 sql 注入。 But this way is not 100% secure .You can use ESAPI to do this, please refer to SQL_Injection_Prevention_Cheat_Sheet .
但是这种方式不是 100% 安全的。您可以使用 ESAPI 来执行此操作,请参阅SQL_Injection_Prevention_Cheat_Sheet 。
You can use ORM framework,like MyBatis.您可以使用 ORM 框架,如 MyBatis。 For example, MyBatis have dynamic-sql ,it allows you generate different sql according to different conditions.
比如MyBatis有dynamic-sql ,它可以让你根据不同的条件生成不同的sql。 In addition, use
#{xxx}
not ${xxx}
to keep sql-injection away.此外,使用
#{xxx}
而不是${xxx}
来防止 sql 注入。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.