重复提交,CSRF,XSS攻击

表单重复提交解决方案(防止Http重复提交

 

网络延时

 在平时开发中,如果网速比较慢的情况下,用户提交表单后,发现服务器半天都没有响应,那么用户可能会以为是自己没有提交表单,就会再点击提交按钮重复提交表单,我们在开发中必须防止表单重复提交。

重新刷新

表单提交后用户点击【刷新】按钮导致表单重复提交

点击浏览器的【后退】按钮回退到表单页面后进行再次提交

用户提交表单后,点击浏览器的【后退】按钮回退到表单页面后进行再次提交

解决方案

使用javascript 解决

 既然存在上述所说的表单重复提交问题,那么我们就要想办法解决,比较常用的方法是采用JavaScript来防止表单重复提交,具体做法如下:

修改form.jsp页面,添加如下的JavaScript代码来防止表单重复提交

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Form表单</title>
<script type="text/javascript">
	var isFlag = false; //表单是否已经提交标识,默认为false

	function submitFlag() {

		if (!isFlag) {
			isFlag = true;
			return true;
		} else {
			return false;
		}

	}
</script>
</head>

<body>
	<form action="${pageContext.request.contextPath}/DoFormServlet"
		method="post" onsubmit="return submitFlag()">
		用户名:<input type="text" name="userName"> <input type="submit"
			value="提交" id="submit">
	</form>
</body>
</html>

 

 除了用这种方式之外,经常见的另一种方式就是表单提交之后,将提交按钮设置为不可用,让用户没有机会点击第二次提交按钮,代码如下:

function dosubmit(){
    //获取表单提交按钮
    var btnSubmit = document.getElementById("submit");
    //将表单提交按钮设置为不可用,这样就可以避免用户再次点击提交按钮
    btnSubmit.disabled= "disabled";
    //返回true让表单可以正常提交
    return true;
}

 

 

使用后端提交解决

 对于【场景二】和【场景三】导致表单重复提交的问题,既然客户端无法解决,那么就在服务器端解决,在服务器端解决就需要用到session了。

  具体的做法:在服务器端生成一个唯一的随机标识号,专业术语称为Token(令牌),同时在当前用户的Session域中保存这个Token。然后将Token发送到客户端的Form表单中,在Form表单中使用隐藏域来存储这个Token,表单提交的时候连同这个Token一起提交到服务器端,然后在服务器端判断客户端提交上来的Token与服务器端生成的Token是否一致,如果不一致,那就是重复提交了,此时服务器端就可以不处理重复提交的表单。如果相同则处理表单提交,处理完后清除当前用户的Session域中存储的标识号。
  在下列情况下,服务器程序将拒绝处理用户提交的表单请求:

存储Session域中的Token(令牌)与表单提交的Token(令牌)不同。

当前用户的Session中不存在Token(令牌)

用户提交的表单数据中没有Token(令牌)

转发代码:

@WebServlet("/ForwardServlet")
public class ForwardServlet extends HttpServlet {
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		req.getSession().setAttribute("sesionToken", TokenUtils.getToken());
		req.getRequestDispatcher("form.jsp").forward(req, resp);
	}
}

转发页面:

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Form表单</title>

</head>

<body>
	<form action="${pageContext.request.contextPath}/DoFormServlet"
		method="post" onsubmit="return dosubmit()">
		<input type="hidden" name="token" value="${sesionToken}"> 用户名:<input type="text"
			name="userName"> <input type="submit" value="提交" id="submit">
	</form>
</body>
</html>

后端Java代码:

@WebServlet("/DoFormServlet")
public class DoFormServlet extends HttpServlet {
	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		req.setCharacterEncoding("UTF-8");
		boolean flag = isFlag(req);
		if (!flag) {
			resp.getWriter().write("已经提交...");
			System.out.println("数据已经提交了..");
			return;
		}
		String userName = req.getParameter("userName");
		try {
			Thread.sleep(300);
		} catch (Exception e) {
			// TODO: handle exception
		}
		System.out.println("往数据库插入数据...." + userName);
		resp.getWriter().write("success");
	}

	public boolean isFlag(HttpServletRequest request) {
		HttpSession session = request.getSession();
		String sesionToken = (String) session.getAttribute("sesionToken");
		String token = request.getParameter("token");
		if (!(token.equals(sesionToken))) {
			return false;
		}
		session.removeAttribute("sesionToken");
		return true;
	}
}

 

 

Filter 也称之为过滤器,它是 Servlet 技术中最实用的技术,Web 开发人员通过 Filter 技术,对 web 服务器管理的所有 web 资源:例如 Jsp, Servlet, 静态图片文件或静态 html 文件等进行拦截,从而实现一些特殊的功能。例如实现 URL 级别的权限访问控制、过滤敏感词汇、压缩响应信息等一些高级功能。

它主要用于对用户请求进行预处理,也可以对 HttpServletResponse 进行后处理。使用 Filter 的完整流程:Filter 对用户请求进行预处理,接着将请求交给 Servlet 进行处理并生成响应,最后 Filter 再对服务器响应进行后处理。

 

CSRF(模拟请求)

