Web 开发的安全之旅 | 青训营笔记

101 阅读5分钟

这是我参与「第四届青训营 」笔记创作活动的第6天

Web 开发的安全之旅

安全问题“很常见”,会危害

  • 用户
  • 公司
  • 星字员(祭天)

两个角度看web安全

  • 假如你是一个hacker——攻击
  • 假如你是一个开发者——防御

攻击篇

Cross-Site Scripting(XSS)

XSS攻击通常指的是通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。

XSS主要利用了:

  • 盲目信任用户提交的内容
  • sring->DOM
  • document.wirte
  • element.innerHTML = anyString
  • SSR(user_data) //伪代码

特点

  • 通常难以从 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: JS0N.stringify({
        id: "1",
        content: '<script>alert( "xss" );</script>`
    });
});
ctx.body =`
    <div>
        <script>alert("xss");</script>
    </div>`;

存储型 Stored XSS

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

反射型 Reflected XSS

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

image (2).png

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

在URL参数上加入恶意脚本,服务端解析参数放入响应体中,导致恶意脚本注入成功

DOM型 Dom-based XSS

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

image (3).png

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

Mutation-based XSS

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

过滤工具:平平无奇的title属性

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

img标签的src属性出错,导致执行onerror里的内容,这里可以注册恶意脚本,进行攻击

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

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

demo

  1. 用户没有访问银行页面
  2. 银行页面中的特定接被请求
  3. 请求执行成功

image (4).png

有cookie的请求===合法用户的请求

SQL Injection

image (5).png

Injection demo

  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);
}
fetch("/api", {
    method : "POST",
    headers: {
    	"Content-Type" : "applicaiton/ json"
    },
    body:JS0N.stringify({
    	username:"any ; DROP TABLE table; ",
	})
})

Injection不止于SQL

  • CLI
  • os command
  • server-side Request Forgery(SSRF),服务端伪造请求
    • 严格而言,SSRF不是injection,但是原理类似

Denial of Service(Dos)

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

正则表达式——贪婪模式

重复匹配时「?」 vs 「no ?」:满⾜“⼀个” 即可 vs 尽量多

ReDoS 基于正则表达式的Dos

贪婪:n 次不⾏?n – 1 次再试试?——回溯

暴力回溯,会导致系统的响应时间边长,接口的吞吐量下降

image (6).png

Distributed DoS

攻击的特点

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

image (7).png

防御篇

XSS 防御

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

前端

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

服务端(Node)

  • DOMPurify

Conten Security Policy(CSP)

  • 拿些源被认为是安全的
  • 来自安全源的脚本可以执行,否则直接抛出错误
  • 对eval + inline script直接拒绝

服务器的响应头部

image (8).png

浏览器meta

image (9).png

CSRF 防御

if 伪造请求===异常来源 then 限制请求来源--->限制伪造请求

  • origin
    • 同源请求中,GET + HEAD不发送
  • Referer

token

除了Origin + Referrer

其他判断【请求来自于合法来源】的方式

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

image (10).png

  1. 用户绑定:攻击者也可以是注册用户===可以获取自己的token
  2. 过期时间:【前向保密】

iframe攻击

image (11).png

anti-pattern

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 (12).png

SameSite Cookie

image (13).png

限制的是:

  1. Cookie domain
  2. 页面域名

SameSite vs CORS

  • SameSite
    • Cookie 发送
    • domain vs页面域名
    • “我跟你说个事儿,出这屋我可就不认了"
  • CORS
    • 资源读写(HTTP请求)
    • 资源域名vs页面域名
    • 白名单

SameSite demo

image (15).png

防御CSRF的正确姿势

做一个防御CSRF中间件,负责生成各类防御策略,在一处保证整个Web App都能对CSRF的防御

image (16).png

Injection 防御

  • 找到项目中查询SQL的地方
  • 使用prepared statenent
PREPARE q FROM 'SELECT user FROM users WHERE gender = ? ';
SET @gender = 'female' ;
EXECUTE q USING @gender;
DEALLOCATE PREPARE q;

Injection beyond SQL

  • 最小权限原则
    • sudo ll root
  • 建立允许名单+过滤
    • rm
  • 对URL类型参数进行协议、域名、ip等限制
    • 访问内网

DOS 防御

Regex DOS防御

  • 完善Code Review,拒绝写出类似/(ab*)+/贪婪的匹配正则表达式
  • 使用一些代码扫描工具去——> 代码扫描 + 正则性能测试
  • 拒绝用户提供的使用正则

DDoS 防御

  • 流量治理
    • 负载均衡
    • API 网关
    • CDN
  • 快速自动扩容
  • 非核心服务降级

传输层——防御中间人

image (17).png

HTTP3(QUIC)内置了TLS 1.3

HTTPS的一些特性

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

TLS 1.2

image (18).png

HTTPS------完整性

image (19).png

插播:数字签名

image (20).png

HTTPS-------不可抵赖:数字签名

CA: Certificate Authority 证书机构

数字签名在HTTPS中的工作

image (21).png

HTTP Strict-Transport-Security (HSTS)

将HTTP主动升级到HTTPS

image (22).png

Subresource Integrity (SRI)

静态资源被劫持篡改?对比 hash

image (23).png

SRI---demo

标签hash(原始内容hash) vs 实际内容hash

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

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