Web安全之CSRF攻击

前言

上一篇文章中介绍了XSS,这篇文章介绍CSRFjavascript

CSRF
CSRF是什么

Cross-site request forgery, 跨站请求伪造。是指黑客引诱用户打开黑客的网站,在黑客的网站中,利用用户的登陆状态发起跨站请求。html

我本身模拟了一个例子,java

  • 首先登陆www.abc.com

在这个页面内,我会自动向服务器发起一个get请求,服务器响应这个请求的同时会向浏览器发送cookie
6.pngexpress

  • 在同一浏览器下登陆www.haha.com

在这个页面内,作了三个事情segmentfault

  1. 利用img标签自动向localhost:8899发送了一个get请求
  2. 利用js自动向localhost:8899发送了一个post请求
  3. 经过引诱用户点击a标签,向localhost:8899发送了一个post请求

8.png
经过截图能够看到,三个请求都带了www.abc.com下的cookie发送给了localhost:8899服务器,而且localhost:8899服务器也正常响应了。
流程图以下:
process.jpg跨域

防止CSRF

黑客发起CSRF攻击的条件浏览器

  • 目标网站必定要有CSRF漏洞,黑客破解了服务器的接口
  • 用户登陆过目标网站,而且浏览器保存目标网站的登陆状态
  • 用户在同一浏览器下以某种方式打开了黑客的网站或者是攻击的连接

CSRF攻击与XSS攻击不一样,CSRF攻击不会往页面内注入恶意脚本,所以黑客是没法经过CSRF攻击来获取用户页面数据的,因此主要由服务器来作预防。
主要有如下几种方式:安全

  1. 充分利用好cookie的SameSite属性

SameSite选项一般由Strict、Lax和None三个值服务器

  • Strict最为严格,若是cookie设置了Strict,那么浏览器会彻底禁止第三方Cookie。
  • Lax相对宽松一点,在跨站点的状况下,从第三方站点的连接打开和从第三方站点提交Get的表单都会携带cookie.可是若是在第三方站点中使用Post方法或者经过img、iframe等标签加载的URL,都不会携带Cookie。
  • None, 任何状况下都会发送Cookie。

可是如今大部分的网站静态资源都放在单独的域名下,因此经过设置Cookie的SameSite为Strict、Lax是不能正常运行的,因此这个方法只适用静态资源跟服务器接口在同一个站点下的网站。
我测试了一下,以下图:
localhost:8899向www.abc.com写入SameSite值为Lax的cookie,写不进去
a_cookie_lax.pngcookie

locahost:8899向该站点下的localhost:8899/get写入SameSite值为Lax的cookie,成功写入。
fuwuqi_cookie_lax.png

  1. 验证请求的来源站点

在服务器端验证请求的来源站点。由于CSRF攻击大多数都是来自第三方站点。

经过http请求头中的Referer和Origin属性

  • referer属性

记录了该http请求的来源地址,但有些场景不适合未来源URL暴露给服务器,因此能够设置不用上传,而且referer属性是能够修改的,因此在服务器端校验referer属性并无那么可靠

  • origin属性

经过XMLHttpRequest、Fetch发起的跨站请求或者Post方法发送请求时,都会带上origin,因此服务器能够优先判断Origin属性,再根据实际状况判断是否使用referer判断。

  1. CSRF Token

除了上面两个方法以外,还可使用CSRF Token来验证。

  • 在浏览器向服务器发起请求时,服务器生成一个CSRF Token(字符串)发送给浏览器,而后将该字符串放入页面中
  • 浏览器请求时(如表单提交)须要带上这个CSRF Token。服务器收到请求后,验证CSRF是否合法,若是不合法拒绝便可。
代码

代码很简单

  • www.abc.com:3000
