Java安全_SQL注入
[!NOTE]
本次学习使用开源项目:
https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/controller/SQLI.java使用工具:
浏览器IDEA
什么是 SQL 注入
SQL 注入(SQL Injection) 是 JavaWeb 应用中最常见、最严重的安全漏洞之一。它指的是攻击者将恶意 SQL 语句作为用户输入传入系统,并被后台拼接执行,从而操控数据库。
在 JavaWeb 项目中,最容易出现 SQL 注入的地方是:
- 登录验证
- 搜索查询
- 数据过滤接口
- URL 参数拼接数据库语句
如下面的代码
Statement statement = con.createStatement();
String sql = "select * from users where username = '" + username + "'";
logger.info(sql);
ResultSet rs = statement.executeQuery(sql);
如果攻击者输入:
- 用户名:
username = ' OR '1'='1
那么 SQL 就变成:
select * from users where username = '' OR '1'='1'
那么条件判断永远为真,就能达到绕过的目的
JDBC 模式下的 SQL 注入
1、最原始的拼接注入
关键 Code
public String jdbc_sqli_vul( String username) {
StringBuilder result = new StringBuilder();
try {
Class.forName(driver);
Connection con = DriverManager.getConnection(url, user, password);
if (!con.isClosed())
System.out.println("Connect to database successfully.");
// sqli vuln code
Statement statement = con.createStatement();
String sql = "select * from users where username = '" + username + "'";
logger.info(sql);
ResultSet rs = statement.executeQuery(sql);
while (rs.next()) {
String res_name = rs.getString("username");
String res_pwd = rs.getString("password");
String info = String.format("%s: %s\n", res_name, res_pwd);
result.append(info);
logger.info(info);
}
rs.close();
con.close();
} catch (ClassNotFoundException e) {
logger.error("Sorry,can`t find the Driver!");
} catch (SQLException e) {
logger.error(e.toString());
}
return result.toString();
}
可以看到接受 Get 参数之后,直接进行了 SQL 语句拼接处理
下面为漏洞关键代码
Statement statement = con.createStatement();
String sql = "select * from users where username = '" + username + "'";
logger.info(sql);
ResultSet rs = statement.executeQuery(sql);
构造 Payload
http://127.0.0.1:8081/sqli/jdbc/vuln?username=joychou' or '1'='1
通过打断点查看,发现最终执行的 SQL 语句变成了 select * from users where username = 'joychou' or '1'='1',从而达到绕过的目的

