小谈时间戳
今天在项目中碰到了时间戳,类似这样的格式yyyyMMddHHmmss,本文就时间戳进行一个简短的总结。
防止浏览器缓存(304)
URL 的末尾追加了时间。这就确保了请求不会在它第一次被发送后即缓存,而是会在此方法每次被调用后重新创建和重发;
此 URL 会由于时间戳的不同而稍微有些不同。
这种技巧常被用于确保到脚本的 POST 每次都会实际生成新请求且 Web 服务器不会尝试缓存来自服务器的响应。
防止重复提交
每次请求时产生一个token(一般为时间戳),存于session中并随之用hidden提交,
在服务端中判断接收到的token和session中的是否一致来判断是否重复提交,
如果不是则重新产生一个 token存于session中覆盖原来的token。
实例代码
index.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme() + "://"
+ request.getServerName() + ":" + request.getServerPort()
+ path + "/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>My JSP 'index.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head>
<body>
<%
long token = System.currentTimeMillis(); //产生时间戳的token
session.setAttribute("token", token);
%>
<form action="Test" method="post">
<input type="text" name="username" /> <input type="text"
name="password" /> <input type="hidden" value="<%=token%>"
name="token" />
<!-- 作为hidden提交 -->
<input type="submit" value="提交" />
</form>
</body>
</html>
Test.java(是一个Servlet)
public class Test extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.doPost(request, response);
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
resp.setCharacterEncoding("utf-8");
resp.setContentType("text/html,charset=utf-8");
String username = req.getParameter("username");
String password = req.getParameter("password");
long token = Long.parseLong(req.getParameter("token"));
long tokenInSession = Long.parseLong(req.getSession().getAttribute(
"token")
+ "");
if (token == tokenInSession) {
resp.getWriter().println("ok ");
// 如果是第一次请求,则产生新的token,以防止下次的重复的提交
req.getSession().setAttribute("token", System.currentTimeMillis());
} else {
resp.getWriter().println("do not repeat submit");
}
}
}
@当用户返回或者刷新重复请求服务端时,服务端判断token是否一致,
由于请求方没有产生新的token,所以和服务端新产生的token不一致,认为重复提交。
@当用户在请求页面刷新也就是重新在请求页面产生token,这时新的token覆盖服务端产生的token,
这时token一致,认为是一个新的请求。
防止请求伪造
如过客户端在向服务端接口进行请求,如果请求信息进行了加密处理,被第三方截取到请求包,
虽然第三方无法解密获取其中的数据,但是可以使用该请求包进行重复的请求操作。
如果服务端不进行防重放攻击,就会参数服务器压力增大,数据紊乱的后果。
而使用添加时间戳的方式可以解决这一问题。
每次HTTP请求,都需要加上timestamp参数,然后把timestamp和其他参数一起进行数字签名。因为一次正常的HTTP请求,从发出到达服务器一般都不会超过60s,所以服务器收到HTTP请求之后,首先判断时间戳参数与当前时间相比较,是否超过了60s,如果超过了则认为是非法的请求。
假如黑客通过抓包得到了我们的请求url:
http://xxx.site/index/Info?uid=ZX07&stime=1480862753&sign=80b886d71449cb33355d017893720666
其中
sign=md5(uid,token,stime);
// token是MD5散列的一个要素,一般保密
一般情况下,黑客从抓包重放请求耗时远远超过了60s,所以此时请求中的stime参数已经失效了。
如果黑客修改stime参数为当前的时间戳,则sign参数对应的数字签名就会失效,因为黑客不知道token值,没有办法生成新的数字签名。
更多关于重放攻击的内容请参考百度百科的解释“重放攻击-百度百科”