Day10:Web 开发安全 - 攻击篇 | 青训营笔记

312 阅读3分钟

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

Cross-Site Scripting(XSS)

盲目信任用户的提交内容,直接将其拼接入DOM中,从而导致恶意代码被插入。

常见的拼接方式:

  • document.write();
  • element.innerHTML = anyString;
  • SSR(user_data)

攻击特点:

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

例子:

public async submit(ctx) {
    const { content, id } = ctx.request.body;
    await db.save({
        content,  // 没有对 content 过滤
        id,
    });
}

public async render(ctx) {
    const { content } = await db.query({
        id: ctx.query.id,
    });
    ctx.body = `<div>${content}</div>`;  // 没有对 content 过滤
}

分类

  • 存储型XSS(Stored XSS)

    • 恶意脚本被存在数据库中
    • 访问页面(读数据)= 被攻击
    • 危害最大,对全部用户可见
  • 反射型XSS(Reflected XSS)

    • 不涉及数据库
    • 从URL上攻击
    • 要服务端对请求渲染后返回浏览器执行才能完成攻击

    例如,对于下述请求渲染函数

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

    构造如下请求URL:

    host/path/?param=<script>alert('123')</script>
    
  • DOM-based XSS

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

    例如:(浏览器脚本中存在如下代码)

    const content = new URL(location.href).searchParams.get("param");
    const div = document.createElement("div");
    div.innerHTML = content;  // 没有对 content 过滤
    document.body.append(div);
    
  • Mutation-based XSS

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

    例如:利用部分浏览器对title属性的错误处理方式

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

    浏览器会错误渲染为:

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

Cross-Site Request Forgery(CSRF)

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

【场景示例】

  • 用户没有访问银行页面
  • 恶意页面请求网银转账(请求自动附带上用户机器中的Cookie)
  • 网银服务器检查Cookie是合法的之后,完成请求

【具体例子】

  • 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" />
    
  • POST请求

    <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 注入

类似于XSS,只是改为通过网页请求传递精心设计的查询参数,使得SQL查询执行攻击者期望的动作。

攻击的关键点:直接将HTTP请求参数拼接入SQL语句中

【例子】

public async renderForm(ctx) {
    const { username, form_id } = ctx.query;
    const result = await sql.query(`
        SELECT a, b, c FROM table
        WHERE username = ${username}
	`);
    ctx.body = renderForm(result);
}

如果username取值为"any; DROP TABLE table;",那么就会执行如下SQL语句:

SELECT a, b, c FROM table
WHERE username = any;
DROP TABLE table;

从而导致数据表被删除。

Injection 不止于SQL

  • CLI

    【例子】

    public async convertVideo(ctx) {
        const { video, options } = ctx.request.body;
        exec(`convert-cli ${video} -o ${options}`);
        ctx.body ="ok";
    }
    

    如果options取值为 && rm -rf xxx,那么就会执行如下CLI命令:

    convert-cli somevideo.mp4 -o && rm -rf xxx
    

    从而导致文件被删除。

  • 服务端伪造请求(Server-Side Request Forgery,SSRF)

    严格而言,SSRF不是injection,但是原理类似

    借用服务端的内网访问权限来获取内网的信息

假设可以通过注入获取系统文件的读取/修改权限,特别是一些重要的配置文件:

  • /etc/passwd
  • /etc/shadow
  • ~/.ssh
  • /etc/apache2/httpd.conf
  • /etc/nginx/nginx.conf

那么,有可能会导致服务端称为攻击者的攻击工具。例如,修改NGNIX配置使得所有访问请求都跳转到攻击者设计需要攻击的网址,那么这一网址相当于接收所有该服务器的流量,实现了DDoS攻击(见下一节)。

Denial of Service(DoS)

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

  • ReDoS:基于正则表达式的 DoS

  • Logical DoS:反复触发复杂的数据库请求

    • 耗时的同步操作
    • 数据库写入
    • SQL join
    • 文件备份
    • 循环执行逻辑
  • Distributed DoS(DDoS):短时间内,来自大量僵尸设备的请求流量,服务器不能及时完成全部请求,导致请求堆积,进而雪崩效应,无法响应新请求。

    攻击特点:

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