项目地址: https://github.com/morethink/web-security

CSRF是什么?

CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF。

CSRF可以做什么?

你这可以这么理解CSRF攻击:攻击者盗用了你的身份,以你的名义发送恶意请求。CSRF能够做的事情包括:以你名义发送邮件,发消息,盗取你的账号,甚至于购买商品,虚拟货币转账......造成的问题包括:个人隐私泄露以及财产安全。

CSRF的原理

下图简单阐述了CSRF攻击的思

从上图可以看出,要完成一次CSRF攻击,受害者必须依次完成两个步骤:

  1. 登录受信任网站A,并在本地生成Cookie。
  2. 在不登出A的情况下,访问危险网站B。

看到这里,你也许会说:“如果我不满足以上两个条件中的一个,我就不会受到CSRF的攻击”。是的,确实如此,但你不能保证以下情况不会发生:

  1. 你不能保证你登录了一个网站后,不再打开一个tab页面并访问另外的网站。
  2. 你不能保证你关闭浏览器了后,你本地的Cookie立刻过期,你上次的会话已经结束。(事实上,关闭浏览器不能结束一个会话,但大多数人都会错误的认为关闭浏览器就等于退出登录/结束会话了......)
  3. 上图中所谓的攻击网站,可能是一个存在其他漏洞的可信任的经常被人访问的网站。

下面讲一讲java解决CSRF攻击的方式。

模拟CSRF攻击

登录A网站

用户名和密码都是admin。

http://localhost:8081/login.html:

你有权限删除1号帖子

http://localhost:8081/deletePost.html:

登录有CSRF攻击A网站的B网站

http://localhost:8082/deletePost.html:

明显看到B网站是8082端口,A网站是8081端口,但是B网站的删除2号帖子功能依然实现。

预防CSRF攻击

简单来说,CSRF 就是网站 A 对用户建立信任关系后,在网站 B 上利用这种信任关系,跨站点向网站 A 发起一些伪造的用户操作请求,以达到攻击的目的。

而之所以可以完成攻击是因为B向A发起攻击的时候会把A网站的cookie带给A网站,也就是说cookie已经不安全了。

通过Synchronizer Tokens

Synchronizer Tokens: 在表单里隐藏一个随机变化的 csrf_token csrf_token 提交到后台进行验证,如果验证通过则可以继续执行操作。这种情况有效的主要原因是网站 B 拿不到网站 A 表单里的 csrf_token

这种方式的使用条件是PHP和JSP等。因为cookie已经不安全了,因此把csrf_token值存储在session中,然后每次表单提交时都从session取出来放到form表单的隐藏域中,这样B网站不可以得到这个存储到session中的值。

下面是JSP的:

<input type="hidden" name="random_form" value=<%=random%>></input>

但是我现在的情况是html,不是JSP,并不能动态的从session中取出csrf_token值。只能采用加密的方式了。

Hash加密cookie中csrf_token值

这可能是最简单的解决方案了,因为攻击者不能获得第三方的Cookie(理论上),所以表单中的数据也就构造失败了。

我采用的hash加密方法是JS实现Java的HashCode方法,得到hash值,这个比较简单。也可以采用其他的hash算法。

前端向后台传递hash之后的csrf_token值和cookie中的csrf_token值,后台拿到cookie中的csrf_token值后得到hashCode值然后与前端传过来的值进行比较,一样则通过。

你有权限删除3号帖子

http://localhost:8081/deletePost.html

B网站的他已经没有权限了

我们通过UserFilter.java给攻击者返回的是403错误,表示服务器理解用户客户端的请求但拒绝处理。

http://localhost:8082/deletePost.html:

攻击者不能删除4号帖子。

前端代码:

deletePost.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>deletePost</title>
<script type="text/javascript" src="js/jquery.min.js"></script>
<script type="text/javascript">
function deletePost() {
var url = '/post/' + document.getElementById("postId").value;
var csrf_token = document.cookie.replace(/(?:(?:^|.*;\s*)csrf_token\s*\=\s*([^;]*).*$)|^.*$/, "$1");
console.log('csrf_token=' + csrf_token);
$.ajax({
type: "post",//请求方式
url: url, //发送请求地址
timeout: 30000,//超时时间:30秒
data: {
"_method": "delete",
"csrf_token": hash(csrf_token) // 对csrf_token进行hash加密
},
dataType: "json",//设置返回数据的格式
success: function (result) {
if (result.message == "success") {
// window.location.href = "index.html";
$("#result").text("删除成功");
} else {
$("#result").text("删除失败");
}
},
error: function () { //请求出错的处理
$("#result").text("请求出错");
}
});
} // javascript的String到int(32位)的hash算法
function hash(str) {
var hash = 0;
if (str.length == 0) return hash;
for (i = 0; i < str.length; i++) {
char = str.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash = hash & hash; // Convert to 32bit integer
}
return hash;
}
</script>
</head>
<body>
<h3>删除帖子</h3>
帖子编号 : <input type="text" id="postId"/>
<button onclick="deletePost();">deletePost</button> <br/>
<br/>
<br/>
<div>
<p id="result"></p>
</div>
</body>
</html>

后台代码:

UserInterceptor.java

package cn.morethink.interceptor;

import cn.morethink.util.JsonUtil;
import cn.morethink.util.Result;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter; /**
* @author 李文浩
* @date 2018/1/4
*/
public class UserInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String method = request.getMethod();
System.out.println(method);
if (method.equalsIgnoreCase("POST") || method.equalsIgnoreCase("DELETE")
|| method.equalsIgnoreCase("PUT")) {
String csrf_token = request.getParameter("csrf_token");
System.out.println(csrf_token + "1222222222222222222222222222222222222222222222");
Cookie[] cookies = request.getCookies();
if (cookies != null && cookies.length > 0 && csrf_token != null) {
for (Cookie cookie : cookies) {
if (cookie.getName().equals("csrf_token")) {
if (Integer.valueOf(csrf_token) == cookie.getValue().hashCode()) {
return true;
}
}
}
}
}
Result result = new Result("403", "你还想攻击我??????????", "");
PrintWriter out = response.getWriter();
out.write(JsonUtil.toJson(result));
out.close();
return false;
} @Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { } @Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { }
}

注意

  1. cookie必须要设置PATH才可以生效,否则在下一次请求的时候无法带给服务器。
  2. Spring Boot 出现启动找不到主类的问题时可以mvn clean一下。
  3. Filter设置response.sendError(403)在Spring Boot没有效果。

参考文档

  1. 浅谈CSRF攻击方式
  2. jQueue 动态设置form表单的action属性的值和方法
  3. javascript的String到int(32位)的hash算法

