Springboot+JdbcTemplate模拟SQL注入攻击案例及解决方法

这篇具有很好参考价值的文章主要介绍了Springboot+JdbcTemplate模拟SQL注入攻击案例及解决方法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

说明

SQL注入是软件开发项目测试过程中必测项,重要等级极高。本文以springboot项目为例,模拟含有SQL注入攻击,并提供解决方法。部分内容整理自网络。

搭建项目

1.创建表tbuser

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');

2.创建工程

  • pom.xml
<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <!-- fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.47</version>
        </dependency>
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.2.1</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
  • application.yml
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;
    }
}
  • DAO接口实现类
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;
    }
}

  • service接口实现
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);
    }
}
  • controller
@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());
    }
}

SQL注入测试

可以看到明明只查admin,拼接后返回了所有用户信息,造成用户信息泄露!!!
spring sql注入,数据库,# Spring-Boot,# 安全问题,spring boot,sql,java,sql注入,安全测试
spring sql注入,数据库,# Spring-Boot,# 安全问题,spring boot,sql,java,sql注入,安全测试

解决方法

方式1:绑定变量

采用预编译绑定变量方式,避免SQL拼接。

String sql="select * from tbuser where username =:name";
Map<String, Object> param = new HashMap<>();
param.put("name",name);

方式2:全局过滤器

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("<script>(.*?)</script>", Pattern.CASE_INSENSITIVE),
            Pattern.compile("src[\r\n]*=[\r\n]*\\\'(.*?)\\\'", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
            Pattern.compile("</script>", Pattern.CASE_INSENSITIVE),
            Pattern.compile("<script(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
            Pattern.compile("eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
            Pattern.compile("expression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
            Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE),
            Pattern.compile("vbscript:", Pattern.CASE_INSENSITIVE),
            Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL)
    };

    public XssHttpServletRequestWrapper() {
        super(null);
    }

    /**
     * 构造函数 - 获取post请求体
     * @param httpservletrequest
     * @throws IOException
     */
    public XssHttpServletRequestWrapper(HttpServletRequest httpservletrequest) throws IOException {
        super(httpservletrequest);
        String sessionStream = getBodyString(httpservletrequest);
        body = sessionStream.getBytes(StandardCharsets.UTF_8);
    }

    /**
     * 读取post请求体
     * @param httpservletrequest
     * @return
     * @throws IOException
     */
    private String getBodyString(HttpServletRequest httpservletrequest) throws IOException {
        StringBuilder sb = new StringBuilder();
        InputStream ins = httpservletrequest.getInputStream();
        boolean isMultipartContent = ServletFileUpload.isMultipartContent(httpservletrequest);
        CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver(httpservletrequest.getSession().getServletContext());
        boolean isMultipart = commonsMultipartResolver.isMultipart(httpservletrequest);
        if (isMultipartContent || isMultipart) {
            fileUpload = true;
        }
        try (BufferedReader isr = new BufferedReader(new InputStreamReader(ins, StandardCharsets.UTF_8));) {
            String line = "";
            while ((line = isr.readLine()) != null) {
                sb.append(line);
            }
        } catch (IOException e) {
            throw e;
        }
        return sb.toString();
    }

    /**
     * 过滤springmvc中的 @RequestParam 注解中的参数
     * @param s
     * @return
     */
    @Override
    public String[] getParameterValues(String s) {
        String[] str = super.getParameterValues(s);
        if (str == null) {
            return null;
        }
        int i = str.length;
        String[] as1 = new String[i];
        for (int j = 0; j < i; j++) {
            as1[j] = cleanXSS(cleanSQLInject(str[j]));
        }
        log.info("XssHttpServletRequestWrapper净化后的请求为:========== {}", Arrays.toString(as1));
        return as1;
    }

    /**
     * 过滤request.getParameter的参数
     * @param s
     * @return
     */
    @Override
    public String getParameter(String s) {
        String s1 = super.getParameter(s);
        if (s1 == null) {
            return null;
        } else {
            String s2 = cleanXSS(cleanSQLInject(s1));
            log.info("XssHttpServletRequestWrapper净化后的请求为:========== {}",  s2);
            return s2;
        }
    }


    /**
     * 过滤请求体 json 格式的
     * @return
     * @throws IOException
     */
    @Override
    public ServletInputStream getInputStream() throws IOException {
        // 非文件上传进行过滤
        if (!fileUpload) {
            // 获取body中的请求参数
            JSONObject json = JSONObject.parseObject(new String(body));
            // 校验并过滤xss攻击和sql注入
            for (String k : json.keySet()) {
                cleanSQLInject(cleanXSS(json.getString(k)));
            }
        }
        // 将请求体参数流转 -- 流读取一次就会消失,所以我们事先读取之后就存在byte数组里边方便流转
        final ByteArrayInputStream bais = new ByteArrayInputStream(body);
        return new ServletInputStream() {

            @Override
            public int read() throws IOException {
                return bais.read();
            }

            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener readListener) {
            }
        };
    }

    /**
     * 清除xss
     * @param src 单个参数
     * @return
     */
    public String cleanXSS(String src) {
        String temp = src;
        // 校验xss脚本
        for (Pattern pattern : scriptPatterns) {
            temp = pattern.matcher(temp).replaceAll("");
        }
        // 校验xss特殊字符
        temp = temp.replaceAll("\0|\n|\r", "");
        temp = temp.replaceAll("<", "&lt;").replaceAll(">", "&gt;");

        if (!temp.equals(src)) {

            log.error("xss攻击检查:参数含有非法攻击字符,已禁止继续访问!!");
            log.error("原始输入信息-->" + temp);

            throw new RuntimeException("xss攻击检查:参数含有非法攻击字符,已禁止继续访问!!");
        }

        return src;
    }

    /**
     * 过滤sql注入 -- 需要增加通配,过滤大小写组合
     * @param src 单个参数值
     * @return
     */
    public String cleanSQLInject(String src) {
        // 非法sql注入正则
        Pattern sqlPattern = Pattern.compile(badStrReg, Pattern.CASE_INSENSITIVE);
        if (sqlPattern.matcher(src.toLowerCase()).find()) {
            log.error("sql注入检查:输入信息存在SQL攻击!");
            throw new RuntimeException("sql注入检查:参数含有非法攻击字符,已禁止继续访问!!");
        }
        return src;
    }

}

