我试图阻止我的网络应用程序出现CSRF(跨站请求伪造) 我点了这个链接Link for CSRF
这是我尝试过的。 要在Java中实现这种机制,我选择使用两个过滤器,一个用于为每个请求创建salt,另一个用于验证它。由于用户请求以及应该验证的后续POST或GET不一定按顺序执行,我决定使用基于时间的缓存来存储有效盐字符串列表。
第一个用于为请求生成新盐并将其存储在缓存中的过滤器可以编码如下:
public class LoadSalt implements Filter{
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// Assume its HTTP
HttpServletRequest httpReq = (HttpServletRequest)request;
// Check the user session for the salt cache, if none is present we create one
@SuppressWarnings("unchecked")
Cache<String, Boolean> csrfPreventionSaltCache = (Cache<String, Boolean>)
httpReq.getSession().getAttribute("csrfPreventionSaltCache");
System.out.println("Checking cahce befor creating it from Request :csrfPreventionSaltCache: "+csrfPreventionSaltCache);
if(csrfPreventionSaltCache == null)
{
System.out.println("csrfPreventionSaltCache is null have to create new one");
String csrfPreventionfromrequest = (String) httpReq.getSession().getAttribute("csrfPreventionSaltCache");
System.out.println("csrfPreventionfromrequest :"+csrfPreventionfromrequest);
// creating a new cache
csrfPreventionSaltCache = CacheBuilder.newBuilder().maximumSize(5000)
.expireAfterAccess(20, TimeUnit.MINUTES).build();
// Setting to gttpReq
httpReq.getSession().setAttribute("csrfPreventionSaltCache", csrfPreventionSaltCache);
System.out.println("After setting the csrfPreventionSaltCache to HttpReq");
System.out.println("--------csrfPreventionSaltCache------ :"+httpReq.getSession().getAttribute("csrfPreventionSaltCache"));
}
// Generate the salt and store it in the users cache
String salt = RandomStringUtils.random(20, 0, 0, true, true, null, new SecureRandom());
System.out.println("Salt: "+salt);
csrfPreventionSaltCache.put(salt, Boolean.TRUE);
// Add the salt to the current request so it can be used
// by the page rendered in this request
httpReq.setAttribute("csrfPreventionSalt", salt);
System.out.println("Before going to validate salt checking for salt in request");
System.out.println(" httpReq.getAttribute(csrfPreventionSalt) ----:"+httpReq.getAttribute("csrfPreventionSalt"));
// System.out.println(" httpReq.getSession().getAttribute(csrfPreventionSalt) :----"+httpReq.getSession().getAttribute("csrfPreventionSalt"));
chain.doFilter(request, response);
}
public void init(FilterConfig arg0) throws ServletException {
}
public void destroy() {
}
}
在web.xml中进行映射
<filter>
<filter-name>loadSalt</filter-name>
<filter-class>com.globalss.dnb.monitor.security.LoadSalt</filter-class>
</filter>
<filter-mapping>
<filter-name>loadSalt</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
在执行安全事务之前验证salt我写了另一个过滤器:
public class ValidateSalt implements Filter {
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// Assume its HTTP
HttpServletRequest httpReq = (HttpServletRequest) request;
//String salt = (String) httpReq.getSession().getAttribute("csrfPreventionSalt");
String salt =(String) httpReq.getAttribute("csrfPreventionSalt");
System.out.println("I am in ValidateSalt : salt: "+salt);
// Validate that the salt is in the cache
@SuppressWarnings("unchecked")
Cache<String, Boolean> csrfPreventionSaltCache = (Cache<String, Boolean>)
httpReq.getSession().getAttribute("csrfPreventionSaltCache");
if(csrfPreventionSaltCache !=null && salt !=null && csrfPreventionSaltCache.getIfPresent(salt)!=null)
{
// If the salt is in the cache, we move on
chain.doFilter(request, response);
}
else
{
// Otherwise we throw an exception aborting the request flow
throw new ServletException("Potential CSRF detected!! Inform a scary sysadmin ASAP.");
}
}
public void init(FilterConfig arg0) throws ServletException {
}
public void destroy() {
}
}
在web.xml中映射第二个Filetr
<filter>
<filter-name>validateSalt</filter-name>
<filter-class>com.globalss.dnb.monitor.security.ValidateSalt</filter-class>
</filter>
<filter-mapping>
<filter-name>validateSalt</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
配置两个servlet之后,所有安全请求都在失败:)。为了解决这个问题,我必须为每个链接添加一个以安全URL结尾的帖子,csrfPreventionSalt参数包含具有相同名称的请求参数的值。例如,在JSP页面中的HTML表单中:
<form action="/transferMoneyServlet" method="get">
<input type="hidden" name="csrfPreventionSalt" value="<c:out value='${csrfPreventionSalt}'/>"/>
...
</form>
完成所有这些后,我尝试尝试CSRF,这就是我做的
<html>
<body>
<form action="http://localhost:8080/mywebapp/dispatcherServlet/addUserController/addUser" method="POST" enctype="text/plain">
<input type="hidden" name="{"userName":"CSRUser","password":"CSRFUser123","roles":"true","status":"true"}" value="" />
<input type="submit" value="Submit request" />
</form>
</body>
</html>
当我点击提交请求按钮时,我收到了成功回复, 和CSRUser已添加到我的数据库中。
我错过了什么,如何防止CSRF攻击?答案 0 :(得分:1)
您可以使用Spring Security进行用户身份验证和授权。默认情况下,从Spring 3.2.0版本开始支持Cross Site Request Forgery (CSRF)。
您还可以使用RequestMatcher轻松排除您不想保护的网址:
public class CsrfSecurityRequestMatcher implements RequestMatcher {
private Pattern allowedMethods = Pattern.compile("^(GET|HEAD|TRACE|OPTIONS)$");
private RegexRequestMatcher unprotectedMatcher = new RegexRequestMatcher("/unprotected", null);
@Override
public boolean matches(HttpServletRequest request) {
if(allowedMethods.matcher(request.getMethod()).matches()){
return false;
}
return !unprotectedMatcher.matches(request);
}
}
答案 1 :(得分:0)
前几天我有同样的问题,我发现了同样的文章。
实际上,当我在复制和粘贴它的时候,我在想它是多么安全......
所以,我的第一个想法是真正开始了解CSRF攻击是什么:OWASP - Cross Site Request Forgery。
在理解之后,我做的第一件事就是用HTTP GET和POST破解我自己的应用程序,我真的很惊讶它是多么容易:Here关于CSRF的更多解释以及如何做到这一点)
最后,我意识到&#34; CSRF攻击中的内容远远超过了眼睛&#34;而且,由于没有关于令牌注入的解释,因此本文并不保证任何安全性,因此,您无法确保攻击者没有拥有有效令牌。在这种情况下,过滤令牌是无用的。
在这一点上,我建议在OWASP CSRF Prevention Cheat Sheet中阅读更多内容以深入了解它。
最后但并非最不重要的是,在我的情况下,我决定使用de OWASP CSRF Guard library,这是非常可配置/灵活和开源的。所以,最后,如果你决定不去使用它,至少你会更好地了解如何通过遵循它的一些架构来实现它。
<强> [更新] 强>
嗯,不幸的是,由于以下缺点,我无法使用CSRF Guard:
它基于同步ajax请求,该请求正在从网络平台中删除:https://xhr.spec.whatwg.org/
我无法使其与动态表单生成一起使用,因为无法访问令牌以在动态表单中添加隐藏字段。我认为这是设计的,但我无法改变我的应用以适应这种现实;
我无法使用多部分表单;
幸运的是,我已经越过了一个非常好的答案here,它引导我使用&#34; Set-Cookie&#34;来做我自己的实现。策略。
我希望它有所帮助。