修复方式
通过参数化查询+预编译,可以达到避免 SQL 注入的目的,代码示例如下:
String sql = "select * from users where username = ?";
PreparedStatement st = con.prepareStatement(sql);
st.setString(1, username);
ResultSet rs = st.executeQuery();
PreparedStatement 是 Java JDBC 提供的预编译 SQL 语句接口,它允许开发者在 SQL 语句中使用占位符 ?,然后通过绑定参数的方式传入用户数据。数据库在执行时会先对 SQL 语句结构进行编译,之后再将参数作为数据绑定进去,保证了输入内容不会被当作 SQL 代码执行。
这种机制有效防止了 SQL 注入攻击,因为即便用户输入中包含恶意的 SQL 片段,也不会被当作语句解析执行,而只会作为普通字符串处理,从而提高了系统的安全性和稳定性。此外,PreparedStatement 还可以提升执行效率,尤其是对相同语句多次执行时,可以复用预编译结果,减少数据库负担。
在使用参数化查询+预编译后,在对代码进行调试,查看最终执行的 SQL 语句
可以发现,最终执行的语句变成了 select * from users where username = 'joychou\' or \'1\'=\'1'
在这里,预编译机制会将用户输入作为 纯字符串值 处理,自动对特殊字符(如单引号 ')进行转义,因此即使输入中包含类似 ' or '1'='1 的恶意内容,也不会改变 SQL 语句的逻辑结构。从而达到了防止 SQL 注入的目的

Mybatis模式下的SQL注入
Mybatis下SQL的写法
Mybatis有两种SQL语句写法:
使用注解
@Select("select * from users where username = #{username}")
User findByUserName(@Param("username") String username);
使用XML文件映射
<select id="findByUserNameVsec02" parameterType="String" resultMap="User">
select * from users where username like concat('%',#{_parameter}, '%')
</select>
关于Mybatis下SQL的两种拼接方法
Mybatis中存在两种SQL语句拼接方法:
- ${xxxxxx} 直接拼接,可以理解为上面的JDBC最原始的拼接 ,是一种不安全的方法
- #{xxxxxx} 预处理后拼接,是一种安全的方法
在 MyBatis 中,${} 和 #{} 的区别非常关键,它们直接影响到 SQL 是否存在注入风险:
${}是字符串替换,会在 SQL 拼接阶段将参数原样插入 SQL 中,不经过任何预处理或转义。如果用户输入中包含恶意 SQL 片段,就可能被拼接进最终 SQL 并执行,存在严重的 SQL 注入风险。例如:
<select id="findUser" resultType="User">
select * from users where username = '${username}'
</select>
如果用户输入是 joychou' or '1'='1,最终 SQL 将变为:
select * from users where username = 'joychou' or '1'='1'
那么体现在SQL语句上,这两种方法有什么区别呢?
#{}会给传入的值自动加上单引号
如select * from #{param} ,如给param传入值为xxxx时,SQL会被拼接成 select * from 'xxxx' ,这是因为#{}自动给拼接上的参数加上了单引号
${}就是什么都不变的直接拼接了
如 select * from ${param} ,如给param传入值为xxxx时,SQL会被拼接成 select * from xxxx ,可以注意这里并没有单引号,因为${}是直接拼接
这就给Mybatis下的SQL注入带来了可乘之机
在SQL语句中存在很多不能包含单引号的情况,例如:被拼接的参数是表名、数据库名、字段名等 , 这一情况在 order by 中尤为常见
能否使用#{}的总结表格:
| 场景 | 能否使用 #{} |
是否建议用 ${} |
风险说明 |
|---|---|---|---|
| 表名 | 否 | 可用(需白名单) | 否则无法拼接 SQL |
| 列名 / 字段名 | 否 | 可用(需白名单) | 常用于排序、动态列选择 |
| 排序方向(ASC/DESC) | 否 | 可用(需白名单) | 否则 SQL 报错 |
| SQL 关键字 | 否 | 可用(需白名单) | 如运算符、动态语法结构 |
| LIMIT / OFFSET | 一般 可用 | 可用(需数值校验) | 数据库版本不同兼容性需注意 |
| 数据库函数名 | 否 | 可用(需白名单) | 如 count, sum 可拼接但需控制 |
因此,在这种不得不使用${}的情况下,需要格外注意对参数的过滤,如下面的代码
//使用了过滤器的order by执行语句
@GetMapping("/mybatis/orderby/sec04")
public List<User> mybatisOrderBySec04(@RequestParam("sort") String sort) {
String filter_order = SecurityUtil.sqlFilter(sort);
return userMapper.findByUserNameVuln03(filter_order);
}
//过滤器
private static final Pattern FILTER_PATTERN = Pattern.compile("^[a-zA-Z0-9_/\\.-]+$");
public static String sqlFilter(String sql) {
if (!FILTER_PATTERN.matcher(sql).matches()) {
return null;
}
return sql;
}
Java安全_SQL注入的更多相关文章
- Java Web表达式注入
原文:http://netsecurity.51cto.com/art/201407/444548.htm 0×00 引言 在2014年6月18日@终极修炼师曾发布这样一条微博: 链接的内容是一个名为 ...
- spring注解方式在一个普通的java类里面注入dao
spring注解方式在一个普通的java类里面注入dao @Repositorypublic class BaseDaoImpl implements BaseDao {这是我的dao如果在servi ...
- ref:一种新的攻击方法——Java Web表达式注入
ref:https://blog.csdn.net/kk_gods/article/details/51840683 一种新的攻击方法——Java Web表达式注入 2016年07月06日 17:01 ...
- Java防止SQL注入的几个途径
java防SQL注入,最简单的办法是杜绝SQL拼接,SQL注入攻击能得逞是因为在原有SQL语句中加入了新的逻辑,如果使用 PreparedStatement来代替Statement来执行SQL语句,其 ...
- java用Annotation注入到成员Bean对象
java用Annotation注入到成员Bean对象 在使用一些java框架的时候,经常看到一些注解,而且使用注解之后就可以免去一些xml的繁琐配置,本文记录如何通过注解获得成员Bean对象. 一.首 ...
- Java之JNDI注入
Java之JNDI注入 About JNDI 0x01 简介 JNDI(Java Naming and Directory Interface)是SUN公司提供的一种标准的Java命名系统接口,JND ...
- Java防止SQL注入2(通过filter过滤器功能进行拦截)
首先说明一点,这个过滤器拦截其实是不靠谱的,比如说我的一篇文章是介绍sql注入的,或者评论的内容是有关sql的,那会过滤掉:且如果每个页面都经过这个过滤器,那么效率也是非常低的. 如果是要SQL注入拦 ...
- Java防止SQL注入(转)
一.SQL注入简介 SQL注入是比较常见的网络攻击方式之一,它不是利用操作系统的BUG来实现攻击,而是针对程序员编程时的疏忽,通过SQL语句,实现无帐号登录,甚至篡改数据库. 二.SQL注入攻击的总体 ...
- 如何在Java Filter 中注入 Service
在项目中遇到一个问题,在 Filter中注入 Serivce失败,注入的service始终为null.如下所示: public class WeiXinFilter implements Filter ...
- java web sql注入测试(1)---概念概述
在进行java web 测试时,经常会忽略的测试种类就是sql注入测试,这类缺陷造成的原因是开发技术在这方面欠缺的表现,虽然不常见,但一旦有这类缺陷,就很因此对运营的数据造成很多不必要的损失,所以,还 ...
随机推荐
- 解决Dcat Admin laravel框架登录报错问题,(blocked:mixed-content)
前言 在使用 Dcat Admin 后台登录时,发生 error 报错:(blocked:mixed-content) xhr VM484:1,浏览器拦截 其实这是浏览器在 HTTPS 页面中尝试加载 ...
- docker Get "https://registry-1.docker.io/v2/": x509: certificate is valid for
前言 docker 在进行 build 时,报错:Get "https://registry-1.docker.io/v2/": x509: certificate is vali ...
- 解决nvm ls-remote 列表只出现iojs版本
前言 在 nvm 安装 node 时发现显示不存在此版本,使用 nvm ls-remote 查看可安装列表时发现,列表中只有 iojs $ nvm ls-remote iojs-v1.0.0 iojs ...
- Containerd 配置使用 Nvidia container runtime
前言 Kubernetes 集群中 Docker 如何使用 GPU,请看这一篇 docker配置Nvidia环境,使用GPU 本文着重讲 Containerd 如何作为容器运行时来使用 GPU CRI ...
- 一文彻底搞清楚ArkUI
程序员Feri一名12年+的程序员,做过开发带过团队创过业,擅长Java相关开发.鸿蒙开发.人工智能等,专注于程序员搞钱那点儿事,希望在搞钱的路上有你相伴!君志所向,一往无前! 0.前言 在移动开发领 ...
- CSAPP学习笔记——Chapter12 并行编程
CSAPP学习笔记--Chapter12 并行编程 并发编程有着其独特的魅力,之前接触cuda编程的时候,感受到一些,没想到书里还有相关的内容.今天我们主要围绕进程,I/O多路复用,线程三种并发的方式 ...
- dify MCP工具调用
一.概述 前面几篇文章,介绍了Cherry Studio客户端调用MCP,接下来介绍dify如何调用MCP 二.dify插件 需要安装2个插件,分别是:Agent 策略(支持 MCP 工具),MCP ...
- Linux四剑客grep、find、sed、awk使用
介绍 Linux四剑客是指在Linux系统中非常常用的四个命令工具,它们分别是grep.find.sed和awk.这四个工具在Linux系统中具有非常强大的功能,可以方便快捷地对文本进行搜索.处理 ...
- 题解:UVA11214 守卫键盘 Guarding the Chessboard
题意:输入一个 n×mn\times mn×m 棋盘,某些格子有标记.用最少的皇后守卫(即占据或者攻击)所有带标记的格子. 分析:因为不知道放几个皇后可以守卫所有带标记的格子,即回溯法求解时解答树的深 ...
- 27.4K Star!这个LLM应用宝库让你秒变AI全栈高手,RAG和AI Agent一网打尽!
嗨,大家好,我是小华同学,关注我们获得"最新.最全.最优质"开源项目和高效工作学习方法 想要快速入门LLM应用开发?想要了解最新的RAG和AI Agent技术?这个收获27.4K ...