无论您是 React.js、Angular、Vue.js,还是仅仅是前端开发人员,您的代码都可以成为黑客的诱人之门。
作为前端开发人员,我们最关心的是性能、SEO 和 UI/UX——安全性往往被忽视。
您可能会惊讶地发现,大型框架让您对跨站点脚本 (XSS) 攻击敞开心扉。 有一些危险的操作名称,例如 React 中的 dangerouslySetInnerHTML
或 Angular 中的 bypassSecurityTrust API。
我们应该记住,在安全性方面,前端现在与后端或 DevOps 分担相同的责任。 前端可能发生数以千计的恶意攻击。
让我们了解最常见的——这些将涵盖这些类型的攻击的很大一部分。
常见的攻击手段
让我们来了解一下常见的攻击类型有哪些。
1. 无限制文件上传
这是一种将恶意文件上传到服务器然后执行以攻击系统的攻击。 攻击可能包括:文件系统或数据库过载、系统完全接管、客户端攻击、向后端系统转发攻击或简单的篡改。
2. 点击劫持
这是一种诱导用户点击非本站网页或元素的攻击方式。这种攻击可能导致用户在不经意之间提供证书或者敏感信息,下载恶意软件,访问恶意网页,在线购买产品或者被偷偷转移资产。
注:简单的说就是在用户观看到的网站上覆盖一层透明的恶意网站,诱导用户点击恶意网站上的按钮来触发攻击行为
3. XSS攻击
这是一种将恶意脚本以浏览器端脚本的形式注入网页的攻击。 网站上的缺陷使这些攻击得以成功并广泛传播。
4. SQL注入
这是一种攻击,其中注入 SQL 语句中的恶意代码以通过输入字段破坏您的数据库。
5. 拒绝服务攻击(DoS 攻击)
这是一种攻击,通过用流量轰炸您的服务器,使目标用户无法使用服务器或其资源。
6. 中间人攻击或会话劫持
这是一种拦截客户端和服务端之间的通信,从中窃取用户的密码、账号或者任何个人详细信息的攻击方式。
攻击者总是试图在前端找到一些漏洞来到达服务器并完成他的工作。 在本文中,我们将看到一些在编写前端代码时要始终牢记的常见最佳实践。
防范手段
1. 严格的限制用户的输入(第一个攻击点)
应该始终严格的对待用户输入,避免诸如SQL注入、点击劫持等等。因此,在将用户输入发送到服务端之前,校验并过滤用户输入是很重要的。
可以通过删除或替换危险的字符来过滤数据,例如,使用白名单并转义输入的数据。
但是,我意识到过滤和编码用户输入并不是一件容易的事,因此我们可以使用以下开源库:
DOMPurify
. 这是最简单的使用方法,并且有一种方法来清理用户的输入。 它有一个自定义规则的选项,它支持 HTML5、SVG 和 MathML。secure-filters
. 一个 Salesforce 库,提供清理 HTML、JavaScript、内联 CSS 样式和其他上下文的方法。 如果您想在其他地方使用用户输入,例如生成 CSS 或 JavaScript,它尤其有用。
如果是文件上传,请务必检查文件类型并且使用文件过滤功能仅允许某些文件类型上传。
2. 注意隐藏保存浏览器内存中的数据或字段
如果我们利用type="hidden"
来隐藏页面中敏感数据,或者把他们放到浏览器的localStorage
、sessionStorage
、cookies
时,我们需要谨慎的考虑这些数据是否安全。
攻击者可以轻松访问添加到浏览器中的所有内容。攻击者可以打开开发工具并更改所有保存在内存中的变量。如果你根据localStorage
、sessionStorage
、cookies
中的值隐藏了身份验证界面,该怎么办?
如果攻击者找到注入脚本的方法,那么浏览器中的 ZapProxy 等工具甚至检查工具可以将这些值暴露给攻击者,然后他们就可以使用它们进行进一步攻击。
因此,避免使用type="hidden"
,避免将密钥、身份验证令牌等尽可能多的存到浏览器的内存中。
3. 使用强大的内容安全策略 (CSP)
永远不要相信服务器返回的所有内容——始终定义一个强大的 Content-Security-Policy HTTP 标头,它只允许某些受信任的内容在浏览器上执行或呈现更多资源。
有一个白名单是一个很好的做法——一个允许来源的列表。 现在,即使攻击者注入脚本,脚本也不会匹配白名单,也不会被执行。
举个例子:
content-security-policy: script-src ‘self’ https://apis.xyz.com
这里定义我们的Web应用仅仅信任https://apis.xyz.com
和本身域名的脚本。对于其他域名的资源都会在控制台中报错。
你可以在MDN网站上阅读更详细CSP
说明。
注:不仅可以在header
中设置csp
规则,你也可以在meta
标签中设置。
4. 开启XSS保护模式
如果攻击者通过某种方式在用户输入中插入攻击代码,我们可以通过 "X-XSS-Protection": "1; mode=block" 来告诉浏览器阻止响应。
大多数现代浏览器默认情况下都启用了XSS保护模式,但仍建议添加X-XSS-Protection
。 这有助于提高不支持CSP
的旧版浏览器的安全性。
5. 避免典型的XSS错误
XSS 攻击通常可以追溯到 DOM API 的 innerHTML。 例如:
document.querySelector('.tagline').innerHTML = nameFromQueryString
任何攻击者都可以使用上面的代码行注入恶意代码。
考虑使用 textContent 来代替 innerHTML,避免直接生成HTML。如果你不生成HTML,那就不会有JavaScript插入到页面中,即使你可以在页面中看到攻击代码,但是什么也不会发生。
密切关注Trusted Types(MDN地址),这是由google程序员开发出来的,旨在防范所有基于DOM的XSS攻击的方案。
在 React.js 的情况下,dangerouslySetInnerHTML 是明确且具有警示意义的,并且可以产生与 innerHTML 类似的影响。
注意:不要根据用户输入设置innerHTML值,尽量使用textContent代替innerHTML。
此外,应正确设置 HTTP 响应标头 Content-Type 和 X-Content-Type-Options 及其预期行为。 例如,不应将 JSON 数据编码为文本/HTML,以防止意外执行。
6. 禁用 iframe 嵌入
禁用 iframe 可以使我们免受点击劫持攻击。我们应该在header中添加"X-Frame-Options": "DENY",来禁止浏览器在页面中渲染iframe
。
此外,我们可以使用 frame-ancestors CSP 指令,它可以更好地控制哪些父级可以将页面嵌入到 iframe 中。
7. 通用的错误提示
类似"您的密码有误"这样的提示对用户很友好,同时,他对攻击者也很友好。他们可以通过服务端返回的错误信息来判断他下一步需要进行什么样的攻击。
当处理用户的账号、邮件、个人信息时,我们应该尝试使用一些模棱两可的错误提示,类似“错误的登陆信息”。
8. 使用验证码
在面向公众的端点(登录、注册、联系)使用验证码。 验证码的目的在于帮助我们区分真人和机器人,并且也可以阻止DoS攻击。
9. 始终设置Referrer-Policy
当我们使用<a>
标签或者超链接引导用户离开我们的网站时,确保你在请求header里面添加了"Referrer-Policy": "no-referrer",或者在<a>
标签中添加了rel = noopener 或者 noreferrer属性。
如果你打开的是一个同域的页面,那么你将可以在新页面访问到原始页面的所有内容,包括document
对象(window.opener.document
)。如果你打开的是一个跨域的页面,你虽然无法访问到document
,但是你依然可以访问到location
对象。
这意味着,如果你在你的站点或者文章中,嵌入了通过新窗口打开一个新页面的链接,这个新页面可以使用window.opener
,在一定程度上来修改原始页面。
<a href="http://example.com" target="_blank" rel="noopener noreferrer"></a>
注:rel=noopener
保证跳转过去的网站无法通过window.opener
窃取原来网页的信息。rel=noreferrer
作用是防止将引用者信息传递到目标网站。上面提到的策略大家可以去mdn上了解一下MDN Referrer-Policy、MDN Link Type
10. 限制浏览器的功能或者API
就像CSP
可以限制可信的资源域名一样,我们也可以限制浏览器提供哪些能力给我们用。我们可以利用http header中的Feature-Policy
字段来限制使用浏览器提供的功能。
提示:禁用一切你不使用的功能
注:Feature-Policy
是一个实验中的header
属性,目前在chrome
浏览器中的兼容性尚可,IE
和Safari
都不支持。具体可以在MDN Feature-Policy中了解。
11. 定期审查npm依赖
定期运行 npm audit 以获取易受攻击的软件包列表并升级它们以避免安全问题。
GitHub
现在会标记出哪些存在漏洞的依赖。我们也可以使用Snyk
来自动检查你的源码,并且自动升级版本号。
12. 分离你的应用
与后端一样,我们也拥有微服务架构,其中,将单一的Web应用转变为多个小型前端应用的聚合,每个小型前端应用可以单独运行。
同样的原理可以应用于前端。 例如,一个Web应用可以分为公共部分、经过身份验证的部分和管理部分,每个部分都托管在不同的子域上,例如
https://public.example.com, https://users.example.com 和 https://admin.example.com .
这将确保减少客户端漏洞。
注意:适当的分隔还可以防止应用程序公共部分中的 XSS 漏洞,防止它自动泄露用户信息。
13. 尽量避免使用第三方服务
一行代码第三方服务,如 Google Analytics、Google Tag Manager、Intercom、Mixpanel,可以使您的 Web 应用程序易受攻击。 想想这些第三方服务受到损害的情况。
拥有一套健全的CSP
策略很重要。大多数第三方服务都有定义的CSP
指令,因此请务必添加它们。
此外,请确保在添加script
标签尽可能包含 integrity
属性。 子资源完整性功能可以验证脚本的加密hash值,并确保它没有被篡改。
<script src="https://example.com/example-framework.js" integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/ux..." crossorigin="anonymous"></script>
注:将使用base64编码
过后的文件哈希值写入你所引用的 <script>
或 <link>
标签的 integrity
属性值中即可启用子资源完整性校验功能。
仔细考虑自动填充字段
存储在浏览器自动填充中的个人身份信息对用户和攻击者都很方便。
攻击者添加第三方脚本来利用浏览器的内置自动填充功能来提取电子邮件地址以构建跟踪标识符。 他们可以使用这些来构建用户浏览历史配置文件,然后将其出售给坏人。 在此处阅读更多信息。
我们中的许多人甚至不知道他们浏览器的自动填充存储了哪些信息。
提示:禁用敏感数据的自动填充表单。
MDN中也有一个web安全相关的专题,有兴趣可以关注一下MDN Web 安全
[译]原文链接