http防御
# 跨域
# 同源策略
同源策略/SOP(Same origin policy)是一种约定;所谓同源是指 "协议 + 域名 + 端口" 三者相同,即便两个不同的域名指向同一个 ip 地址,也非同源。 从一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的关键的安全机制
# 跨域的含义
当协议、域名、端口号,有一个或多个不同时,有希望可以访问并获取数据的现象称为跨域访问,同源策略限制下 cookie
、localStorage
、dom
、ajax
、IndexDB
都是不支持跨域的。
不同源之间不能执行以下情况,以下情况都是同源时执行:
- Cookie、LocalStorage和indexDB无法读取;
- AJAX请求不能发送; 限制了通过 XMLHttpRequest 等方式将站点的数据发送给不同源的站点;
- 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);
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);
});
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>
本地回调函数
function Hello(data){
alert(data.result);
}
2
3
跨域服务端:
protected void Page_Load(object sender, EventArgs e){
string callback=Request.QueryString["callback"];//获取客户端回调函数名,值为"Hello"
Response.Write(callback+"({result: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);
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;
}
}
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
成功时,响应头是如下所示
HTTP/1.1 200 OK
Server: nginx
Access-Control-Allow-Origin: example.com
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>
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");
});
});
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 攻击不涉及网站服务器,通常是由于前端页面不严谨的代码产生的安全漏洞,导致注入了恶意代码。
例如,在使用
.innerHTML
、document.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 攻击不涉及网站服务器,通常是由于前端页面不严谨的代码产生的安全漏洞,导致注入了恶意代码。
例如,在使用 .innerHTML
、document.write()
、document.outerHTML
这些能够修改页面结构的 API 时要注意防范恶意代码,尽量使用 .textContent
、.setAttribute()
等。
例如,黑客构造带有恶意代码的 URL 诱导用户打开 http://www.xxx.com/index.html/?content=<script>alert('我是 DOM 型 XSS 攻击')</script>
,浏览器收到请求后解析之行,如果使用 document.write()
未经转义输出,就可能遭到攻击。
# 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'">
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
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。
另外同源策略主要是浏览器来进行验证的
, 并且不同浏览器的实现又各自不同, 所以在某些浏览器上可以直接绕过, 而且也可以直接通过短信等方式直接绕过浏览器.
预防:
- A 站 (预防站) 检查 http 请求的 header 确认其 origin
- 检查 CSRF token
# 同源检查
通过检查来过滤简单的 CSRF 攻击, 主要检查一下两个 header:
- Origin Header:请求来源限制
- Referer Header
# CSRF token
简单来说, 对需要预防的请求, 通过特别的算法生成 token 存在 session 中, 然后将 token 隐藏在正确的界面表单中, 正式请求时带上该 token 在服务端验证, 避免跨站请求;【常用】
基本思路如下:
- 服务器随机产生
token
(比如把cookie
hash化生成),存在session
中,放在cookie
中或者以ajax
的形式交给前端。 - 前端发请求的时候,解析
cookie
中的token
,放到请求url
里或者请求头中。 - 服务器验证
token
,由于黑客无法得到或者伪造token
,所以能防范csrf
更进一步的加强手段(不需要session):
- 服务器随机产生
token
,然后以token
为密钥散列生成一段密文; - 把
token
和密文都随cookie
交给前端; - 前端发起请求时把密文和
token
都交给后端; - 后端对
token
和密文进行正向散列验证,看token
能不能生成同样的密文; - 这样即使黑客拿到了
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
, app
和 agent
上都提供了 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';
},
},
};
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
请求头信息校验;(例如host
,User-Agent
,Referer
); 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 攻击不同的地方。
浏览器输入
http://localhost:3000/?message=<script>var cookie = document.cookie; alert(
我是 XSS 攻击,你中招了 😄, cookie: ${cookie})</script>
回车后,页面上会弹出如下提示框。Stored XSS(存储型的 XSS 攻击)持久型
- 基于存储的 XSS 攻击,是通过提交带有恶意脚本的内容存储在服务器上,当其他人看到这些内容时发起 Web 攻击。一般提交的内容都是通过一些富文本编辑器编辑的,很容易插入危险代码。(如上述的留言评论功能);
- 利用漏洞提交恶意 JavaScript 代码,比如在input, textarea等所有可能输入文本信息的区域,输入
<script src="http://恶意网站"></script>
等,提交后信息会存在服务器中,当用户再次打开网站请求到相应的数据,打开页面,恶意脚本就会将用户的 Cookie 信息等数据上传到黑客服务器。 - 比如:攻击者在论坛中放一个看似安全的链接,骗取用户点击后,窃取
cookie
中的用户私密信息;或者攻击者在论坛中加一个恶意表单,当用户提交表单的时候,却把信息传送到攻击者的服务器中,而不是用户原本以为的信任站点。
防范
为了防止 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)防御
- 网关控制流量洪峰,对在一个时间段内出现流量异常,可以拒绝请求;
- 源
ip
请求个数限制。对请求来源的ip
请求个数做限制; - 频率限制(1s内接口调用次数限制)IP限制; 防刷一般分两种:
- 总调用次数受限制。这个一般是在后端做限制,单位时间内最多可调用次数。
- 同一客户端次数限制。这个前端的一般使用是给接口调用加锁,在返回结果或者一定时间之后解锁。
刷接口直接走脚本,前端限制有一定作用,但不大。
- 根据
IP
限制调用次数【请求在代理服务器被拦截nginx
,减少后端服务被调用】; - 拦截器特定信息校验,例如:
token
【后端服务器拦截器,减少业务判断和处理】; 1、校验请求头referer 2、校验请求token; 3、请求频率
# 文件上传漏洞
服务器未校验上传的文件,致使黑客可以上传恶意脚本等方式。
预防策略
- 用文件头来检测文件类型,使用白名单过滤(有些文件可以从其中一部分执行,只检查文件头无效,例如 PHP 等脚本语言);
- 上传后将文件彻底重命名并移动到不可执行的目录下;
- 升级服务器软件以避免路径解析漏洞;
- 升级用到的开源编辑器;
- 管理后台设置强密码。
# SQL注入
拼接 SQL 时未仔细过滤,黑客可提交畸形数据改变语义。比如查某个文章,提交了这样的数据id=-1 or 1=1
等。1=1 永远是true,导致where语句永远是ture.那么查询的结果相当于整张表的内容,攻击者就达到了目的。或者,通过屏幕上的报错提示推测 SQL 语句等。
预防策略
- 禁止目标网站利用动态拼接字符串的方式访问数据库
- 减少不必要的数据库抛出的错误信息
- 对数据库的操作赋予严格的权限控制
- 净化和过滤掉不必要的SQL保留字,比如:where, or, exec 等
- 不要信任用户的输入,要对用户的输入进行校验,可以通过正则表达式,或限制长度,对单引号和双"-"进行转换等
- 不要使用动态拼装SQL,可以使用参数化的SQL或者直接使用存储过程进行数据查询存取
- 不要使用管理员权限的数据库连接,为每个应用使用单独的权限有限的数据库连接
- 不要把机密信息明文存放,请加密或者hash掉密码和敏感的信息
# 参考链接
https://eggjs.org/zh-cn/core/security.html
https://juejin.cn/post/6929281260045434894