专栏首页咸鱼不闲CSRF漏洞的原理与防御

CSRF漏洞的原理与防御

CSRF 全称:Cross Site Request Forgery,译:跨站请求伪造

场景

点击一个链接之后发现:账号被盗,钱被转走,或者莫名发表某些评论等一切自己不知情的操作。

CSRF是什么

csrf 是一个可以发送http请求的脚本。可以伪装受害者向网站发送请求,达到修改网站数据的目的。

原理

当你在浏览器上登录某网站后,cookie会保存登录的信息,这样在继续访问的时候不用每次都登录了,这个大家都知道。而CSRF就利用这个登陆态去发送恶意请求给后端。

为什么脚本可以获得目标网站的cookie呢? 只要是请求目标网站,浏览器会自动带上该网站域名下面的cookie,看下面的脚本,可以证明恶意脚本可以获得CSDN网站的登录信息。 前提是你已经在浏览器上登录了CSND网站。

<!doctype html>
<html>
    <head>
        <meta charset="utf-8"/>
        <title>csrf demo</title>
    </head>
    <body>
    您在CSDN上的
        粉丝数:<span id="fans_num"></span>
        关注数:<span id="follow_num"></span>
        <script>
            fetch('https://me.csdn.net/api/relation/get', {
              credentials: 'include'  
            }).then(res => res.json())
            .then(
                res => {
                document.getElementById('fans_num').innerText = res.data.fans_num;
                document.getElementById('follow_num').innerText = res.data.follow_num;
            })  
        </script>
    </body>
</html>

保证CSDN的登录状态,用浏览器打开这个html文件,可以看到这个脚本已经获得了我在csdn 上的用户信息。以及寒酸的粉丝数量! F12打开选择应用程序一栏左边Cookie 还有来自csdn网站关于当前用户的一些信息。

这个脚本让每个不同的登录用户打开,都会根据当前用户来展示关注数和粉丝数, 这就足以说明可以获得目标网站的当前用户的信息,并能够代表用户发送请求。 这只是个无害的get请求,如果是post请求呢?

CSRF攻击

知道了原理,攻击就变得好理解了,接着上面的例子, 我把请求地址改成评论本篇文章的url,参数为 “这篇文章写得6”, 在没有CSRF防御的情况下,我发表一个评论如:脱单秘笈:,后面附上这个脚本的链接,只要有用户点了链接,就会以他的名义给本篇文章发评论“这篇文章写得6”。

CSDN 肯定是做了防御了哈,我就不白费力气了。

CSRF防御

三种防御方式:

1. SameSit

禁止第三方网站使用本站Cookie

这是后端在设置Cookie时候给SameSite的值设置为Strict或者Lax。 当设置Strict的时候代表第三方网站所有请求都不能使用本站的Cookie。 当设置Lax的时候代表只允许第三方网站的GET表单、<a>标签和<link>标签携带Cookie。 当设置None的时候代表和没设一样。

@Bean
public CookieSerializer httpSessionIdResolver(){
    DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();
    cookieSerializer.setCookieName("JESSIONID");
    cookieSerializer.setUseHttpOnlyCookie(true);
    cookieSerializer.setSameSite("Lax");
    cookieSerializer.setUseSecureCookie(true);
    return cookieSerializer;
}

缺点: 目前只有chrome浏览器支持........

2. referer

referer代表着请求的来源,不可以伪造。 后端写个过滤器检查请求的headers中的referer,检验是不是本网站的请求。 题外话: refererorigin的区别,只有post请求会携带origin请求头,而referer不论何种情况下都带。 referer正确的拼写 应该是 referrer,HTTP的标准制定者们将错就错,不打算改了 缺点: 浏览器可以关闭referer..........

3. token

最普遍的一种防御方法,后端生成一个token放在session中并发给前端,前端发送请求时携带这个token,后端通过校验这个token和session中的token是否一致判断是否是本网站的请求。

