【java】API接口防重放机制研究
1、接口重放的定义
接口重放是一种常见的安全需求,特别是api接口在网络通信中,攻击者捕捉并重放发送有效的请求,进行探测,分析 从而获取可利用的信息,进一步进行攻击, 达到非法目的。如何防止重复提交请求、减少接口攻击风险,在实际的开发中需确保每个请求的唯一性和时效性。以下是通过研究常见的几种防止接口重放攻击策略
2、解决措施
通过引入时间戳 timestamp 、和唯一随机数 nonce one-time-number 可以确保每个请求在服务器端具有唯一性
2.1时间戳
通过引入时间戳 timestamp记录web端当前触发接口的开始时间,服务器端可以验证特定时间范围内的运行的请求
2.2唯一随机数
每个请求都包含一个唯一的,不可预测的值,服务器保存已经处理过的nonce 确保每个请求的唯一性
2.3签名验签
通过签名机制,服务器校验来自客户端的请求是否被篡改,从而判断其合法性
3、验证实验
3.1环境
本套测试使用 java+springboot+jsp的整合研究(应用程序搭建自行部署)
3.2思考
3.2.1生成时间戳timestamp和nonce
当客户端发送请求时,请求接口自带当前的时间戳和唯一的随机生成的nonce
3.2.2如何生成
后端生成通过 model视图返回前端表单、接口发送请求携带此参数即可
3.3具体实现
3.3.1前端jsp代码加上 timestamp和nonce
<%@ page contentType="text/html; charset=UTF-8" language="java" %>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>登录</title>
</head>
<body>
<h2>登录</h2>
<form action="/nonce" method="POST">
<label for="username">用户名:</label>
<input type="text" id="username" name="username" required/><br/>
<label for="password">密码:</label>
<input type="password" id="password" name="password" required/><br/>
<!-- 加入时间戳和Nonce -->
<input type="hidden" name="timestamp" value="${timestamp}"/>
<input type="hidden" name="nonce" value="${nonce}"/>
<button type="submit">登录</button>
</form>
<p style="color:red">时间戳${timestamp}</p >
<p style="color:red">一次性校验码${nonce}</p >
<p style="color:red">${error}</p >
<p style="color:darkmagenta">${success}</p >
</body>
</html>
控制层用户接收来自客户端的请求
通过 model.addAttribute("timestamp", timestamp); 和 model.addAttribute("nonce", nonce); 将生成的时间戳和随机 nonce 传递到 JSP 页面,以便在客户端使用
@Controller
public class NonceContrller {
private static final Set<String> usedNonces = new HashSet<>();
@GetMapping("/nonce")
public String showNonce(Model model) {
// 生成一个时间戳和一个随机的 nonce
long timestamp = System.currentTimeMillis();
String nonce = UUID.randomUUID().toString();
// 将时间戳和nonce传递到JSP页面
model.addAttribute("timestamp", timestamp);
model.addAttribute("nonce", nonce);
System.out.println(nonce);
return "nonce"; // 返回login.jsp视图
}
3.3.2时间戳回显
启动spingboot 项目访问 http://192.168.3.118:8080/nonce 则出现一下内容 界面的时间戳和一次性校验码则返回前端
接口值携带时间戳和一次性校验码
后端代码校验
通过启动springboot服务器, 向服务器发送请求登录
提交form表单,信息内容提交至服务器端 服务器端获取来自前端的参数进行判断
其中
final long MAX_TIMESTAMP_DIFF1 = 15 * 1000; // 15秒 时间戳15s后过期
usedNonces.contains(nonce)判断来自客户端的nonce值是否被使用
二者均满足则服务器合法处理
@PostMapping("/nonce")
public String handleLogin(@RequestParam String username, @RequestParam String password,
@RequestParam long timestamp, @RequestParam String nonce,
Model model) {
final long MAX_TIMESTAMP_DIFF1 = 15 * 1000; // 15秒
long currentTimestamp = System.currentTimeMillis();
long times = currentTimestamp - timestamp;
System.out.println("时间差: " + times);
// 1. 时间超时校验
if (times > MAX_TIMESTAMP_DIFF1) {
model.addAttribute("error", "时间超时,请重新登录");
return "nonce"; // 返回登录页面
}
// 2. 验证 nonce 是否已使用过
if (usedNonces.contains(nonce)) {
model.addAttribute("error", "请求重复,请重新登录");
return "nonce"; // 返回登录页面
}
// 3. 进行用户身份验证
String sql = "SELECT * FROM users WHERE username = ? AND password = ?";
try (Connection connection = getConnection();
PreparedStatement preparedStatement = connection.prepareStatement(sql)) {
preparedStatement.setString(1, username);
preparedStatement.setString(2, password); // 注意:应使用加密后的密码
ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.next()) {
// 4. 用户存在且密码匹配,nonce 记录到 usedNonces,防止重放
usedNonces.add(nonce);
model.addAttribute("success", "登录成功");
return "home"; // 跳转到首页
} else {
// 5. 登录失败但 nonce 不能存入
model.addAttribute("error", "用户名或密码错误,请重新登录");
// model.addAttribute("refresh", true);
return "redirect:/nonce";
}
} catch (SQLException e) {
e.printStackTrace();
model.addAttribute("error", "系统错误,请稍后重试");
return "nonce";
}
}
超期校验,刷新接口 待生成时间戳 15s后向服务器发送请求则提示超时
重放校验,拦截请求重放发送 nonce使用则提示请求重复
原文地址:https://blog.csdn.net/weixin_43372364/article/details/145299509
免责声明:本站文章内容转载自网络资源,如侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!