CSRF进攻 (跨站域请求伪造)

CSRF 攻击的对象

在讨论如何抵御 CSRF 之前,先要明确 CSRF 攻击的对象,也就是要保护的对象。从以上的例子可知,CSRF 攻击是黑客借助受害者的 cookie 骗取服务器的信任,但是黑客并不能拿到 cookie,也看不到 cookie 的内容。另外,对于服务器返回的结果,由于浏览器同源策略的限制,黑客也无法进行解析。因此,黑客无法从返回的结果中得到任何东西,他所能做的就是给服务器发送请求,以执行请求中所描述的命令,在服务器端直接改变数据的值,而非窃取服务器中的数据。所以,我们要保护的对象是那些可以直接产生数据改变的服务,而对于读取数据的服务,则不需要进行 CSRF的保护。比如银行系统中转账的请求会直接改变账户的金额,会遭到 CSRF 攻击,需要保护。而查询余额是对金额的读取操作,不会改变数据,CSRF 攻击无法解析服务器返回的结果,无需保护。\

 

Csrf攻击方式:

对象:A:普通用户,B:攻击者

1、假设A已经登录过xxx.com并且取得了合法的session,假设用户中心地址为:http://xxx.com/ucenter/index.do

2B想把A余额转到自己的账户上,但是B不知道A的密码,通过分析转账功能发现xxx.com网站存在CSRF攻击漏洞和XSS漏洞。

3B通过构建转账链接的URL如:http://xxx.com/ucenter/index.do?action=transfer&money=100000 &toUser=(B的帐号),因为A已经登录了所以后端在验证身份信息的时候肯定能取得A的信息。B可以通过xss或在其他站点构建这样一个URL诱惑A去点击或触发Xss。一旦A用自己的合法身份去发送一个GET请求后A100000元人民币就转到B账户去了。当然了在转账支付等操作时这种低级的安全问题一般都很少出现。

 

防御CSRF

1、验证 HTTP Referer 字段

2、在请求地址中添加 token 并验证

3、 HTTP 头中自定义属性并验证

4、加验证码

 (copy防御CSRF毫无意义,参考上面给的IBM专题的URL)

Token

最常见的做法是加token,Java里面典型的做法是用filterhttps://code.google.com/p/csrf-filter/(链接由plt提供,源码上面的在:http://ahack.iteye.com/blog/1900708)

 

 

csrf-filter:

 

 

package com.google.code.csrf;

