SQL注入是软件开发项目测试过程中必测项,重要等级极高。本文以springboot项目为例,模拟含有SQL注入攻击,并提供解决方法。部分内容整理自网络。
DROP TABLE IF EXISTS `tbuser`;
CREATE TABLE `tbuser` (
`username` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of tbuser
-- ----------------------------
INSERT INTO `tbuser` VALUES ('admin');
INSERT INTO `tbuser` VALUES ('zhangsan');
INSERT INTO `tbuser` VALUES ('lisi');
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<scope>runtimescope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-jdbcartifactId>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
<version>1.2.47version>
dependency>
<dependency>
<groupId>commons-fileuploadgroupId>
<artifactId>commons-fileuploadartifactId>
<version>1.2.1version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&serverTimezone=GMT&useSSL=false
password: root123
server:
port: 8081
logging:
level:
org.springframework.jdbc.core.JdbcTemplate: DEBUG
public class User {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public interface UserDao {
public List<User> findUser(String name);
public List<User> findUserSec(String name);
}
@Repository
public class UserDaoImpl implements UserDao {
@Autowired
private NamedParameterJdbcTemplate jdbcTemplate;
/**
* 字符串拼接方式,有注入漏洞
* @param name
* @return
*/
@Override
public List<User> findUser(String name) {
List<User> myUserList= new ArrayList<>();
String sql="select * from tbuser where username ='"+name+"'";
Map<String, Object> param = new HashMap<>();
List<Map<String, Object>> mapList=new ArrayList<>();
mapList=jdbcTemplate.queryForList(sql,param);
for(int i=0;i<mapList.size();i++){
Map<String,Object> testmap= mapList.get(i);
User myuser=new User();
myuser.setName((String) testmap.get("username"));
myUserList.add(myuser);
}
return myUserList;
}
/**
* 预编译方式,执行会报错
* @param name
* @return
*/
@Override
public List<User> findUserSec(String name) {
List<User> myUserList= new ArrayList<>();
String sql="select * from tbuser where username =:name";
Map<String, Object> param = new HashMap<>();
param.put("name",name);
List<Map<String, Object>> mapList=new ArrayList<>();
mapList=jdbcTemplate.queryForList(sql,param);
for(int i=0;i<mapList.size();i++){
Map<String,Object> testmap= mapList.get(i);
User myuser=new User();
myuser.setName((String) testmap.get("username"));
myUserList.add(myuser);
}
return myUserList;
}
}
public interface UserService {
public List<User> findUser(String name);
public List<User> findUserSec(String name);
}
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public List<User> findUser(String name) {
return userDao.findUser(name);
}
@Override
public List<User> findUserSec(String name) {
return userDao.findUserSec(name);
}
}
@RestController
public class UserController {
@Autowired
private UserService userService;
@PostMapping("/user")
public List<User> findUser(@RequestBody User user){
return userService.findUser(user.getName());
}
@PostMapping("/usersec")
public List<User> findUserSec(@RequestBody User user){
return userService.findUserSec(user.getName());
}
}
可以看到明明只查admin,拼接后返回了所有用户信息,造成用户信息泄露!!!
采用预编译绑定变量方式,避免SQL拼接。
String sql="select * from tbuser where username =:name";
Map<String, Object> param = new HashMap<>();
param.put("name",name);
package com.demo.jdbcinject.config;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.http.fileupload.servlet.ServletFileUpload;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.regex.Pattern;
/**
* @Author laoxu
* @Date 2023/3/15 23:09
* @Desc xxx
*/
@Slf4j
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
/**
* post请求体
*/
private byte[] body;
/**
* 是否是文件上传
*/
private boolean fileUpload = false;
/**
* sql注入正则
*/
private static String badStrReg =
"\\b(and|or)\\b.{1,6}?(=|>|<|\\bin\\b|\\blike\\b)|\\/\\*.+?\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT|UPDATE.+?SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE).+?FROM|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)";
/**
* xss脚本正则
*/
private final static Pattern[] scriptPatterns = {
Pattern.compile("", Pattern.CASE_INSENSITIVE),
Pattern.compile("src[\r\n]*=[\r\n]*\\\'(.*?)\\\'", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
Pattern.compile("", Pattern.CASE_INSENSITIVE),
Pattern.compile("