package com.demo.jdbcinject.config;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @Author laoxu
 * @Date 2023/3/15 23:22
 * @Desc xxx
 */
@WebFilter(filterName = "xssFilter", urlPatterns = "/*", asyncSupported = true)
public class XSSFilter implements Filter {
    /**
     * 忽略权限检查的url地址
     */
    private final String[] excludeUrls = new String[]{
            "/login.html"
    };

    @Override
    public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2)
            throws IOException, ServletException {

        HttpServletRequest req = (HttpServletRequest) arg0;
        HttpServletResponse response = (HttpServletResponse) arg1;
        //获取请求你ip后的全部路径
        String uri = req.getRequestURI();
        //跳过不需要的Xss校验的地址
        for (String str : excludeUrls) {
            if (uri.contains(str)) {
                arg2.doFilter(arg0, response);
                return;
            }
        }
        //注入xss过滤器实例
        XssHttpServletRequestWrapper reqW = new XssHttpServletRequestWrapper(req);
        //过滤
        arg2.doFilter(reqW, response);
    }

    @Override
    public void destroy() {
    }

    @Override
    public void init(FilterConfig filterConfig1) throws ServletException {
    }

}

  • 启动类注解
@SpringBootApplication
@ServletComponentScan(basePackages = {"com.demo.jdbcinject.config"})
public class WebApplication {

    public static void main(String[] args) {
        SpringApplication.run(WebApplication.class, args);
    }

}

采用预编译接口测试

可以看到无论前端传入什么参数,后端采用绑定变量方式不会返回任何内容。
spring sql注入,数据库,# Spring-Boot,# 安全问题,spring boot,sql,java,sql注入,安全测试

spring sql注入,数据库,# Spring-Boot,# 安全问题,spring boot,sql,java,sql注入,安全测试

采用全局过滤器方式后测试

添加过滤器后再次访问,直接报错!!!
spring sql注入,数据库,# Spring-Boot,# 安全问题,spring boot,sql,java,sql注入,安全测试文章来源地址https://www.toymoban.com/news/detail-598454.html

