平时做技术实践时,很多问题不是概念不会,而是细节没串起来。拿“前端常见的安全问题以及防范措施总结大全”来说,它看着像小点,放到项目里常会牵出环境、配置、兼容性和维护成本。下面按实际采用顺序,把思路、关键写法和容易踩坑的地方讲清楚,便于大家直接对照操作。
从实现思路看,随着互联网的高速发展,信息安全问题已经成为行业最为关注的焦点之一。总的来说安全是很复杂的一个领域,在移动互联网时代,前端人员除了传统的 XSS、CSRF 等安全问题之外,还时常遭遇网络劫持、非法调用 Hybrid API 等新型安全问题。这篇文章会介绍一些常用的安全问题及如何防范的内容,在当下其实安全问题越来越重要,已经逐渐成为前端开发必备的技能了。

实际处理时,Cross-Site Scripting(跨站脚本攻击)简称 XSS,是一种代码注入攻击。攻击者借助在目标网站上注入恶意脚本,使之在用户的浏览器上运行。借助这些恶意脚本,攻击者可拿到用户的敏感信息如 Cookie、SessionID 等,进而危害数据安全。
实际处理时,为便于和 CSS 区分,这里把攻击的第一个字母改成了 X,于是叫做 XSS。
一般能够借助三种方式来注入恶意脚本:
从实现思路看,顾名思义,恶意 JavaScript 脚本属于用户发送给网站请求中的一部分,随后网站又将这部分得到给用户,恶意脚本在页面中被执行。一般发生在前后端一体的应用中,服务端逻辑会改变最后的网页代码。
反射型 XSS 的攻击步骤:
在这个场景下,目前更流行前后端分离的项目,反射型 XSS 无用武之地。 但这种攻击不需经过服务器,我们知道,网页本身的 JavaScript 也是能够改变 HTML 的,黑客正是借助这一点来实现插入恶意脚本。
基于DOM 的 XSS 攻击步骤:
理解这一步时,又叫持久型 XSS,顾名思义,黑客将恶意 JavaScript 脚本长期保存在服务端数据库中,用户一旦访问相关页面数据,恶意脚本就会被执行。常用于搜索、微博、社区贴吧评论等。
存储型 XSS 的攻击步骤:
结合项目来看,反射型的 XSS 的恶意脚本存在 URL 里,存储型 XSS 的恶意代码存在数据库里。
实际处理时,反射型 XSS 攻击常用于借助 URL 传递参数的功能,如网站搜索、跳转等。
结合项目来看,存储型XSS攻击常用于带有用户保存数据的网站功能,如论坛发帖、商品评论、用户私信等。
在这个场景下,而基于DOM的XSS 攻击中,取出和执行恶意代码由浏览器端完成,属于前端 JavaScript 自身的安全漏洞,其他两种 XSS 都属于服务端的安全漏洞。
由上面对XSS攻击的介绍我们知道,XSS攻击主要有两大步骤:
所以我们能够针对这两点来制定防范措施:
结合项目来看,在用户提交时,由前端过滤输入,随后提交到后端,这种方法不可行,因为攻击者可能绕过前端过滤,直接构造请求,提交恶意代码。一般在写入数据库前,后端对输入数据进行过滤。虽然输入侧过滤能够在某些情况下解决特定的 XSS 问题,但会引入很大的不确定性和乱码问题。在防范 XSS 攻击时应避免此类方法。
实际处理时,DOM 型 XSS 攻击,实际上就是网站前端 JavaScript 代码本身不够严谨,把不可信的数据当作代码执行了。
在这个场景下,在采用 .innerHTML、.outerHTML、document.write() 时要特别小心,不要把不可信的数据作为 HTML 插到页面上,而应尽量采用 .textContent、.setAttribute() 等。
实际处理时,若用 Vue/React 技术栈,同时且不采用 v-html/dangerouslySetInnerHTML 功能,就在前端 render 阶段避免 innerHTML、outerHTML 的 XSS 隐患。
落到代码里,DOM 中的内联事件器,如 location、onclick、onerror、onload、onmouseover 等,<a> 标签的 href 属性,JavaScript 的 eval()、setTimeout()、setInterval() 等,都能把字符串作为代码运行。如果不可信的数据拼接到字符串中传递给这些 API,很容易产生安全隐患,请务必避免。
<!-- 内联事件器中包含恶意代码 -->
<img onclick="UNTRUSTED" onerror="UNTRUSTED" src="data:image/png,">
<!-- 链接内包含恶意代码 -->
<a href="UNTRUSTED" rel="external nofollow" >1</a>
<script>
// setTimeout()/setInterval() 中调用恶意代码
setTimeout("UNTRUSTED")
setInterval("UNTRUSTED")
// location 调用恶意代码
location.href = 'UNTRUSTED'
// eval() 中调用恶意代码
eval("UNTRUSTED")
</script>
严格的 CSP 在 XSS 的防范中能够起到以下的作用:
理解这一步时,采用 W3C 提出的 CSP (Content Security Policy,内容安全策略),定义域名白名单
实际处理时,2005年,年仅19岁的 Samy Kamkar 发起了对 MySpace.com 的 XSS Worm 攻击。 Samy Kamkar 的蠕虫在短短几小时内就感染了100万用户——它在每个用户的自我简介后边加了一句话:“but most of all, Samy is my hero.”(Samy是我的偶像)。这是 Web 安全史上第一个重量级的 XSS Worm,具有里程碑意义。
2007年12月,百度空间收到蠕虫攻击,用户之间开始转发垃圾短消息。
QQ 邮箱 m.exmail.qq.com 域名被发现反射型 XSS 漏洞
实际处理时,2011年新浪微博曾被黑客 XSS 攻击,黑客诱导用户点击一个带有诱惑性的链接,便会自动发送一条带有同样诱惑性链接微博。攻击范围层层扩大,也是一种蠕虫攻击。
结合项目来看,CSRF(Cross-site request forgery)跨站请求伪造:攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求。借助受害者在被攻击网站已经拿到的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目的。
典型的CSRF攻击是这样的:
GET类型的CSRF借助很轻松,只需一个HTTP请求,一般会这样借助:
<img src="http://bank.example/withdraw?amount=10000&for=hacker" >
结合项目来看,在受害者访问含有这个img的页面后,浏览器会自动向(链接已移除)。bank.example就会收到包含受害者登录信息的一次跨域请求。
这种类型的CSRF借助起来通常采用的是一个自动提交的表单,如:
<form action="http://bank.example/withdraw" method=POST>
<input type="hidden" name="account" value="xiaoming" />
<input type="hidden" name="amount" value="10000" />
<input type="hidden" name="for" value="hacker" />
</form>
<script> document.forms[0].submit(); </script>
访问该页面后,表单会自动提交,相当于模拟用户完成了一次POST操作。
实际处理时,POST类型的攻击通常比GET要求更加严格一点,但仍同时不复杂。任何个人网站、博客,被黑客上传页面的网站都有可能是发起攻击的来源,后端接口不能将安全寄托在仅允许POST上面。
实际处理时,链接类型的CSRF同时不常用,比起其他两种用户打开页面就中招的情况,这种需用户点击链接才会触发。这种类型通常是在论坛中发布的图片中嵌入恶意链接,或者以广告的形式诱导用户中招,攻击者通常会以比较夸张的词语诱骗用户点击
<a href="http://test.com/csrf/withdraw.php?amount=1000&for=hacker" rel="external nofollow" taget="_blank">
重磅消息!!
<a/>
理解这一步时,由上面对CSRF的介绍我们知道了,CSRF通常发生在第三方域名,同时且CSRF攻击者不能拿到到受害者的cookie等信息,只是借用他们的登录状态来伪造请求。所以我们能够针对这两点来制定防范措施:
理解这一步时,既然CSRF大多来自第三方网站,那么我们就直接禁止第三方域名(或者不受信任的域名)对我们发起请求。
在这个场景下,在HTTP协议里,每一个异步请求都会携带两个Header,用来标记来源域名:
理解这一步时,这两个Header在浏览器发起请求时,大多数情况会自动带上,同时且不能由前端自定义内容。 服务器能够借助解析这两个Header中的域名,确定请求的来源域。同时服务器应该优先检测 Origin。为了安全考虑,相比于 Referer,Origin 只包含了域名而不带路径。
实际处理时,在浏览器向服务器发起请求时,服务器生成一个 CSRF Token。CSRF Token 其实就是服务器生成的随机字符串,随后将该字符串植入到得到的页面中,通常是放到表单的隐藏输入框中,这样能够很好的保护 CSRF Token 不被泄漏;
结合项目来看,当浏览器再次发送请求的时候,就需携带这个 CSRF Token 值一起提交;
从实现思路看,服务器验证 CSRF Token 是否一致;从第三方网站发出的请求是无法拿到用户页面中的 CSRF Token 值的。
结合项目来看,当从 A 网站登录后,会从响应头中得到服务器设置的 Cookie 信息,而如果 Cookie 携带了 SameSite=strict 则表示完全禁用第三方站点请求头携带 Cookie,比如当从 B 网站请求 A 网站接口的时候,浏览器的请求头将不会携带该 Cookie。
实际处理时,Samesite=Strict,这种称为严格模式,表明这个 Cookie 在任何情况下都不可能作为第三方 Cookie
从实现思路看,Samesite=Lax,这种称为宽松模式,比 Strict 放宽了点限制:假如这个请求是这种请求(改变了当前页面或者打开了新页面)且同时是个GET请求,则这个Cookie能够作为第三方Cookie。(默认)
None 任何情况下都会携带;
落到代码里,点击劫持(Clickjacking)是一种借助视觉欺骗的手段来达到攻击目的手段。往往是攻击者将目标网站借助 iframe 嵌入到自己的网页中,借助 opacity 等手段设置 iframe 为透明的,使得肉眼不可见,这样一来当用户在攻击者的网站中操作的时候,比如点击某个按钮(这个按钮的顶层其实是 iframe),从而实现目标网站被点击劫持。
在这个场景下,在HTTP投中加入 X-FRAME-OPTIONS 属性,此属性控制页面是否可被嵌入 iframe 中
判断当前网页是否被 iframe 嵌套
在这个场景下,HTTP严格传输安全(HSTS)是一种安全功能,web服务器借助它来告诉浏览器仅用HTTPS来与之通讯,而不是采用HTTP。
落到代码里,HSTS代表HTTP严格传输安全性,由I在2012年的RFC 6797中指定。新建它是为了在站点借助HTTPS运行时强制浏览器采用安全连接。它是您添加到Web服务器的安全标头,同时在响应标头中反映为Strict-Transport-Security。HSTS很重要,因为它解决了以下问题:
落到代码里,注意,如果之前没有采用HTTPS协议访问过该站点,那么HSTS是不奏效的,只有浏览器曾经与服务器新建过一次安全连接同时且网站借助HTTPS协议告诉浏览器它兼容HSTS,那么之后浏览器才会强制采用HTTPS,即使链接被换成了HTTP。
结合项目来看,虽然我们的系统默认更喜欢HTTPS版本,但您也能够借助将您的HTTP站点重定向到您的HTTPS版本同时在您的服务器上实施HSTS标头,使其他搜索引擎更清楚这一点。 —— 谷歌安全团队
在Apache中启用HSTS
将以下代码添加到您的虚拟主机文件中。
Header always set Strict-Transport-Security max-age=31536000
在NGINX中启用 HSTS
将以下代码添加到您的NGINX设置中。
add_header Strict-Transport-Security max-age=31536000
从实现思路看,事实上,添加HSTS标头有性能优势。如果有人试图借助HTTP访问您的站点,而不是发出HTTP请求,它只是重定向到HTTPS版本。
从实现思路看,它的名字就叫做CDN——Content Delivery Network,内容分发网络。具体来说,CDN就是采用更多的缓存服务器(CDN边缘节点),布放在用户访问相对集中的地区或网络中。当用户访问网站时,借助全局负载技术,将用户的访问指向距离最近的缓存服务器上,由缓存服务器响应用户请求。(有点像电商的本地仓吧?)CDN应用广泛,兼容多种行业、多种场景内容加速,比如:图片小文件、大文件下载、视音频点播、直播流媒体、全站加速、安全加速。
从实现思路看,网络上有很多黑客为了让用户能够登录自己开发的钓鱼网站,都会借助对CDN进行劫持的方法,让用户自动转入自己开发的网站。而很多用户却往往无法察觉到自己已经被劫持。其实验证被劫持的方法,就是输入任何网址看看所打开的网页是否和自己输入的网址一致,
理解这一步时,SRI 全称 Subresource Integrity - 子资源完整性,是指浏览器借助验证资源的完整性(通常从 CDN 拿到)来判断其是否被篡改的安全特性。
实际处理时,借助给 link 标签或者 script 标签增加 integrity 属性即可开启 SRI 功能,比如
<script type="text/javascript" src="//s.url.cn/xxxx/aaa.js"
integrity="sha256-xxx sha384-yyy"
crossorigin="anonymous"></script>
从实现思路看,integrity 值分成两个部分,第一部分指定哈希值的生成算法(sha256、sha384 及 sha512),第二部分是经过 base64 编码的实际哈希值,两者之间借助一个短横(-)分割。integrity 值能够包含多个由空格分隔的哈希值,只要文件匹配其中任意一个哈希值,就能够借助校验同时加载该资源。开启 SRI 能有效保证页面引用资源的完整性,避免恶意代码执行。
从实现思路看,内容安全策略(Content Security Policy)简称 CSP,借助它能够明确的告诉客户端浏览器当前页面的哪些外部资源能够被加载执行,而哪些又是不能够的。
在这个场景下,防XSS等攻击的利器。CSP 的实质就是白名单制度,开发者明确告诉客户端,哪些外部资源能够加载和执行,等同于提供白名单。它的实现和执行全部由浏览器完成,开发者只需提供设置。CSP 大大增强了网页的安全性。攻击者即使发现了漏洞,也没法注入脚本,除非还控制了一台列入了白名单的可信主机。
Content-Security-Policy: script-src 'self' https://apis.google.com
<meta http-equiv="Content-Security-Policy" content="script-src 'self' https://apis.google.com">
理解这一步时,多进程的浏览器架构将主要分为两块:浏览器内核和渲染内核。而安全沙箱能限制了渲染进程对操作系统资源的访问和修改,同时渲染进程内部也没有读写操作系统的能力,而这些都是在浏览器内核中一一实现了,包括持久存储、网络访问和用户交互等一系列直接与操作系统交互的功能。浏览器内核和渲染内核各自职责分明,当他们需进行数据传输的时候会借助 IPC 进行。
从实现思路看,而渲染进程的工作是进行 HTML、CSS 的解析,JavaScript 的执行等,而这部分内容是直接暴露给用户的,所以也是最容易被黑客借助攻击的地方,如果黑客攻击了这里就有可能拿到到渲染进程的权限,进而威胁到操作系统。所以需一道墙用来把不可信任的代码运行在一定的环境中,限制不可信代码访问隔离区之外的资源,而这道墙就是浏览器的安全沙箱。
理解这一步时,安全沙箱的存在是为了保护客户端操作系统免受黑客攻击,但是阻止不了 XSS 和 CSRF。

