Day3 初识Web安全 | 青训营

91 阅读8分钟

本文将从攻击和防御两个方面分别对Web安全进行入门研学

攻击篇

XSS攻击

好处:

  1. 难以从UI上感知
  2. 窃取用户信息(cookie/token)
  3. 诱骗点击
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>
}

这种时候要是提交内容含有

  1. Stored XSS,存储型XSS,危害最大

  2. Reflected XSS,反射型XSS,注入脚本的地方是Server服务器,直接读取url中的字段并生成Html片段,如ctx.body=<div>${param}</div>那么我们需要的param可能就会被searchParams get到param

  3. DOM-based XSS,注入脚本的地方的Browser浏览器,不需要服务器参与,全在浏览器完成

  4. Mutation XSS,没人比我更懂浏览器,利用noscript标签,不同浏览器的不同渲染方式不同,攻击者利用浏览器的渲染规则完成脚本的注入

  5. CSRF攻击,比如说我们可以把发给用户的页面绑定银行的请求,这样只要用户输入指定信息,就刑了。

  6. injection注入,请求带上SQL语句,被运行之后就会触发修改、删除数据...

    1. 删库跑路
    2. 读取修改,读取修改数据库的文件,比如可以修改nginx配置的话就把我们的流量转发到了别的服务器上,流量徒增就会导致别人的服务器被挤爆。
  7. DoS 通过构造特定请求,导致服务器资源被显著消耗,来不及响应更多请求导致请求被挤压,进而导致雪崩。 如何实现?正则表达式的贪婪模式,/a+/和/a+?/的区别,+?匹配一个,而不加则有多少匹配多少。攻击者利用贪婪匹配例如卡bug递归就能实现。eg:/^((ab)*)+$/,匹配由连续的 "ab" 子字符串组成的字符串,可如果我的匹配字符串为ababababa,显然多了一个a,这样则需要花费大量的时间对剩余的a进行回溯匹配,直到放弃。

  8. DDoS,就没那么复杂,就让僵尸号凹凹请求,在TCP三次握手的时候,最后一次我不返回给你,让你一直卡着,这样导致常见的洪水攻击

    如何解决?

    • 过滤:
      1. 流量治理:负载均衡、API网关、CDN
    • 抗量:
      1. 快速自动扩容、非核心服务降级
  9. 中间人攻击,明文传输、信息篡改不可知、对方身份未验证

防御篇

防御XSS攻击

  1. 主流框架默认防御XSS
  2. Node:DOMPurify

提防上传SVG

预防用户在svg中插入有害脚本,对svg进行扫描,常用采取手段如下:

  1. 输入验证,确保SVG输入的应用程序或者服务在用户提供SVG之前进行验证,比如对输入进行过滤和长度的检查。比如如下就是一个防止输入时XSS攻击的例子:
function sanitizeInput(inputStr) {
  // 过滤非法字符
  const sanitizedStr = inputStr.replace(/[^\w\s]/g, "");
  return sanitizedStr;
}

function removeScripts(inputStr) {
  // 移除 <script> 标签和事件处理程序
  let sanitizedStr = inputStr.replace(/<script.*?>.*?<\/script>/gi, "");
  sanitizedStr = sanitizedStr.replace(/on\w+=".*?"/gi, "");
  return sanitizedStr;
}

// 用户输入
const userInput = '<script>alert("XSS");</script><p>Hello, World!</p>';

// 进行输入验证和过滤
const sanitizedInput = sanitizeInput(userInput);
console.log(sanitizedInput);
// 输出:scriptalertXSSscriptpHelloWorldp

// 移除脚本
const cleanInput = removeScripts(userInput);
console.log(cleanInput);
// 输出:<p>Hello, World!</p>

或者使用node的xss库和validator库来防止替换敏感字,基本逻辑和上述差不多,只不过上述代码它是使用原生 JavaScript 结合正则表达式来进行输入验证、过滤和脚本移除的。

npm install xss validator

const xss = require('xss');
const validator = require('validator');

function sanitizeInput(inputStr) {
  // 过滤非法字符
  const sanitizedStr = validator.stripTags(inputStr);
  return sanitizedStr;
}

function removeScripts(inputStr) {
  // 移除脚本
  const cleanedStr = xss(inputStr);
  return cleanedStr;
}

// 用户输入
const userInput = '<script>alert("XSS");</script><p>Hello, World!</p>';

// 进行输入验证和过滤
const sanitizedInput = sanitizeInput(userInput);
console.log('经过输入验证和过滤后的结果:', sanitizedInput);
// 输出:alert("XSS");Hello, World!

// 移除脚本
const cleanedInput = removeScripts(userInput);
console.log('经过脚本移除后的结果:', cleanedInput);
// 输出:&lt;script&gt;alert("XSS");&lt;/script&gt;&lt;p&gt;Hello, World!&lt;/p&gt;
  1. 解析器选择,避免使用有漏洞的解析器
  2. 白名单验证,创建一个可允许的标签和属性的白名单,在SVG渲染和解析时只允许渲染和解析白名单内的内容,以此防止恶意插入。
  3. URL验证,如果 SVG 中包含 URL,确保这些 URL 是合法的且不会导致安全问题。可以使用白名单验证来验证和过滤不受信任的 URL。
  4. 数据过滤和转义(上下文敏感信息转义),对敏感内容进行转换,防止 SQL 注入攻击。下面是一个例子:
