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

148 阅读3分钟

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

WEB开发的安全之旅

攻击篇

XSS跨站攻击

  • 什么是XSS

    • XSS(Cross Site Script)跨站脚本攻击是指攻击者在Web页面里插入恶意Script代码,当用户访问网页时,该代码会执行如<script>alert("text");</script>
  • XSS主要利用了什么

    • 盲目信任用户的提交内容
    • 直接把用户提交的字符串转为DOM如document.write,element.innerHTML= anyString;
  • XSS的一些特点

    • 难以从UI上感知,攻击者通常在暗地执行脚本
    • 窃取用户信息(cookie、token)
    • 绘制UI(例如弹窗),诱骗用户点击/填写表单
  • XSS的Demo(此Demo为存储型XSS攻击的一种形式)

public async submit(ctx){
    const {content,id} = ctx.request.body;
    //没有对content进行过滤就直接存入数据库了
    await db.save({
        content,
        id
    });
}
public async render(ctx){
    //从数据库读取content
    const {content} = await db.query({
        id:ctx.query.id
    });
    //此处也没有对content过滤,直接使用模板字符串渲染dom
    ctx.body = `<div>${content}</div>`;
}

那么如果用户在输入框等地方进行提交<script>alert("text");</script> 实际提交的请求就是

fetch("/submit",{
    body:JSON.stringify({
        id:"1",
        content: `<script>alert("text");</script>`
    });
});

那么最后实际的ctx.body = <div><script>alert("text");</script></div>

  • 存储型XSS攻击
    • 恶意脚本被存在数据库中
    • 访问页面->读数据->被攻击
    • 危害最大,对全部用户可见
  • 反射型XSS攻击
    • 不涉及数据库
    • 从url上攻击
    • host/path/?param=<script>alert("text");</script>,服务器会从用户请求的参数中读取片段
  • 基于DOM的XSS攻击
    • 不需要服务器的参与
    • 恶意攻击的发起和执行全在浏览器完成
    • 与反射型相似,但是反射型是在服务端进行注入,而基于DOM的XSS注入完全是由浏览器完成
  • 基于Mutation的XSS攻击
    • 利用浏览器渲染DOM的特性(独特优化)
    • 不同浏览器会有区别(按浏览器攻击)
    • <noscript><p title ="</noscript><img src=x onerror=alert(1)>"> 在很多的xss过滤工具中会把title属性当做合法的内容。将该代码放入chrom浏览器中,则会解析成
<div>
    <noscript><p title ="</noscript>
    <img scr = "x" onerror = "alert(1)">
    "">"
</div>

CSRF跨站伪造请求

  • 特点

    • 用户不知情的前提下
    • 利用用户权限
    • 构造指定hhttp请求,窃取或修改用户敏感信息
  • 攻击方式方式

    • 构造一个get的请求如a标签,img标签
    • 构造一个post请求表单
  • 分类

    • SQL注入
    • CLI
    • OS command
    • SSRF(严格来说不算注入,但是原理类似)

Injection

  • SQL Injection(SQL注入)
    • 原理:处理请求字段的时候采取直接拼接SQL语句的方法
    • 如用户在登录框中输入 text;DROP TABLE table; 如果不做预编译处理直接拼接字符串,则数据库执行的语句可能是 select text from tb_user;DROP TABLE table
  • Injection demo2