import java.io.IOException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.regex.Pattern;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class StatelessCookieFilter implements Filter {

	private final static Logger LOG = LoggerFactory.getLogger(StatelessCookieFilter.class);
	private final static Pattern COMMA = Pattern.compile(",");

	private String csrfTokenName;
	private String oncePerRequestAttributeName;
	private int cookieMaxAge;
	private Set<String> excludeURLs;
	private List<String> excludeStartWithURLs;
	private Set<String> excludeFormURLs;
	private Random random;

	public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
		HttpServletRequest httpReq = (HttpServletRequest) req;
		HttpServletResponse httpResp = (HttpServletResponse) resp;

		if (httpReq.getAttribute(oncePerRequestAttributeName) != null) {
			chain.doFilter(httpReq, httpResp);
		} else {
			httpReq.setAttribute(oncePerRequestAttributeName, Boolean.TRUE);
			try {
				doFilterInternal(httpReq, httpResp, chain);
			} finally {
				httpReq.removeAttribute(oncePerRequestAttributeName);
			}
		}
	}

	private void doFilterInternal(HttpServletRequest req, HttpServletResponse resp, FilterChain chain) throws IOException, ServletException {
		if (!req.getMethod().equals("POST")) {
			if (excludeFormURLs.contains(req.getServletPath())) {
				chain.doFilter(req, resp);
				return;
			}
			for (String curStart : excludeStartWithURLs) {
				if (req.getServletPath().startsWith(curStart)) {
					chain.doFilter(req, resp);
					return;
				}
			}
			String token = Long.toString(random.nextLong(), 36);
			LOG.debug("new csrf token generated: {} path: {}", token, req.getServletPath());
			req.setAttribute(csrfTokenName, token);
			Cookie cookie = new Cookie(csrfTokenName, token);
			cookie.setPath("/");
			cookie.setMaxAge(cookieMaxAge);
			resp.addCookie(cookie);
			chain.doFilter(req, resp);
			return;
		}

		if (excludeURLs.contains(req.getServletPath())) {
			chain.doFilter(req, resp);
			return;
		}

		String csrfToken = req.getParameter(csrfTokenName);
		if (csrfToken == null) {
			LOG.error("csrf token not found in POST request: {}", req);
			if (!resp.isCommitted()) {
				resp.sendError(400);
			}
			return;
		}
		req.setAttribute(csrfTokenName, csrfToken);

		for (Cookie curCookie : req.getCookies()) {
			if (curCookie.getName().equals(csrfTokenName)) {
				if (curCookie.getValue().equals(csrfToken)) {
					chain.doFilter(req, resp);
					return;
				} else {
					LOG.error("mismatched csrf token. expected: {} received: {}", csrfToken, curCookie.getValue());
					if (!resp.isCommitted()) {
						resp.sendError(400);
					}
					return;
				}
			}
		}

		LOG.error("csrf cookie not found at: {}", req.getServletPath());
		if (!resp.isCommitted()) {
			resp.sendError(400);
		}
	}

	public void destroy() {
		// do nothing
	}

	public void init(FilterConfig config) throws ServletException {
		String value = config.getInitParameter("csrfTokenName");
		if (value == null || value.trim().length() == 0) {
			throw new ServletException("csrfTokenName parameter should be specified");
		}
		csrfTokenName = value;
		String excludedURLsStr = config.getInitParameter("exclude");
		if (excludedURLsStr != null) {
			String[] parts = COMMA.split(excludedURLsStr);
			excludeURLs = new HashSet<String>(parts.length);
			for (String cur : parts) {
				excludeURLs.add(cur);
			}
		} else {
			excludeURLs = new HashSet<String>(0);
		}
		String excludedFormURLsStr = config.getInitParameter("excludeGET");
		if (excludedFormURLsStr != null) {
			String[] parts = COMMA.split(excludedFormURLsStr);
			excludeFormURLs = new HashSet<String>(parts.length);
			for (String cur : parts) {
				excludeFormURLs.add(cur.trim());
			}
		} else {
			excludeFormURLs = new HashSet<String>(0);
		}
		String excludeStartWithURLsStr = config.getInitParameter("excludeGETStartWith");
		if (excludeStartWithURLsStr != null) {
			String[] parts = COMMA.split(excludeStartWithURLsStr);
			excludeStartWithURLs = new ArrayList<String>(parts.length);
			for (String curPart : parts) {
				excludeStartWithURLs.add(curPart.trim());
			}
		} else {
			excludeStartWithURLs = new ArrayList<String>(0);
		}
		String cookieMaxAgeStr = config.getInitParameter("cookieMaxAge");
		if (cookieMaxAgeStr != null) {
			try {
				cookieMaxAge = Integer.parseInt(cookieMaxAgeStr);
			} catch (NumberFormatException nfe) {
				throw new ServletException("cookieMaxAge must be an integer: " + cookieMaxAgeStr, nfe);
			}
		} else {
			cookieMaxAge = 3600; // 60*60 seconds = 1 hour
		}
		oncePerRequestAttributeName = getFirstTimeAttributeName();
		random = new SecureRandom();
	}

	public static String getFirstTimeAttributeName() {
		return StatelessCookieFilter.class.getName() + ".ATTR";
	}

}

 

 

HowTo

  • Configure web.xml:
<filter>
                <filter-name>csrfFilter</filter-name>
                <filter-class>com.google.code.csrf.StatelessCookieFilter</filter-class>
                <init-param> 
                        <param-name>csrfTokenName</param-name> 
                        <param-value>csrf</param-value> 
                </init-param>
                <init-param>
                        <!-- optional. urls to exclude from check -->
                        <param-name>exclude</param-name> 
                        <param-value>/url1,/url/url2</param-value> 
                </init-param>
                <init-param>
                        <!-- optional. urls to exclude from generating csrf cookie. Useful for ajax requests that do not contain forms -->
                        <param-name>excludeGET</param-name> 
                        <param-value>/url3,/url/url4</param-value> 
                </init-param>
                <init-param>
                        <!-- optional. urls to exclude from generating csrf cookie. Exclude do check servletPath().startsWith() -->
                        <param-name>excludeGETStartWith</param-name> 
                        <param-value>/js/,/css/,/img/</param-value> 
                </init-param>
                <init-param>
                        <!-- optional. cookieMaxAge. By default 3600 seconds -->
                        <param-name>cookieMaxAge</param-name> 
                        <param-value>18000</param-value> 
                </init-param>
        </filter>
        <filter-mapping>
                <filter-name>csrfFilter</filter-name>
                <url-pattern>/*</url-pattern>
        </filter-mapping>

 

 

  • Add to every POST request parameter "csrf". For example form.jsp:

 

<form method="POST">
                <input type="hidden" name="csrf" value="${csrf}">
 </form>

 

  • For "multipart/form-data" requests add to "action" attribute:
<form action="/url?csrf=${csrf}" method="POST" enctype="multipart/form-data">
                <input type="file" name="file" size="50"/>
</form>

  

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值