import html
user_input = '<script>alert("XSS");</script>'
escaped_input = html.escape(user_input)
print(escaped_input)
# 输出:&lt;script&gt;alert(&quot;XSS&quot;);&lt;/script&gt;
function escapeStringForJavaScript(input) {
  return input
    .replace(/\\/g, '\\\\')
    .replace(/"/g, '\\"')
    .replace(/'/g, "\\'")
    .replace(/\r/g, '\\r')
    .replace(/\n/g, '\\n');
}

var userInput = '<script>alert("XSS");</script>';
var escapedInput = escapeStringForJavaScript(userInput);
console.log(escapedInput);
// 输出:\<script\>alert(\"XSS\");\<\/script\>

CSP内容安全策略

CSP,可以自定义哪些源是安全的。他可以通过 HTTP 响应头或标签中的 Content-Security-Policy 字段来配置,通常作为响应头的一部分

response.setHeader("Content-Security-Policy",
    "default-src 'self';
    script-src 'self' 'unsafe-inline' cdn.example.com; 
    style-src 'self' 'unsafe-inline';
    img-src 'self' data:; font-src 'self' cdn.example.com");
  1. default-src 'self'

    • default-src 指令用于设置默认的资源加载策略。
    • 'self' 表示只允许从同域加载资源。
  2. script-src 'self' 'unsafe-inline' cdn.example.com

    • script-src 指令用于设置允许加载 JavaScript 脚本的策略。
    • 'self' 表示只允许从同域加载脚本。
    • 'unsafe-inline' 表示允许执行内联的脚本。
    • cdn.example.com 表示允许从 cdn.example.com 加载脚本。
  3. style-src 'self' 'unsafe-inline'

    • style-src 指令用于设置允许加载样式表的策略。
    • 'self' 表示只允许从同域加载样式表。
    • 'unsafe-inline' 表示允许执行内联的样式。
  4. img-src 'self' data:

    • img-src 指令用于设置允许加载图像的策略。
    • 'self' 表示只允许从同域加载图像。
    • data: 表示允许加载使用 Data URI 方式嵌入的图像。
  5. font-src 'self' cdn.example.com

    • font-src 指令用于设置允许加载字体的策略。
    • 'self' 表示只允许从同域加载字体。
    • cdn.example.com 表示允许从 cdn.example.com 加载字体。

CSRF跨站请求伪造漏洞的种类及防御方法

CSRF-token攻击

CSRF(跨站请求伪造)是一种利用用户在受信任网站上的身份执行未经授权的操作的攻击方式。攻击者通过诱使用户在另一个网站上触发特定请求,从而欺骗受信任网站执行该请求,以达到攻击者的目的。为了防止 CSRF 攻击,应用程序可以使用一些防御措施,例如生成和验证随机的 CSRF 令牌,检查请求的来源等。我们常用的使用请求头携带token的方式也是防御CSRF攻击的一种手段。

  • CSRF 攻击的主要原理是攻击者利用用户的身份在受信任的网站上执行未经授权的操作。为了防止这种攻击,常见的做法是在用户的会话中引入 CSRF 令牌。
  • 常用解决方法:
  1. CSRF 令牌是一个随机生成的值,通常作为表单字段或请求头中的一部分发送给服务器。服务器在接收到请求时会验证令牌的有效性,确保请求是由合法的用户发送的。
  2. ②比较有效和通用的方法是增加一个**随机不可预测 token **,这个token可以放在 session 或客户端的 cookie 中,在提交请求时带上,因此攻击都无法得知token,因此也无法伪造请求。通过在每个请求中包含令牌并验证其有效性,可以防止 CSRF 攻击。攻击者无法获取用户的令牌,因为它通常存储在用户的会话中,并且跨域请求无法获取到用户的会话信息。

CSRF-iframe攻击

因为iframe里面发出的请求是同源请求而不是跨域,为了防止 CSRF-IFrame 攻击,网站可以采取以下措施:

  1. X-Frame-Options 头:通过设置 X-Frame-Options 头为"sameorigin",限制网页内容只能在相同域名下的 中加载,从而阻止其他域的内容在 中加载。
  2. Content-Security-Policy 头:使用 Content-Security-Policy 头中的 frame-ancestors 指令,限制哪些页面可以嵌入当前页面的 中。例如,可以将 frame-ancestors 'self' 设置为只允许来自相同域的页面嵌入。

避免SameSite Cookie

我页面的cookie只能为我所用,别的页面(第三方Cookie)都不能用,SameSiteCookie限制是cookie的domain属性和页面域名是否匹配

大名鼎鼎的Https

特点: - 可靠性:加密(×铭文) - 完整性:MAC验证(×篡改) - 不可抵赖性:数字签名(√身份校验)

传输过程

传输内容加密信息+加密信息_hash)——> 接收方if(hash(加密信息)===加密信息_hash){ok}else{不ok}

传播:数字签名(数学!)——以此确保不可抵赖性

Http怎么主动升级到Https

浏览器向服务端发起https的请求,服务器接受到这个请求之后会返回一个Strict-Transport-Security这个头部,并传递设定时间。也就是在这个时间范围内,浏览器输入http的时候会自动升级成Https协议。其要点就是首次发送需要是Https,也就是先有鸡后有蛋的思路了。

SRI——确保子资源完整性

那么这里有一个点,我们在请求资源的时候经常需要获取服务端资源,这些静态资源极容易被篡改,如何保证子资源完整性就依赖于SRI。

我们可以通过script标签或者link标签元素的intergrity元素中指定你要告诉浏览器所获取的资源(或文件)的 base64 编码的加密哈希值(通俗的讲也就是加密密文)。下面是一个例子(sha384-后跟的即密文):

sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC

我们可以通过SRI Hash Generator来生成你想要的加密密文