Spring MVC防御CSRF和XSS

本文介绍如何使用SpringMVC防御CSRF和XSS攻击,详细解释了CSRF的工作原理,并提供了一种通过生成随机Token来防止CSRF攻击的方法。同时也介绍了如何通过配置SpringMVC来防御XSS攻击。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文说一下SpringMVC如何防御CSRF(Cross-site request forgery跨站请求伪造)和XSS(Cross site script跨站脚本攻击)。

说说CSRF

CSRF来说,其实Spring3.1ASP.NET MVC3、Rails、Django等都已经支持自动在涉及POST的地方添加Token(包括FORM表单和AJAX POST等),似乎是一个tag的事情,但如果了解一些实现原理,手工来处理,也是有好处的。因为其实很多人做web开发,但涉及到web安全方面的都是比较资深的开发人员,很多人安全意识非常薄弱,CSRF是什么根本没有听说过。所以对他们来说,CSRF已经是比较高深的东西了。先说说什么是CSRF?你这可以这么理解CSRF攻击:攻击者盗用了你的身份,以你的名义发送恶意请求。CSRF能够做的事情包括:以你名义发送邮件,发消息,盗取你的账号,甚至于购买商品,虚拟货币转账......造成的问题包括:个人隐私泄露以及财产安全。CSRF一般都是利用你的已登录已验证的身份来发送恶意请求。比较著名的一个例子就是2009年黑客利用Gmail的一个CSRF漏洞成功获取好莱坞明星Vanessa Hudgens的独家艳照。其攻击过程非常简单,给该明星的gmail账户发了一封email,标题是某大导演邀请你来看看这个电影,里面有个图片:<img src="https://mail.google.com/mail?ui=2&fw=true&fwe=hacker@email.com">,结果她登录Gmail,打开邮件就默默无闻的中招了,所有邮件被转发到黑客的账号。因为当时Gmail设置转发的设置页面有漏洞,其设置方法是打开一个窗口,点击确定后实际URL是https://mail.google.com/mail?ui=2&fw=true&fwe=newMail@email.com:

其实即使不是在同一个页面打开,在不同的tab打开也是一样可以通过网站登录验证的,因为受害者首先已经登录了网站,在浏览网站的过程中,若网站设置了Session cookie,那么在浏览器进程的生命周期内,即使浏览器同一个窗口打开了新的tab页面,Session cookie也都是有效的,他们在浏览器同一个窗口的多个tab页面里面是共享的(注:现在Gmail支持多个tab同时持有多个SessionID)。所以攻击步骤是,第一,受害者必须在同一浏览器窗口(即使不是同一tab)内访问并登陆目标站点;第二,这使得Session cookie有效,从而利用受害者的身份进行恶意操作。

再举个实际的例子,假设我们界面上有删除某一项的链接,例如:<a href="javascript:void(0)" onclick="region_del.do?name=0000001">Delete</a>;

其Java Spring MVC后台有个函数是删除某个item,注意是GET不是POST:

复制代码
@RequestMapping(value = "region_del.do", method = RequestMethod.GET)
public String regionDel(@RequestParam String name, Locale locale)
{
// Delete region name=@name....

return "redirect:/region.html";
}
复制代码

点击界面上那个<a href="javascript:void(0)" onclick="region_del.do?name=0000001">Delete</a>链接,就后台删除某项,看起来非常正常啊。

好,现在你登录你的网站,然后在另外一个tab打开这个html文件:

复制代码
<! DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" >
< html xmlns ="http://www.w3.org/1999/xhtml" xml:lang ="en" lang ="en" >
< head >
< meta http-equiv ="Content-Type" content ="text/html; charset=utf-8" />
< title >hack </ title >
</ head >
< body >
< img src ="http://localhost/testsite/region_del.do?name=0000001" />
</ body >
</ html >
复制代码

发现同样被删除了某项。试想,如果是网银,你的钱已经被转账......(除了referer不一样,session cookie被利用)

好了,现在 后台改成POST(写操作尽量用POST),前台界面那个删除的链接改成Form提交:

< form action ="region_del.do" method ="POST" >
< input type ="hidden" name ="name" value ="0000001" >
< input type ="submit" value ="Delete" />
</ form >