<div>
    <h6>CSRF测试</h2>
      <a href="http://www.haha.com:9999">点我点我</a>
      <script>
        fetch('http://localhost:8899', {
          method: 'get',
          mode: 'cors',
          credentials: 'include' //很重要,容许跨域访问传输cookie
        }).then((res) => {
          console.log(res)
        })
      </script>
  </div>
  • 服务器 localhost:8899
const express = require('express')
const app = express()
const port = 8899

//allow custom header and CORS
app.all('*',function (req, res, next) {
    // res.header('Access-Control-Allow-Origin', '*');
    // res.header('Access-Control-Allow-Origin', req.hostname);
    res.header('Access-Control-Allow-Origin', req.headers.origin);
    res.header('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild');
    res.header('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');
    res.header('Access-Control-Allow-Credentials', true); // 很重要,容许跨域访问传输cookie
  
    if (req.method == 'OPTIONS') {
      res.send(200); /让options请求快速返回/
    }
    else {
      next();
    }
  });

app.get('/', (req, res) => {
    // res.cookie('name', 'hey', { domain: req.hostname, path: '/'});
    res.cookie('name', 'hey', { domain: req.hostname, path: '/', sameSite: 'None'});
    // res.cookie('name', 'hey', { secure: true });
    res.send('Hello World!a')
})

app.get('/get/test', (req, res) => {
  res.cookie('username', 'hahah', { domain: req.hostname, path: '/', sameSite: 'Lax'});
  // res.cookie('name', 'hey', { secure: true });
  res.send('Hello World!a')
})

app.get('/get', (req, res) => {
  res.set('Content-Type', 'text/html')
  console.log(req.headers, 'header')
  const html = `
  <div>
    <div>服务器同站点下的页面</div>
    <script>
      function fetchUrl(url, method='get') {
        fetch(url, {
        method,
        mode: 'cors',
        credentials: 'include' //很重要,容许跨域访问传输cookie
      }).then((res) => {
        console.log(res)
      })

      }

      fetchUrl('http://localhost:8899/get/test')
    </script>
  </div>
  `
  res.send(html)
})

app.post('/post', (req, res) => {
  console.log(req.headers, 'header')
    // res.cookie('name', 'hey', { secure: true });
    res.send('I am post')
})

app.listen(port, () => console.log(`Example app listening on port ${port}!`))
  • 服务器 www.haha.com:9999
const express = require('express')
const app = express()
const port = 9999

//allow custom header and CORS
app.all('*',function (req, res, next) {
    // res.header('Access-Control-Allow-Origin', '*');
    // res.header('Access-Control-Allow-Origin', req.hostname);
    console.log(req.headers.origin)
    res.header('Access-Control-Allow-Origin', req.headers.origin);
    res.header('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild');
    res.header('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');
    res.header('Access-Control-Allow-Credentials', true); // 很重要,容许跨域访问传输cookie
  
    if (req.method == 'OPTIONS') {
      res.send(200); /让options请求快速返回/
    }
    else {
      next();
    }
  });

app.get('/', (req, res) => {
    res.set('Content-Type', 'text/html')
    console.log(req.headers, 'header')
    const html = `
    <div>
      <div>nihao</div>
      <img src="http://localhost:8899" />
      <a href="javascript: fetchUrl('http://localhost:8899/post', 'post');">你好,交个朋友吧</a>
      <script>
        function fetchUrl(url, method='get') {
          fetch(url, {
          method,
          mode: 'cors',
          credentials: 'include' //很重要,容许跨域访问传输cookie
        }).then((res) => {
          console.log(res)
        })

        }

        fetchUrl('http://localhost:8899/post', 'post')
      </script>
    </div>
    `
    res.send(html)
})

app.listen(port, () => console.log(`Example app listening on port ${port}!`))
最后

以前虽然知道CSRF的原理,可是没有实际模拟过,实际模拟以后,感受,原来真的那么简单。也算是一个小小收获

Web安全之XSS攻击

欢迎跟我一块儿挖坑、填坑。
image

相关文章
相关标签/搜索