从实现思路看,安全沙箱是借助操作系统提供的安全技术,这样渲染进程在运行中就无法拿到或修改操作系统中的数据。安全沙箱最小隔离单位是进程,所以无法保护单进程浏览器。
结合项目来看,iframe在给我们的页面带来更多丰富的内容和能力的同时,也带来了不少的安全隐患。因为iframe中的内容是由第三方来提供的,默认情况下他们不受我们的控制,他们能够在iframe中运行JavaScirpt脚本、Flash插件、弹出对话框等等,这可能会破坏前端用户体验。
js的防御方案:将下面这段代码放到网站页面的</body>标签前,这样别人在借助iframe框架引用你的网站网页时,浏览器会自动跳转到你的网站所引用的页面上。
<script>
if (self == top) {
var theBody = document.getElementsByTagName('body')[0];
theBody.style.display = "block";
} else {
top.location = self.location;
}
</script>
采用X-Frame-Options防止网页被iframe:X-FRAME-OPTIONS是微软提出的一个http头,专门用来防御借助iframe嵌套的点击劫持攻击。
DENY // 拒绝任何域加载
SAMEORIGIN // 允许同源域下加载
ALLOW-FROM // 可以定义允许frame加载的页面地址
在这个场景下,sandbox是html5的新属性,主要是提高iframe安全系数。iframe因安全问题而臭名昭著,这主要是因为iframe常被用来嵌入到第三方中,随后执行某些恶意操作。【这个与上面说到的安全沙箱(Sandbox)不同】 现在有一场景:我的网站需 iframe 引用某网站,但是不想被该网站操作DOM、不想加载某些js(广告、弹框等)、当前窗口被强行跳转链接等,我们能够设置 sandbox 属性:
到此这篇关于前端常用的安全问题以及防范措施的文章就介绍到这了,更多相关前端常用安全问题及防范内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多兼容脚本之家!