Web开发安全|笔记

122 阅读6分钟

Web开发安全

使用的依赖(npm package, 甚至是NodeJS)可能成为最薄弱的一环

npm install除了带来了黑洞, 还可以带来漏洞

攻击篇

Cross-Site Scripting(跨站脚本攻击)-XSS

image-20241118191425053.png

主要利用

image-20241118191753212.png

特点
  • 通常难以从 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>`;
}

攻击者可以直接提交恶意脚本

image-20241118192701462.png

分类
Stored XSS(存储型XSS)
  • 恶意脚本被存在数据库中

  • 访问网页=>读数据=>被攻击

  • 危害最大,对全部用户可见

image-20241118193211986.png

Reflected XSS(反射型XSS)
  • 不涉及数据库

  • 从URL上攻击

    恶意修改要匹配的字段 点击即命中

    image-20241118193522879.png

DOM-based XSS(DOM型XSS)
  • 不需要服务器的参与

  • 恶意攻击的发起+执行 全在浏览器完成

    image-20241118193930695.png

Reflected VS DOM-based
  • 完成注入脚本的地方不同

    image-20241118194140714.png

Mutation-based XSS(mXSS)

是一种特殊类型的跨站脚本攻击,它利用了浏览器在解析和渲染HTML时的行为变化。在这种情况下,即使输入数据在服务器端被清洗和验证为安全,但在客户端渲染时,浏览器可能会以意想不到的方式改变HTML结构,从而产生安全漏洞

  • 利用了浏览器渲染DOM的特性(独特优化)

  • 不同浏览器会有区别(按浏览器进行区别攻击)

  • 最难防御

    image-20241118194653157.png

CroCross-site request forgery(跨站伪造请求)-CSRF

特点
  • 在用户不知情的前提下

  • 利用用户权限(cookie)

  • 构造指定 HTTP 请求,窃取或修改用户敏感信息

    image-20241118195645435.png

iframe攻击

同源请求

image-20241118213714407.png

点击button会先点击到iframe

CSRF-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"/>

SQL Injection(SQL注入)

image-20241118200238577.png

例子
  1. 读取请求字段

  2. 直接以字符串的形式拼接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}
            AND form_id = ${form_id}
        `);
        ctx.body = renderForm(result);
    }
    

Injection不止于SQL

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

image-20241118201336283.png

OS command

image-20241118201459635.png

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

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

  1. 请求[用户自定义]的callback URL

  2. web server 通常有内网访问权限

    public async webhook(ctx) {
        // callback 可能是内网 url
        // e.g http://secret.com/get_employ_payrolls
        ctx.body = await fetch(ctx.query.callback);
    }
    

    访问 callback === 暴露内网信息

Denial of Service(拒绝服务攻击)-DoS

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

正则表达式-贪婪模式

重复匹配时「?」vs「no?」:满足 一个即可 vs 尽量多

const greedyRegExp = /a+/;//有多少匹配多少
const nonGreedyRegExp =/a+?/;//有一个就行
const str = "aaaaaa";
console.log(str.match(greedyRegExp)[0]); // "aaaaaa"
console.log(str.match(nonGreedyRegExp)[0]); // "a"
ReDoS: 基于正则表达式的DoS

贪婪:n次不行?n-1次再试试?-- 回溯

image-20241118204018615.png

响应时间+

接口吞吐量-

Distributed DoS(DDoS)

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

DoS特点
  • 耗时的同步操作
  • 数据库写入
  • SQL join
  • 文件备份
  • 循环执行逻辑
例子:洪水攻击

image-20241118204623549.png

基于传输层的攻击

中间人攻击

image-20241118204826695.png

防御篇

XSS的防御

  • 永远不信任用户的提交内

  • 不要将用户提交内容直接转换成DOM

    image-20241118205138022.png

防御XSS的现成工具

前端

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

服务端(Node)

  • DOMPurify(npm包)
【用户需求】必须动态生成DOM
string->DOM
new DOMParser();
上传svg
<svg>
    <script>alert("xss");</script>
</svg>
尽量不要让用户进行自定义跳转操作 Bolb动态生成script
const blob = new Blob(
    [script],
    { type: "text/javascript"},
);
const url= new URL.create0bjectURL(blob);
const script = document.createElement("script");
script.src = url;
自定义样式

image-20241118210306594.png

Same-origin Policy(同源策略)

源(origin)由协议(如HTTP或HTTPS)、域名(或IP地址)和端口号组成。只有当这三个都相同时,才被认为是“同源

image-20241118210720721.png

1/2协议不同 2/3域名不同

HTTP请求:同源 OK 跨域 NO(要看请求的类型以及服务器配置)

Content Security Policy(内容安全策略)-CSP

  • 允许开发者定义哪些源(域名)被认为是安全的
  • 来自安全源的脚本可以执行,否则直接抛错
  • 对 eval + inline script 说不
例子
服务器的响应头部
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

token防御机制

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

if(请求来自合法页面)

then(服务器接收过页面请求)

then(服务器可以标识)