Java解决CSRF问题的更多相关文章

  1. java 解决Hash(散列)冲突的四种方法--开放定址法(线性探测,二次探测,伪随机探测)、链地址法、再哈希、建立公共溢出区

    java 解决Hash(散列)冲突的四种方法--开放定址法(线性探测,二次探测,伪随机探测).链地址法.再哈希.建立公共溢出区 标签: hashmaphashmap冲突解决冲突的方法冲突 2016-0 ...

  2. JAVA | Java 解决跨域问题

    JAVA | Java 解决跨域问题 Table of Contents 引言 什么是跨域(CORS) 什么情况会跨域 解决方案 前端解决方案 后端解决方案 具体方式 一.使用Filter方式进行设置 ...

  3. Java解决大文件读取的内存问题以及文件流的比较

    Java解决大文件读取的内存问题以及文件流的比较 传统方式 读取文件的方式一般是是从内存中读取,官方提供了几种方式,如BufferedReader, 以及InputStream 系列的,也有封装好的如 ...

  4. java 解决中文乱码

    //1.代码解决 public class LuanMa { public static String getNewString(String luanma){ String result = &qu ...

  5. 约瑟夫环的java解决

    总共3中解决方法,1.数学推导,2.使用ArrayList递归解决,3.使用首位相连的LinkedList解决 import java.util.ArrayList; /** * 约瑟夫环问题 * 需 ...

  6. java 解决JFrame不能设置背景色的问题 分类: Java Game 2014-08-15 09:48 119人阅读 评论(0) 收藏

    这段时间比较多,于是写一写JAVA的一些IT技术文章.如有JAVA高手请加QQ:314783246,互相讨论. 在Java的GUI设计中,Frame和JFrame两者之间有很大差别,上次刚学时编一个窗 ...

  7. Java解决TopK问题(使用集合和直接实现)

    在处理大量数据的时候,有时候往往需要找出Top前几的数据,这时候如果直接对数据进行排序,在处理海量数据的时候往往就是不可行的了,而且在排序最好的时间复杂度为nlogn,当n远大于需要获取到的数据的时候 ...

  8. 关于eclipse新建web项目,提示:"The superclass "javax.servlet.http.HttpServlet" was not found on the Java"解决办法

    新建jsp页面老提示: Multiple annotations found at this line: - The superclass "javax.servlet.http.HttpS ...

  9. 解决No 'Access-Control-Allow-Origin' header is present on the requested resource.跨域问题(后台(java)解决方法)

    附:前端常见跨域解决方案(全) 跨域错误 解决方法 在后台写一个过滤器来改写请求头 附上一个前端不知所以然的后台java代码: public class CorsFilter implements F ...

随机推荐

  1. 线上系统/tmp 目录不断增长分析与总结

    1.问题描述 系统配置为单核4G, web 工程配置堆2G,  /tmp目录 二进制文件不断增加,平均一天增加20G, 手动清理/tmp目录,重启系统,问题依旧. 2.分析 /tmp 目录存放系统运行 ...

  2. weui 中的tabbar导航

    最近做微信的服务号项目,用的weui作为主要的ui,但是对于用惯了ele ui的开发者来说,文档貌似有点不友好.真是很让人头疼! 所以结合着自己做的项目,随便写一点东西. 比如说,tabbar导航的切 ...

  3. win10/win7下不通过winmanager整合NERDTree和Tagbar的gVim8.0配置

    本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利. 注:图片来自作者公众号--"iKM_2018",亦即& ...

  4. 数据挖掘之聚类算法Apriori总结

    项目中有时候需要用到对数据进行关联分析,比如分析一个小商店中顾客购买习惯. package com.data.algorithm; import com.google.common.base.Spli ...

  5. 基于Windows环境下cmd/编译器无法输入中文,显示中文乱码解决方案

    基于Windows环境下cmd/编译器无法输入中文,显示中文乱码解决方案 两个月前做C++课设的时候,电脑编译器编译结果出现了中文乱码,寻求了百度和大神们,都没有解决这个问题,百度上一堆解释是对编译器 ...

  6. 51 Nod 1057 N的阶乘【Java大数乱搞】

    1057 N的阶乘 基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题 输入N求N的阶乘的准确值.   Input 输入N(1 <= N <= 10000) Ou ...

  7. HDU1312-Red and Black-DFS

    Red and Black Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Tot ...

  8. Win10没有以太网图标如何找回?以太网适配器不见了怎么恢复?

    Win10以太网适配器不见了怎么恢复?以太网其实就是Win7系统中常说的"本地连接"假若用户发现网络适配器中的以太网适配器图标不见了,可以在设备管理器中添加一些这类适配器,具体过程 ...

  9. SpringMVC框架学习笔记(5)——数据处理

    1.提交数据的处理 a)提交的域名称和参数名称一致 http://localhost:8080/foward/hello.do?name=zhangsan 处理方法 @RequestMapping(v ...

  10. 一步一步从原理跟我学邮件收取及发送 4.不同平台下的socket

    既然是面向程序员的文章那当然不能只说说原理,一定要有实际动手的操作.    其实作为我个人的经历来说,对于网络编程,这是最重要的一章! 作为一位混迹业内近20年的快退休的程序员,我学习过很多的开发语言 ...