Web 开发安全 | 青训营笔记

108 阅读4分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 5 天

Web 开发安全

Web 攻击

Cross-Site Scripting(XSS)

跨站脚本攻击(Cross-Site Scripting,也称为 XSS)指利用网站漏洞从用户那里恶意盗取信息。

XSS 主要利用开发者盲目信任用户的提交内容将用户提交的字符串转换为 DOM 元素两个特点来进行攻击。

XSS 的特点:

  • 通常难以从 UI 上感知(暗地执行脚本)
  • 窃取用户信息(cookie / token)
  • 绘制 UI(例如弹窗),诱骗用户点击 / 填写表单

XSS demo:

public async submit(ctx) {
    const { content, id } = ctx.request.body;
    // 没有对 content 过滤
    await db.save({
        content,
        id        
    });
}
public async render(ctx) {
    const { content } = await db.query({
        id: ctx.query.id
    });
    // 没有对 content 过滤
    ctx.body = `<div>${content}</div>`;
}
​
// 提交时候
fetch("/submit", {
    body: JSON.stringify({
        id: "1",
        content: `<script>alert("xss");</script>`
    })
})
​
// 此时 ctx.body 为附带攻击脚本的内容
ctx.body = `
    <div>
        <script>alert("xss");</script>
    </div>`;

Stored XSS

  • 恶意脚本被存在数据库中
  • 访问页面 -> 读数据 === 被攻击
  • 危害最大,对全部用户可见

Reflected XSS

  • 不涉及数据库
  • 从 URL 上攻击

Demo:

在 url 地址附带攻击内容:host/path/?param=<script>alert('123')</script>

public async render(ctx) {
    const { sparam } = ctx.query;
    ctx.status = 200;
    ctx.body = `<div>${param}</div>`;
}

DOM-based XSS

  • 不需要服务器的参与
  • 恶意攻击的发起 + 执行,全在浏览器完成

Demo:

在 url 地址附带攻击内容:host/path/?param=<script>alert('123')</script>

const content = new URL(location.href).searchParams.get("param");
const div = document.createElement("div");
// 恶意脚本注入
div.innerHTML = content;
document.body.append(div);

Reflected XSS 和 DOM-based XSS 区别在于完成脚本注入的地方

Mutation-based XSS

  • 利用了浏览器渲染 DOM 的特性(独特优化)
  • 不同浏览器,会有区别(按浏览器进行攻击)

Demo:

利用 title 属性:

<noscript><p title="</noscript><img src=x onerror=alert(1)">

DOM 节点会被解析成:

Mutation-basedXSS.png

Cross-site request forgery(CSRF)

  • 在用户不知情的前提下
  • 利用用户权限(cookie)
  • 构造指定 HTTP 请求,窃取或修改用户敏感信息

Demo:

CSRF.png

GET

<a href="https://bank.com/transfer?to=hacker&amount=100">点我抽奖</a>

<img style="display:none;" src="https://bank.com/transfer?to=hacker&amount=100/>

beyond GET

<form action="https://bank/transfer_tons_of_money" method="POST">
    <input name="amount" value="1000000000000" type="hidden"/>
    <input name="to" value="hacker"type="hidden"/>
</form>

Injection

SQL Injection

SQLInjection.png

服务端被攻击的过程:

  1. 读取请求字段
  2. 直接以字符串的形式拼接 SQL 语句
  3. 执行操作,例如 SELECT XXX FROM XXX; DROP TABLE table; ,寄

除了 SQL,还可以通过以下方式注入:

  • CLI
  • OS command
  • Server-Side Request Forgery(SSRF),服务端伪造请求(严格来讲,SSRF 不是 injection,但原理类似)

Denial of Service(DoS)

通过某种方式(构造特定请求),导致服务器资源被显著消耗,来不及响应更多请求,导致请求挤压,进而雪崩效应。

ReDos:基于正则表达式的 DoS

Logical Dos

  • 耗时的同步操作
  • 数据库写入
  • SQL join
  • 文件备份
  • 循环执行逻辑

Distributed DoS(DDoS)

短时间内,来自大量僵尸设备的请求流量,服务器不能及时完成全部请求,导致请求堆积,进而雪崩效应,无法响应新请求。

攻击特点:

  • 直接访问 IP
  • 任意 API
  • 消耗大量带宽(耗尽)

中间人攻击

Web 防御

XSS 的防御

  • 永远不要相信用户的提交内容
  • 不要将用户提交的内容直接转换成 DOM

现成的防御工具:

  • 前端

    • 主流框架默认防御 XSS
    • google-closure-library
  • 服务端

    • DOMPurify

Content Security Policy(CSP)

  • 哪些源(域名)被认为是安全的
  • 来自安全源的脚本可以执行,否则直接抛错
  • 对 eval + inline script 说 no

方法:

  • 在服务器的响应头部加上

    Content-Security-Policy: script-src'self! // 同源
    Content-Security-Policy: script-src 'self' https://domain.com
    
  • 在浏览器添加 meta 元素

    <meta http-equiv="Content-Security-Policy" content="script-src self">
    

防御 CSRF 的正确姿势

请求头部逻辑

if 伪造请求 === 异常来源
then 限制请求来源 -> 限制伪造请求
- Origin
    - 同源请求中,GET + HEAD 不发送
- Referer

除了 Origin + Referrer,其他判断【请求来自于合法来源】的方式:

if(请求来自合法页面)
then (服务器接收过页面请求)
then (服务器可以标识)

防御 iframe 攻击

  • X-Frame-0ptions: DENY/SAMEORIGIN

anti-pattern

SameSite Cookie 防御

防御 DoS

Regex DoS

  • Code Review(防止出现 /(ab*)+/)
  • 代码扫描 + 正则性能测试
  • 防止用户提供的使用正则

Logical DoS

  • 不是非黑即白,有些 case,只有在请求量大到一定程度之后才会体现

  • 分析代码中的性能瓶颈

    • 同步调用
    • 串行逻辑
    • CPU 密集型操作
  • 限流

  • 流量治理

    • 负载均衡(过滤)
    • API 网关(过滤)
    • CDN(抗量)
  • 快速自动扩容(抗量)

  • 非核心服务降级(抗量)

防御 Injection

  • 找到项目中的查询 SQL 地方
  • 使用 prepared statement
  • 最小权限原则

    • 不要使用 sudo || root
  • 建立允许名单 + 过滤

    • 不要使用 rm 命令
  • 对 URL 类型参数进行协议、域名、ip 等限制

    • 不要允许访问内网

防御中间人

通过 HTTPS 的一些特性防御:

  • 可靠性:加密
  • 完整性:MAX 验证
  • 不可抵赖性:数字签名