image-20241118213131787.png

  1. 用户绑定:攻击者也可以是注册用户===可以获取自己的token
  2. 设置过期时间
iframe的防御

设置X-Frame-Options响应头部:DENY/SAMEORIGIN

CSRF anti-pattern(CSRF防御反模式)

GET !== GET + POST

//将更新+获取逻辑放到同一个GET接口  应该把各个接口按照职责划分开  
public async getAndUpdate(ctx) {
    const { update, id } = ctx.query;
    if (update) {
    await this.update(update);
    }
    ctx.body = await this.get(id);
}
SameSite Cookie(避免用户信息被携带

image-20241118214853396.png

image-20241118214922220.png

依赖于Cookie的第三方服务怎么办?

eg.内嵌一个某站播放器,识别不了用户登录态,发不了弹幕

Set-Cookie: SameSite=None; Secure;

image-20241118215608947.png

SameSite vs CORS
  • SameSite

    • Cookie 发送
    • domain vs 页面域名
    • “我跟你说个事儿,出这屋我可就不认了"
  • CORS

    • 资源读写(HTTP 请求)
    • 资源域名 vs 页面域
    • 白名单

防御CSRF的正确姿势

image-20241118215727689.png

Injection的防御

SQL
  • 找到项目中查询 SQL 的地方
  • 使用 prepared statement
PREPARE q FROM 'SELECT user FROM users WHERE gender = ?';
SET @gender = 'female';
EXECUTE q USING @gender;
DEALLOCATE PREPARE q;
除SQL外其他的注入攻击
  • 最小权限原则

    • 所有的命令都不要通过sudo来执行,不要给root权限
  • 建立允许名单 +过滤

    • 只允许指定命令执行,禁止rm操作
  • 对 URL 类型参数进行协议、域名、ip 等限制,避免攻击者访问内网资源

DoS的防御

Regex DoS
  • 完善代码Review的工作(避免写出贪婪匹配的方式/(ab*)+/)
  • 代码扫描 +正则性能测试
  • 拒绝使用用户提供的使用正则
DDoS
  • 流量治理

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

  • 非核心服务降级 抗量

传输层的防御

防御中间人

image-20241119183913105.png

HTTPS的特性
  • 可靠性

    • 加密 避免了明文传输
  • 完整性

    • MAC验证规则 确保信息不被篡改

    image-20241119184626857.png

  • 不可抵赖性

    • 数字签名 确保了双方身份是可被信任的

    image-20241119184649050.png

HTTPS的大致流程

image-20241119184333784.png

  • 证书

    image-20241119185334163.png

    image-20241119185557900.png

  • 当签名算法不够健壮时:

    image-20241119185852077.png

HTTP Strict-Transport-Security(将HTTP 主动升级到 HTTPS)-HSTS

image-20241119190103672.png

  • 需要先有一次HTTPS请求
Subresource Integrity(SRI)
  • CDN:静态资源托管的地方

    image-20241119190502659.png

  • 静态资源被劫持篡改怎么办-SRI

    • 例子

    • 标签hash(原始内容hash)

      <script src="https://example/app.js" integrity="sha384-{some-hash-value}" crossorigin="anonymous"></script>
      
    • 实际内容hash

      const remoteHash = hash(content);
      if (tagHash !== remoteHash) {
          throw new Error("wrong hash");
      }
      

补充:Feature Policy(特性策略)/Permission Policy(权限策略)

是Web开发中的两个相关概念,它们用于控制和限制网页可以访问的浏览器API和特性。

Feature Policy

Feature Policy允许开发者声明哪些源(页面)(origins)可以使用特定的API和浏览器特性。这通过在HTTP头部中设置Feature-Policy字段来实现。

例如,不希望页面使用全屏API,可以设置如下策略:

Feature-Policy: fullscreen 'none';

这告诉浏览器,不允许任何源使用全屏API。

可以通过指定不同的值来允许某些源使用特定的特性:

  • 'self':仅允许当前源使用特性。
  • 'none':不允许任何源使用特性。
  • 指定的域名,如example.com:允许指定的域名使用特性。
  • '*':允许所有源使用特性。
Permission Policy

Permission Policy(有时称为Permissions Policy)是Feature Policy的一个子集,它专注于控制Web页面请求权限的能力。这些权限包括访问地理位置、摄像头、麦克风等。Permission Policy也通过HTTP头部实现,但使用Permissions-Policy字段。

例如,不希望页面访问用户的麦克风,可以设置如下策略:

Permissions-Policy: microphone=();

或者,允许当前源访问麦克风,但不允许其他源访问:

Permissions-Policy: microphone=(self);
共同点和区别
  • 共同点:Feature Policy和Permission Policy都提供了一种机制,允许开发者限制页面对某些API和特性的访问。
  • 区别:Feature Policy更广泛,包括了浏览器特性和API的控制,而Permission Policy专注于权限控制。
为什么使用这些策略?

如果不小心被攻击了,那我们至少还可以限制这个页面不能调用一些敏感的东西,比如用户的照相机