具体实现: 用户登录输入账号密码,请求登录接口,后端在用户登录信息正确的情况下将token放到session中,并返回token给前端,前端把token 存放在localstory中,之后再发送请求都会将token放到header中。 后端写个过滤器,拦截POST请求,注意忽略掉不需要token的请求,比如登录接口,获取token的接口,免得还没有获取token就检验token。 校验原则: session中的token和前端header中的token一致的post ,放行。

/**
 * @author mashu
 * Date 2020/6/22 9:37
 */
@Slf4j
@Component
@WebFilter(urlPatterns = "/*", filterName = "verificationTokenFilter", description = "用于校验token")
public class VerificationTokenFilter implements Filter {

    List<String> ignorePathList = ImmutableList.of("/demo/login","/demo/getToken");

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
        //忽略不需要token的请求
        String serviceUrl = httpServletRequest.getServletPath();
        for (final String ignorePath : ignorePathList) {
            if (serviceUrl.contains(ignorePath)) {
                filterChain.doFilter(servletRequest, servletResponse);
                return;
            }
        }
        String method = httpServletRequest.getMethod();
        if ("POST".equals(method)) {
           String tokenSession = (String)httpServletRequest.getSession().getAttribute("token");
           String token = httpServletRequest.getHeader("token");
            if (null != token && null != tokenSession && tokenSession.equals(token)) {
                filterChain.doFilter(servletRequest, servletResponse);
                return;
            } else {
                log.error("验证token失败!" + tokenSession + "!=" + token);
                httpServletResponse.sendError(403);
                return;
            }
        }
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {

    }
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 不能解析EL表达式解决方案

    错误代码:The identifier [static] is not a valid Java identifier as required by secti...

    Mshu
  • springboot 集成CAS 实现单点登录

    最近新参与的项目用到了cas单点登录,我还不会,这怎么能容忍!空了学习并搭建了一个spring-boot 集成CAS 的demo。实现了单点登录与登出。

    Mshu
  • Python Tesseract 图片识别-小操练

    but,Tesseract是老外开发的,默认不支持中文,需要我们加个中文语言包 将文件chi_sim.traineddata (密码:nd6p) 放到安装目录...

    Mshu
  • Spring Cloud OAuth2 实现用户认证及单点登录

    OAuth 2 有四种授权模式,分别是授权码模式(authorization code)、简化模式(implicit)、密码模式(resource owner ...

    古时的风筝
  • Flask 博客接入第三方登录

    Flask不像Django一样有各种现成的组件可以选用,Flask的各种扩展也不那么「开箱即用」。在我的博客项目中,我选用的是Authlib,它是国内的一名Py...

    岂不美哉Frost
  • 基于token的多平台身份认证架构设计

    基于token的多平台身份认证架构设计 1   概述 在存在账号体系的信息系统中,对身份的鉴定是非常重要的事情。 随着移动互联网时代到来,客户端的类型越来越多,...

    用户1170933
  • Python接口自动化之接口依赖

    在上一篇Python接口自动化测试系列文章:Python接口自动化之logging封装及实战,

    ITester软件测试小栈
  • 浅析分布式下的事件驱动机制(PubSub模式)

    上一篇文章《浅析Spring中的事件驱动机制》简单介绍了Spring对事件的支持。Event的整个生命周期,从publisher发出,经过application...

    kirito-moe
  • python接口自动化25-全局变量token项目设计

    在做接口自动化的时候,经常会遇到多个用例需要用同一个参数token,并且这些测试用例跨.py脚本了。 一般token只需要获取一次就行了,然后其它使用unitt...

    上海-悠悠
  • Spring Cloud中Feign如何统一设置验证token

    原理是通过每个微服务请求之前都从认证服务获取认证之后的token,然后将token放入到请求头中带过去,这样被调用方通过验证token来判断是否合法请求。

    猿天地

扫码关注云+社区

领取腾讯云代金券