参考资料引荐
XSS
简介
- 跨站脚本攻击(Cross-Site Scripting, 简称XSS)
- 当用户将恶意代码注入网页时,其他用户在浏览网页时就会受到影响
- 攻击主要方向主要用于盗取cookie凭据,钓鱼攻击,流量指向等
攻击类型
反射型xss
-
反射型xss又叫非持久型xss (一般具有一次性),交互的数据一般不会被存在数据库里面也就是一次性
-
攻击者可以将恶意的XSS代码包含在URL漏洞,然后发送给用户,诱导用户自己点开(一次性)
-
是通过URL参数直接注入,一般是使用alert来探测站点是否防御,直接攻击的使用src来引入自己的脚本
js// 例如 http://localhost:1521/?from=<script>alert(1)</script>bing
DOM型xss
- DOM的全称为Document Object Model,即文档对象模型,DOM通常用于代表在HTML、XHTML和XML中的对象。使用DOM可以允许程序和脚本动态地访问和更新文档的内容、结构和样式。
- DOM型xss不基于后端,是修改属性,插入内容 document.write... 等改变前端DOM
- 改变结构后,造成攻击
存储型xss
- 攻击脚本将永久储存到目标服务器的数据库或文件中,因此每次访问网页时都会触发xss漏洞,所有人访问时都会造成攻击
- 漏洞一般存在于留言板,微博,论坛,博客等
js
// 例如在留言板提交的时候输入
<script>alert('hello')</script>
防御方案
- 客户端传递给服务器的时候,需要校验先过滤一下,转义一下
- 服务端入库存储在做一次过滤
- 直接在输出的时候过滤
服务端返回转义
- 服务端解析返回的时候,把返回的数据进行encodeURIComponent
客户端转义
页面提交、回显均做处理
HTML处理
HTML节点
- 转义掉<<和>> 即转义掉<>即可,转义的时机有两种,一种是写入数据库的时候进行转义,另一种实在解析的时候进行转义。
js
var escapeHtml = function(str){
str = str.replace(/>/g, '<');
str = str.replace(/>/g, '>');
return str;
}
escapeHtml(content);
HTML属性
- 转义"&quto; 即转义掉双引号,'转义掉单引号,(另一个要注意的是实际上html的属性可以不包括引号,因此严格的说我们还需要对空格进行转义,但是这样会导致渲染的时候空格数不对,因此我们不转义空格,然后再写html属性的时候全部带上引号)这样属性就不会被提前关闭了
js
var escapeHtmlProperty = function(str){
str = str.replace(/"/g, '&quto;');
str = str.replace(/'/g, ''');
str = str.replace(/ /g, ' ');
return str;
}
escapeHtml(content);
JavaScript处理
防御方法就是 JavaScriptEncode
jsonp或js处理
-
callback 做长度限制,这个比较 low,一般对函数名限制在 50 个字符内。
-
检测 callback 里面的字符 。一般 callback 里面都是字母和数字,别的符号都不能有。函数名只允许
[
,]
,a-zA-Z0123456789_
,$
,.
,防止一般的 XSS,utf-7 XSS等攻击。 JSONP 中可以通过意外截断 JSON 数据或者在页面中玩转引号来造成 XSS 攻击。 -
需要用 \ 对特殊字符进行转义。保险的方法其实很简单,就是对数据做一次JSON.stringify即可
jsvar escapeForJs = function(str){ if(!str) return ''; str = str.replace(/\\/g,'\\\\'); str = str.replace(/"/g,'\\"'); }
防脚本劫持处理
- 通过判断是否是用户真实操作,而不是脚本处理页面操作
Event.isTrusted
属性返回一个布尔值,表示该事件是否由真实的用户行为产生。比如,用户点击链接会产生一个click
事件,该事件是用户产生的;Event
构造函数生成的事件,则是脚本产生的。
CSS处理
- 在 CSS 中或者 style 标签中的攻击花样特别多,要解决 CSS 的攻击问题,一方面要严格控制用户将变量输入 style 标签内,另一方面不要引用未知的 CSS 文件,如果一定有用户改变 CSS 变量这种需求的话,可以使用 OWASP ESAPI 中的 encodeForCSS() 函数。
- 除了阿拉伯数字和字母,对其他所有的字符进行编码,只要该字符的ASCII码小于256。编码后输出的格式为 \HH (以 \ 开头,HH则是指该字符对应的十六进制数字)
URL处理
- 在 URL 中的输出直接使用 encodeURIComponent即可,需要转义变量的部分。
富文本处理
-
首先例行进行输入检查,保证用户输入的是完整的 HTML 代码,而不是有拼接的代码
-
通过 htmlParser 解析出 HTML 代码的标签、属性、事件
-
富文本的事件肯定要被禁止,因为富文本并不需要事件这种东西,另外一些危险的标签也需要禁止,例如: ,
安全头处理
HTTP-only Cookie
- 加密结合Cookie中HttpOnly属性
验证码提交处理
- 防止脚本冒充用户提交危险操作
CSRF
简介
CSRF (Cross-site request forgery,跨站请求伪造)也被称为One Click Attack或者Session Riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。尽管听起来像跨站脚本(XSS),但它与XSS非常不同,XSS利用站点内的信任用户,而CSRF则通过伪装成受信任用户请求受信任的网站。
简单的说,是攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己以前认证过的站点并运行一些操作(如发邮件,发消息,甚至财产操作(如转账和购买商品))。因为浏览器之前认证过,所以被访问的站点会觉得这是真正的用户操作而去运行。
防御方案
验证码
-
CSRF攻击的过程,往往是在用户不知情的情况下构造了网络请求。而验证码,则强制用户必须与应用进行交互,才能完成最终请求。因此在通常情况下,验证码能够很好地遏制CSRF攻击。
-
但是验证码并非万能。很多时候,出于用户体验考虑,网站不能给所有的操作都加上验证码。因此,验证码只能作为防御CSRF的一种辅助手段,而不能作为最主要的解决方案。
验证请求头的Referer
- 不靠谱,referer可以伪造
验证请求参数或请求头的token
- 将 token 设置在 Cookie 中,在提交 POST 请求的时候提交 Cookie,并通过 header 或者 body 带上 Cookie 中的 token,服务端进行对比校验。
Cookie
跨站携带
- cookie信息可以被跨站携带,对于核心cookie的SameSite设置为Strict或Lax来避免
被获取
- 将重要的cookie信息设置为HttpOnly来避免
websocket劫持漏洞(CSWSH)
参考资料
websocket是通过ws:// 和wss:// 通信协议,其中ws可以认为是http的切换,wss是https的切换,是加密的传输协议
websocket与http的是没有任何关系的,websocket是持久化的协议,而http是非持久的
WebSocket 提供了全双工沟通,俗称 Web 的 TCP 连接,但 TCP 通常处理字节流(跟消息无关),而 WebSocket 基于 TCP 实现了消息流
WebSocket 也类似于 TCP 一样进行握手连接,跟 TCP 不同的是,WebSocket 是基于 HTTP 协议进行的握手
优点:
- 支持双向通信,实时性比较强
- 更好的二进制支持
- 较少的开销。创建连接后,数据交换时候不用携带所有的数据头部信息
- 支持拓展
缺点:
- 缺少认证机制
- 存在跨站点劫持漏洞
防御方案
服务器端检测Origin
- 在服务器端的代码中增加 Origin 检查,如果客户端发来的 Origin 信息来自不同域,建议服务器端拒绝这个请求,发回 403 错误响应拒绝连接
WebSocket 令牌机制
-
具体实现流程如下:
服务器端为每个 WebSocket 客户端生成唯一的一次性 Token;
客户端将 Token 作为 WebSocket 连接 URL 的参数(譬如 ws://echo.websocket.org/?token=randomOneTimeToken),发送到服务器端进行 WebSocket 握手连接;
服务器端验证 Token 是否正确,一旦正确则将这个 Token 标示为废弃不再重用,同时确认 WebSocket 握手连接成功;如果 Token 验证失败或者身份认证失败,则返回 403 错误。
握手信息加密
- 对重要的websocket信息进行握手加密
CORS漏洞
- 因为需要配置CORS响应头来告知浏览器是否允许该请求,所以如果配置不当,就可能导致攻击者通过恶意网站或代码执行跨域请求,从而获取或篡改用户的敏感数据
防御方案
-
限制
Access-Control-Allow-Origin
的值为可信源,尽可能设置白名单,不能为*
,也不能为null
-
避免
Access-Control-Allow-Credentials
的值为True
-
设置
Access-Control-Allow-Methods
(允许的 HTTP 方法)、Access-Control-Allow-Headers
(允许的请求头)
传输安全
- HTTPS证书加密
CSP漏洞
- CSP即content security policy(内容安全策略),通过设置策略指令来规定资源的加载来源,简单地说就是CSP可以规定页面能从哪些域(站点)加载资源,这里的资源包括js脚本、图片和音频等等
- 用于检测并削弱某些特定类型的攻击,包括跨站脚本 (XSS) 和数据注入攻击等。无论是数据盗取、网站内容污染还是散发恶意软件,这些攻击都是主要的手段。CSP 的实质就是白名单制度,开发者明确告诉客户端,哪些外部资源可以加载和执行,等同于提供白名单。它的实现和执行全部由浏览器完成,开发者只需提供配置。CSP 大大增强了网页的安全性。攻击者即使发现了漏洞,也没法注入脚本,除非还控制了一台列入了白名单的可信主机。
参考资料
CSP 指令说明 :
default-src : 定义针对所有类型(js/image/css/font/ajax/iframe/多媒体等)资源的默认加载策略,如果某类型资源没有单独定义策略,就使用默认的。
script-src : 定义针对 JavaScript 的加载策略。
style-src : 定义针对样式的加载策略。
worker-src:worker脚本。
img-src : 定义针对图片的加载策略。
font-src : 定义针对字体的加载策略。
media-src : 定义针对多媒体的加载策略,例如:音频标签和视频标签。
object-src : 定义针对插件的加载策略,例如:、、。
child-src : 定义针对框架的加载策略,例如: ,。
connect-src : 定义针对 Ajax/WebSocket 等请求的加载策略。不允许的情况下,浏览器会模拟一个状态为400的响应。
sandbox : 定义针对 sandbox 的限制,相当于 的sandbox属性。
report-uri : 告诉浏览器如果请求的资源不被策略允许时,往哪个地址提交日志信息。
form-action : 定义针对提交的 form 到特定来源的加载策略。
referrer : 定义针对 referrer 的加载策略。
reflected-xss : 定义针对 XSS 过滤器使用策略。
防御方案
设置CSP headers
在服务器端,为响应添加以下CSP头部:
-
Content-Security-Policy:定义允许加载资源的来源,从而限制恶意脚本的来源。例如:Content-Security-Policy: default-src 'self'; script-src 'self'; img-src 'self' data:; style-src 'self'
-
Content-Security-Script-Source-List:指定允许脚本加载的来源,仅允许来自可信来源的脚本。例如:Content-Security-Script-Source-List: https://example.com
限制脚本执行
-
在HTML中使用
sandbox
属性,限制脚本在页面上的执行范围。js<iframe src="https://example.com" sandbox="allow-scripts allow-same-origin allow-forms"></iframe>
启用CSP报告
-
启用CSP报告功能,以便在检测到违规行为时收到通知。
jsnavigator.serviceWorker.ready.then(registration => { registration.active.addEventListener('message', (event) => { if (event.data.type === 'violation-report') { console.log('CSP violation:', event.data.message); } }); });
定期审查和更新CSP策略
- 随着网络环境的变化,定期审查和更新CSP策略,确保其持续有效。
JavaScript原型链污染漏洞
- 在JavaScript中,对象是通过原型链来继承属性和方法的。每个对象都有一个指向其原型的链接,当我们试图访问一个对象的属性或方法时,如果对象本身没有这个属性或方法,JavaScript会沿着原型链向上查找,直到找到该属性或方法或者抵达原型链的顶端(通常是
Object.prototype
)。 - 原型链污染是指攻击者通过修改对象的原型链,使其包含恶意代码或不受信任的属性和方法。这样一来,当其他代码使用该对象时,可能会意外地调用到恶意代码或者使用不受信任的属性和方法,从而导致安全漏洞。
原型链污染原理
在 JavaScript 中,每个对象都有一个原型对象,当访问一个对象的属性时,JavaScript 会沿着该对象的原型链向上查找,直到找到该属性或者到达原型链的顶端为止。原型链污染是指攻击者通过修改对象的原型链来实现对代码的攻击。
下面是一个简单的示例,演示了原型链污染的基本原理:
javascript
javascript复制代码// 定义一个基础对象,包含一个 getName 方法
const baseObj = {
getName: function() {
return 'I am a base object';
}
};
// 定义一个恶意对象,通过原型链污染修改了 getName 方法
const maliciousObj = {
getName: function() {
return 'I am a malicious object';
}
};
maliciousObj.__proto__ = baseObj; // 修改原型对象为 baseObj
// 创建一个普通的对象,并访问 getName 方法
const normalObj = {};
console.log(normalObj.getName()); // 输出:Uncaught TypeError: normalObj.getName is not a function
// 修改 normalObj 对象的原型为 maliciousObj
Object.setPrototypeOf(normalObj, maliciousObj);
// 此时再次访问 getName 方法,输出被污染的内容
console.log(normalObj.getName()); // 输出:I am a malicious object
-
在上面的示例中,定义了一个基础对象
baseObj
,它包含一个getName
方法,该方法返回一个固定的字符串。接下来,定义了一个恶意对象maliciousObj
,该对象同样包含一个getName
方法,但它返回的字符串不同,并且将__proto__
属性设置为base
-
Obj
对象,从而使maliciousObj
对象的原型指向了baseObj
。接下来,创建了一个普通的对象normalObj
,该对象在原型链上并没有定义getName
方法。然后,使用Object.setPrototypeOf
方法将normalObj
的原型设置为maliciousObj
,从而在normalObj
对象上成功实现了原型链污染。最后,调用normalObj.getName()
方法时,JavaScript 沿着normalObj
对象的原型链查找到了maliciousObj
对象,并返回了被污染的getName
方法的内容。
防御方案
输入验证
- 在接收用户输入之前,始终进行有效性验证,确保用户提供的数据符合预期的格式和内容。
JSON.parse安全解析
-
如果需要解析JSON数据,请使用
JSON.parse
的第二个参数将属性名转换为字符串,避免原型链污染。jsconst userInput = '{"__proto__": {"debug": true}}'; const userConfig = JSON.parse(userInput, (key, value) => { if (key === '__proto__') { return null; // 或者抛出错误 } return value; });
Object.defineProperty设置
- 在设置对象的属性时,可以使用
Object.defineProperty
来定义只读或不可枚举的属性,防止原型链被污染。
js
const config = {
debug: false
};
const userInput = '{"__proto__": {"debug": true}}';
const userConfig = JSON.parse(userInput);
// 合并用户配置和默认配置
const mergedConfig = Object.assign({}, config);
Object.keys(userConfig).forEach(key => {
Object.defineProperty(mergedConfig, key, {
value: userConfig[key],
enumerable: false, // 阻止属性被枚举
writable: false // 阻止属性被修改
});
});
使用Object.freeze()冻结对象
- 使用
Object.freeze()
可以冻结一个对象,使其无法添加、删除属性,已有属性的值也无法修改。这使得即使攻击者能够修改到对象的原型,也无法通过修改冻结对象的原型来造成污染。
js
const obj = { name: "SAFe" };
Object.freeze(obj);
使用Map代替普通对象
- 在某些情况下,可以使用Map而不是普通的JavaScript对象来储存键值对。与普通对象不同,Map不会受到原型污染的影响,因为它们不是通过原型链查找值。
js
const safeMap = new Map();
safeMap.set('key', 'value'); // 安全地设置键值对
使用Object.create
- 使用这个方法就可以更好的防御原型链污染攻击了,因为Object.create(null)使得创建的新对象没有任何的原型链,是null的,不具备任何的继承关系,当你接受一个客户端的参数并且打算merge的话,可以使用此方法后去merge,这样的对象是比较安全的,客户端没办法通过原型链来污染攻击(因为压根就没原型链通往其他的对象)
文件上传漏洞
客户端
- 校验文件类型及后缀格式
服务端
- 检查后缀及MIME
- 检查内容及文件头