看起来安全多了。OK,现在你登录你的网站,然后在另外一个tab打开这个html文件:

复制代码
<! DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" >
< html xmlns ="http://www.w3.org/1999/xhtml" xml:lang ="en" lang ="en" >
< head >
< title >Hack </ title >
< script >
function steal(){
var mySubmit = document.getElementById( ' steal_form ' );
mySubmit.submit();
}
</ script >
</ head >
< body onload ='steal()' >
< form id = "steal_form" method ="POST" action ="http://localhost/testsite/region_del.do" >
< input type ="hidden" name ="func" value ="post" >
< input type ="hidden" name ="name" value ="0000001" >
</ form >
</ body >
</ html >
复制代码

发现同样被删除了某项。试想,如果是网银,你的钱已经被转账......
当然,你如果前台还是用链接,但改成js,用AJAX POST提交,也是一样的效果:

$.ajax({
type: "POST",
url:....
});

解决办法就是在Form表单加一个hidden field,里面是服务端生成的足够随机数的一个Token,使得黑客猜不到也无法仿照Token

先写一个类,生成足够随机数的Token(注:Java的Random UUID已经足够随机了,参考这个这个)

复制代码
package com.ibm.cn.web.beans;

import java.util.UUID;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

/**
* A manager for the CSRF token for a given session. The {
@link #getTokenForSession(HttpSession)} should used to
* obtain the token value for the current session (and this should be the only way to obtain the token value).
* **
*/

public final class CSRFTokenManager {

/**
* The token parameter name
*/
static final String CSRF_PARAM_NAME = "CSRFToken";

/**
* The location on the session which stores the token
*/
public static final String CSRF_TOKEN_FOR_SESSION_ATTR_NAME = CSRFTokenManager. class
.getName() + ".tokenval";

public static String getTokenForSession(HttpSession session) {
String token = null;

// I cannot allow more than one token on a session - in the case of two
// requests trying to
// init the token concurrently
synchronized (session) {
token = (String) session
.getAttribute(CSRF_TOKEN_FOR_SESSION_ATTR_NAME);
if ( null == token) {
token = UUID.randomUUID().toString();
session.setAttribute(CSRF_TOKEN_FOR_SESSION_ATTR_NAME, token);
}
}
return token;
}

/**
* Extracts the token value from the session
*
*
@param request
*
@return
*/
public static String getTokenFromRequest(HttpServletRequest request) {
return request.getParameter(CSRF_PARAM_NAME);
}

private CSRFTokenManager() {
};

}
复制代码

打开Form页面的时候在服务端生成Token并保存到Session中,例如:model.addAttribute("csrf", CSRFTokenManager.getTokenForSession(this.session));

然后在Form中添加Hidden field:

<input type="hidden" name="CSRFToken" value="${csrf}" />

然后在后台提交的时候验证token :

复制代码
@RequestMapping(value = "region_del.do", method = RequestMethod.GET)
public String regionDel(@RequestParam String name, @RequestParam String CSRFToken, Locale locale)
{
if(CSRFToken == null || !CSRFToken.equals(session.getAttribute(CSRFTokenManager.CSRF_TOKEN_FOR_SESSION_ATTR_NAME).toString())){
logger.debug("CSRF attack detected. URL: region_edit.do");
return "redirect:/login.form";
}

// Delete region name=@name....

return "redirect:/region.html";
}
复制代码

你还可以把上面的步骤写到BaseController里面,或者写到拦截器里面,拦截所有POST请求,验证CSRF Token。这里掠过....

如果你用AJAX POST的方法,那么后台一样,前台也要有Hidden field保存Token,然后在提交AJAX POST的时候加上该csrf参数即可。(更多csrf参考这个这个。)

xss

关于xss的介绍可以看这个这个网页,具体我就讲讲Spring MVC里面的预防:

web.xml加上:

< context-param >
< param-name >defaultHtmlEscape </ param-name >
< param-value >true </ param-value >
</ context-param >

Forms加上:

<spring:htmlEscape defaultHtmlEscape="true" />

更多信息查看OWASP页面

 

http://www.cnblogs.com/Mainz/archive/2012/11/01/2749874.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值