到了这里,关于Springboot+JdbcTemplate模拟SQL注入攻击案例及解决方法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • SQL注入攻击与防御

    一、SQL注入原理及危害         SQL注入漏洞是指攻击者通过浏览器或者其他客户端将恶意SQL语句插入到网站参数中,而网站应用程序未对其进行过滤,将恶意SQL语句带入数据库使恶意SQL语句得以执行,从而使攻击者通过数据库获取敏感信息或者执行其他恶意操作。     

    2023年04月15日
    浏览(80)
  • SQL 注入漏洞攻击

    假设你用自己的用户名和密码登录了一个付费网站,网站服务器就会查询一下你是不是 VIP 用户,而用户数据都是放在数据库中的,服务器通常都会向数据库进行查询,要向数据库进行增删改查操作,就需要用到 SQL 语言。 但是,作为 SQL 的注入攻击者,我们并不知道网站的密

    2024年02月09日
    浏览(53)
  • SQL注入攻击方法

    SQL注入攻击是一种利用Web应用程序中存在的安全漏洞,通过在输入框中插入恶意的SQL代码,从而实现对数据库的非法操作。以下是一些常见的SQL注入攻击方法: 使用单引号(\\\')进行字符串拼接:在输入框中插入带有单引号的字符串,使得原始SQL语句结构发生变化,从而实现

    2024年02月03日
    浏览(44)
  • SQL注入攻击原理 实战

    我来进行实战了,总在看教程。 提示:这里可以添加本文要记录的大概内容: 前言,前言,前言(挠头)啊,我终于打实战了 提示:以下是本篇文章正文内容,下面案例可供参考 看这样子,应该是不存在的 如果没有就继续吧, 哎,你看它变了, 又回来了,没有那就试试这个

    2024年02月12日
    浏览(47)
  • 【SQL注入攻击介绍】

    目录 前言 本质和危害  分类 注入一般步骤 注入实战   sql注入一直以来都稳居owasp-top10榜首,近年来更是爆出很多的数据库泄露攻击事件,如最近上海某公安存在数据库泄露事件。今天简单的分析以下sql注入的一些特性和方式:   owasp-top10   一、sql注入的危害包括但不局限

    2024年02月06日
    浏览(44)
  • 网络安全---SQL注入攻击

            SQL 注入是一种代码注入技术,可利用 Web 应用程序和数据库服务器之间接口中的漏洞。当用户的输入在发送到后端数据库服务器之前未在 Web 应用程序中正确检查时,该漏洞就存在。         许多 Web 应用程序从用户处获取输入,然后使用这些输入构建 SQL 查询

    2024年04月12日
    浏览(50)
  • 一文搞懂SQL注入攻击

    随着互联网的发展和普及,网络安全问题越来越突出,网络在为用户提供越来越多服务的同时,也要面对各类越来越复杂的恶意攻击。SQL注入(SQL Injection)攻击是其中最普遍的安全隐患之一,它利用应用程序对用户输入数据的信任,将恶意SQL代码注入到应用程序中,从而执行

    2023年04月09日
    浏览(36)
  • SQL注入漏洞复现:探索不同类型的注入攻击方法

    这篇文章旨在用于网络安全学习,请勿进行任何非法行为,否则后果自负。  准备环境 sqlilabs靶场 安装:Sqli-labs靶场搭建(适合新手小白围观)_sqlilabs靶场搭建-CSDN博客   注入讲解 介绍         基于错误的注入(Error-based Injection)是一种 利用应用程序返回的错误信息来

    2024年02月11日
    浏览(44)
  • SQL注入进阶:掌握联合查询注入和报错注入攻击技巧

    数据来源         本文仅用于信息安全的学习,请遵守相关法律法规,严禁用于非法途径。若观众因此作出任何危害网络安全的行为,后果自负,与本人无关。 介绍         联合查询注入是SQL注入的一种,通过在原有的SQL语句中添加UNION(联合)操作,将恶意构造的

    2024年02月06日
    浏览(54)
  • SQL注入攻击实战演示(附源码)

    SQL注入是一种非常常见的数据库攻击手段,SQL注入漏洞也是网络世界中最普遍的漏洞之一。大家也许都听过某某学长通过攻击学校数据库修改自己成绩的事情,这些学长们一般用的就是SQL注入方法。 文章目录: 何谓SQL注入? SQL数据库操作示例 SQL数据库注入示例 如何防止S

    2024年02月13日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包