CSRF攻击攻击原理及过程如下:

       1. 用户C打开浏览器,访问受信任网站A,输入用户名和密码请求登录网站A;

       2.在用户信息通过验证后,网站A产生Cookie信息并返回给浏览器,此时用户登录网站A成功,可以正常发送请求到网站A;

       3. 用户未退出网站A之前,在同一浏览器中,打开一个TAB页访问网站B;

       4. 网站B接收到用户请求后,返回一些攻击性代码,并发出一个请求要求访问第三方站点A;


       5. 浏览器在接收到这些攻击性代码后,根据网站B的请求,在用户不知情的情况下携带Cookie信息,向网站A发出请求。网站A并不知道该请求其实是由B发起的,所以会根据用户C的Cookie信息以C的权限处理该请求,导致来自网站B的恶意代码被执行。

防御CSRF攻击:

       目前防御 CSRF 攻击主要有三种策略:验证 HTTP Referer 字段;在请求地址中添加 token 并验证;在 HTTP 头中自定义属性并验证。

(1)验证 HTTP Referer 字段

文章深入理解Http协议有介绍

  根据 HTTP 协议,在 HTTP 头中有一个字段叫 Referer,它记录了该 HTTP 请求的来源地址。在通常情况下,访问一个安全受限页面的请求来自于同一个网站,比如需要访问 http://bank.example/withdraw?account=bob&amount=1000000&for=Mallory,用户必须先登陆 bank.example,然后通过点击页面上的按钮来触发转账事件。这时,该转帐请求的 Referer 值就会是转账按钮所在的页面的 URL,通常是以 bank.example 域名开头的地址。而如果黑客要对银行网站实施 CSRF 攻击,他只能在他自己的网站构造请求,当用户通过黑客的网站发送请求到银行时,该请求的 Referer 是指向黑客自己的网站。因此,要防御 CSRF 攻击,银行网站只需要对于每一个转账请求验证其 Referer 值,如果是以 bank.example 开头的域名,则说明该请求是来自银行网站自己的请求,是合法的。如果 Referer 是其他网站的话,则有可能是黑客的 CSRF 攻击,拒绝该请求。

        然而,这种方法并非万无一失。Referer 的值是由浏览器提供的,虽然 HTTP 协议上有明确的要求,但是每个浏览器对于 Referer 的具体实现可能有差别,并不能保证浏览器自身没有安全漏洞。使用验证 Referer 值的方法,就是把安全性都依赖于第三方(即浏览器)来保障,从理论上来讲,这样并不安全。事实上,对于某些浏览器,比如 IE6 或 FF2,目前已经有一些方法可以篡改 Referer 值。如果 bank.example 网站支持 IE6 浏览器,黑客完全可以把用户浏览器的 Referer 值设为以 bank.example 域名开头的地址,这样就可以通过验证,从而进行 CSRF 攻击。

即便是使用最新的浏览器,黑客无法篡改 Referer 值,这种方法仍然有问题。因为 Referer 值会记录下用户的访问来源,有些用户认为这样会侵犯到他们自己的隐私权,特别是有些组织担心 Referer 值会把组织内网中的某些信息泄露到外网中。因此,用户自己可以设置浏览器使其在发送请求时不再提供 Referer。当他们正常访问银行网站时,网站会因为请求没有 Referer 值而认为是 CSRF 攻击,拒绝合法用户的访问。

(2)在请求地址中添加 token 并验证 + 验证码

token放在session中,每次访问先校验,交验完就删除,防止再次请求。

 

使用Fileter防止XSS攻击

什么是XSS攻击?

XSS攻击使用Javascript脚本注入进行攻击

例如在表单中注入: <script>location.href='http://www.itmayiedu.com'</script>(一些钓鱼网站就是这么玩的)

注意:谷歌浏览器 已经防止了XSS攻击,为了演示效果,最好使用火狐浏览器

实例:

演示:

代码: fromToXss.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
	<form action="XssDemo" method="post">
		<input type="text" name="userName"> <input type="submit">
	</form>
</body>
</html>

代码: XssDemo

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/XssDemo")
public class XssDemo extends HttpServlet {

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		String userName = req.getParameter("userName");
		req.setAttribute("userName", userName);
		req.getRequestDispatcher("showUserName.jsp").forward(req, resp);
	}
	

}

代码: showUserName.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>

</head>
<body>userName:${userName}

</body>
</html>

 

解決方案(转义)

使用Fileter过滤器过滤器注入标签

XSSFilter

public class XssFiter implements Filter {

	public void init(FilterConfig filterConfig) throws ServletException {

	}

	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		HttpServletRequest req = (HttpServletRequest) request;
		XssAndSqlHttpServletRequestWrapper xssRequestWrapper = new XssAndSqlHttpServletRequestWrapper(req);
		chain.doFilter(xssRequestWrapper, response);
	}

	public void destroy() {

	}

}
 

XssAndSqlHttpServletRequestWrapper


import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;

/**
 * 防止XSS攻击
 */
public class XssAndSqlHttpServletRequestWrapper extends HttpServletRequestWrapper {
	HttpServletRequest request;
	public XssAndSqlHttpServletRequestWrapper(HttpServletRequest request) {
		super(request);
		this.request = request;
	}
	@Override
	public String getParameter(String name) {
		String value = request.getParameter(name);
		System.out.println("name:" + name + "," + value);
		if (!StringUtils.isEmpty(value)) {
			// 转换Html
			value = StringEscapeUtils.escapeHtml4(value);
		}
		return value;
	}
}
<filter>
		<filter-name>XssFiter</filter-name>
		<filter-class>filter.XssFiter</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>XssFiter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值