//执行
public saync converVideo(ctx){
    const {video,options} = ctx.request.body
    exec(`convert-cli ${video} -o ${options};
    ctx.body = "ok"
}

fetch("/api",{
    method: "POST",
    body:JSON.stringfy({ option : `‘ && rm -rf xx` }) 
})

//读取+修改
暴露重要文件如
/etc/passwd
/etc/shadow
~/.ssh
/etc/apache2/http.conf
/etc/nginx/nginx.conf
  • SSRF demo
    • 请求用户自定义的callback URL
    • web server通常有内网访问权限
public async webhook(ctx){
    //callback可能是内网url
    //如 http://secret.com/get-employ_payrolls
    ctx.body = await fetch(ctx.query.callback);
    //访问callback相当于暴露内网信息
}

DoS

  • 定义:通过某种方式构造特定请求导致服务器资源被消耗,来不及响应更多请求导致请求挤压,进而雪崩效应
  • 正则表达式--贪婪模式
    • 在写正则时使用?则是匹配一个即可,不使用则是尽量多
const greedyRegExp = /a+/;//有多少匹配多少
const nonGreedyRegExp = /a+?/;//有一个就行
const str = "aaaaaaaaaaa"
console.log(str.match(greedyRegExp)[0]);//aaaaaaaaaaa
console.log(str.match(nonGreedyRegExp)[0]);//a
  • 基于正则表达式的Dos
    • 贪婪:n次不行,n-1次再试试?-回溯
  • DDos
    • 不搞复杂的,量大就完事
    • 特点:直接访问ip,任意api,主要想消耗带宽
    • 原理:攻击者发大量SYN,服务器按规范返回ACK+SYC,当时攻击者此时不会返回第三次ACK,导致三次握手没有完成,连接无法释放,很快达到最大连接数,新请求无法继续

image.png

传输层:中间人攻击

  • 利用原理

    • 明文传输
    • 信息篡改不可知
    • 对方身份未验证

image.png

防御篇

XSS

  • 永远不信任用户的提交内容
    • 不要将用户提交内容直接转成DOM,应该转为String
  • XSS工具
    • 前端
      • 主流框架默认防御XSS
      • goole0closure-librar
    • 服务端(Node)
      • DOMPurify
  • 如果需求需要动态生成DOM
    • string->dom 一定要转义
    • 上传svg 对svg进行扫描
    • 用户自定义跳转链接 进行过滤
    • 用户自定义样式

Same-origin Policy(SoP同源策略)

  • 同源是什么
    • 两个url上的协议,域名,端口号相同

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
    • 如果Origin和Referer是自己的域名就放行
  • token
    • 验证token
    • token必须和具体用户绑定,才可以防止其他用户利用
    • token必须设置过期时间
  • CSRF--iframe攻击
    • X-Frame-Options:DENY/SAMEORIGIN 规避该攻击
    • DENY当前页面不能作为iframe加载
    • SAMEORIGIN 必须是同源才记载iframe
  • CSRF anti-pattern
    • GET !== GET+POST
    • 不能将更新获取等功能放在同一个get接口,该是get就是get,该是post就是post
  • SameSite Cookie
    • 从根源上解决CSRF的攻击
    • 我的Cookie只能为我所用,不携带第三方cookie
    • 限制的是Cookie的domian属性和当前页面的域名是否匹配
    • 如果依赖cookie的第三方服务怎么办
      • Set-Cookie:SameSIte=None; Secure;
      • 服务器端设置属性为none,不对samesite进行限制,但是表明cookie为secure,已确认安全
  • SameSite 对比 CORS
    • SameSite
      • Cookie发送
      • domain属性对比页面域名
      • 出了这个屋就不认了
    • CORS
      • 资源读写(http请求)
      • 资源域名对比页面域名
      • 白名单
  • 防御CSRF的正确姿势
    • 从node角度讲,应该在中间生成中间件完成CSRF的防御

Injection SQL防御

  • 使用prepared statement
  • 以Java中Mybatis进行增删改查为例
    • 当使用$符号插入参数,则进行的是字符串拼接
    • 使用#符号插入参数,其实是将输入内容作为参数

Injection beyond SQL

  • 最小权限原则
  • 建立允许名单+过滤
    • 预防rm -rf
  • 对url类型参数进行协议,域名,ip等限制
    • 避免攻击者访问内网

DoS

  • 代码Review,避免正则表达式写出贪婪正则
  • 代码扫描+正则性能测试
  • 拒绝使用用户提供的正则

DDos

  • 流量治理
    • 负载均衡
    • API网关
    • CDN
  • 快速自动扩容
  • 非核心服务降级
    • 容出更多计算资源应对集中流量

传输层-防御中间人

  • HTTPS(其实HTTP3页内置了TLS1.3)
    • HTTP,TLS
    • 特点
      • 可靠性:加密
      • 完整性:MAC验证,防止篡改
      • 不可抵赖性:数字签名
    • HTTPS加密过程简介
      • TLS握手以及非对称加密和对称加密过程

image.png

  • HTTPS特性简介
    • 完整性
      • 传输内容:加密信息+加密信息_hash
      • 接收方将加密信息hash,与传输来的加密信息_hash对比,如果不一样则被篡改了
    • 数字签名
      • 签名执行者,包含私钥,公钥
      • 过程:签名执行者使用私钥加密,生成对应签名,具备公钥的人使用公钥进行校验
    • 不可抵赖性:数字签名
      • CA:Certificate Authority(证书机构)完成相关签名工作
        • 比如服务提供方将公钥合并成信息,使用CA提供的私钥进行签名,生成真正服务器端保存的证书,证书传递给浏览器侧,浏览器侧使用CA颁发的私钥进行校验,如果通过,则身份可信
      • 每个浏览器里面都会有大量CA颁发的证书,证书包含CA的各个公钥
      • 成也证书,败也证书,当签名算法不够强壮,签名算法可以被暴力破解
  • HSTS
    • 将http请求主动升级到https
    • 流程
      • 浏览器端向服务端发出一次https请求
      • 服务器接收请求后返回Strict-Transport-Securit这个请求头部并且传递一个值max-age:3600s,在这之后的这么多秒内,如果发送的http请求都升级为https请求
    • 缺点:一定要先有一次https请求
  • Subresource Integrity(SRI)
    • 如果cdn被攻陷,导致静态资源被劫持
      • 实际是对比hash值
      • 伪代码:
//integerity表明了当前摘要的算法以及具体值
<script src = "https://example/app.js" 
        integerity="sha384-{some-hash-value}"
        crossoring="anonymous">
        
//拿到资源后会将其进行计算,对比hash值,如果不同可能被篡改了
const remoteHash = hash(content);
if(tagHash!== remoteHash){
    throw new Error("wrong hash");
}
  • Feature Policy/Permission Policy
    • 一个源页面下可以使用哪些功能
      • camera
      • microphone
      • autoplay