随着开发框架和平台的不断成熟,需要开发者考虑的安全问题越来越少,但并不是开发者就不需要关心项目的安全问题。Linux、Tomcat等大型项目时不时爆出安全漏洞,把网络安全话题重新拉回大众视野。现代前端开发依赖node作为打包、构建和依赖管理平台,最近一次安全问题就是npm仓库中的event-stream包多了一个flatmap-stream依赖,而这个依赖项正在窃取开发者的数字货币。
就前端项目来说,需要考虑的安全问题相对较少,受到攻击后的损失也不及服务器被攻击后那么大,前端主要需要考虑的安全问题有:
npm生态下依赖的安全性。npm 非常年轻,和Java相比包的质量参差不齐,也缺乏良好的审查机制,不过好在npm提供了一些安全工具帮我们审查依赖的安全问题。
XSS跨站脚本攻击。XSS攻击是非常常见的攻击方式,前端开发需要日常注意,我们后面会主要讨论这类攻击。
CSRF跨站请求伪造攻击。CSRF不是非常流行,目前来说容易将此类攻击的破坏性降低到可以接受的程度。
代码自查
开发者和攻击者最大的不同之处在于,开发者完全拥有代码,因此占据了主动权。一般来说攻击者的扫描都只能对线上产品进行,如果开发者在上线之前对代码进行审查和扫描,可以事半功倍。
另外内部的渗透测试也类似于模拟攻击者来进行扫描业界已知漏洞,而代码层面的审查则需要开发团队完成。
npm audit
(npm audit tool/npm 安全扫描工具)
为了提高npm依赖的安全,npm 6.1 后添加了npm audit工具,这个工具可以搜索当前项目中使用的依赖是否存在安全问题,并提供了npm audit fix工具修复。
它的工作原理是维护了一个已知不良依赖的名单,如果代码中使用了直接从GitHub而不是npm仓库中获取依赖,或不知名的依赖。npm audit也是无法发现威胁。总的来说在加入第三方依赖时,需要谨慎考虑,不滥用依赖在前端开发也是非常重要的。
Sonarqube
(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型攻击采取安全措施。
(XSS Demostration/ XSS 攻击演示)
上面这个动图是我假设的一个漏洞,前端代码直接接收外部输入,并添加到页面上。演示攻击者使用了一段代码:
通过图片触发onerror事件的方式执行一段JavaScript代码片段,再读取LocalStorage中的token,最后通过图片Ping的方式发送到外部网站。这只是一段作为演示用的攻击方式,这种注入XSS的代码叫做 XSS payload,很多强大的payload在网络上传播。下面我们来讨论下在开发过程中如何应对这些攻击。
使用HTTP头启用浏览器安全行为
浏览器有很多内置的安全行为,可以防范XSS攻击,第一步需要做的是在上线时合理配置服务器环境,这是一种性价比很高的方式。使用Nginx或者Apache输入相应头信息不是一件特别难的事,这里有一个checklist,分别说明了一些必要的HTTP头和用途。
下面演示一个HTTP only的例子,即使XSS攻击成功,也无法盗走token:
(protect cookie by HTTP Only/HTTP Only 保护 cookie)
避免框架中的危险特性
现代前端开发中使用了一般基于常用的框架开发,框架提供了很多安全特性在输入内容到DOM避免了XSS注入,但是如果不当使用,也会有一些风险。框架为了提供更大的灵活性往往允许原生的HTML内容被添加到DOM中并提供了对应API,但基本上也会在文档中说明。
Vue的v-html指令。 Vue的明确提示使用该指令的前提是信任输入内容,但是大量项目使用了此指令,甚至从URL上获取的部分内容。下面图片中的使用方式在项目中很常见,但是如果使用xss payload很容易像上面演示的那样被xss注入攻击。
(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标签定义一系列白名单规则,限制页面上脚本的执行和资源的加载来源,例如不允许执行内联代码(块内容,内联事件,内联样式),禁止执行eval() , newFunction() , setTimeout([string], ...) 和setInterval([string], ...) ,达到进一步限制页面脚本的目的。例如:
Content-Security-Policy:default-src 'self'; img-src https://*; child-src 'none';
CSP 策略包括多个指令和指令值组成:
CSP 策略中有一个特别的指令report-uri可以配置页面上违规后的报告,一旦浏览器检测到违规的资源加载,浏览器会发送一个JSON数据包到指定服务器。
应对CSRF攻击
CSRF攻击者在用户已经登陆目标网站后,诱导用户访问一个攻击页面,利用用户已经获取的权限,发起伪造的请求。
举个例子:
假设GitHub提供了一个给仓库加星的接口,htttps://github.com/{repositoryName}/star 而且这个接口使用了GET方法,当然GitHub不会这样做。
这样攻击者就可以在GitHub上Readme文件中中增加一张图片:
当用户浏览到这个仓库的时候就会给该仓库增加一个星。
因此业界通常的做法是避免使用GET操作对数据资源的修改,使用POST时增加一个一次性的token。
如果Spring boot中使用Spring security,会有默认的 CsrfFilter,只需要注册CsrfFilter即可启用CSRF机制。客户端对相同的Restful资源发出POST请求之前需要首先从GET方法得到一个一次性的token,否则会得到一个403错误。
其他
实际项目中我们还有其他的一些安全措施:
例如加密代码中的密码等敏感信息,加密本身对前端来说意义不大,但是可以增加攻击者反向分析代码的难度,屏蔽代码中可以直接搜索到的关键字,例如Login、Password等字符串。
另外也要防止程序报错后意外暴露一些信息给用户,面对各种各样的异常,用户不可能主动也没有能力报告错误,我们可以通过使用sentry.io之类的一些工具收集控制台报错信息 。
最后这个世界上没有绝对的安全,即使CSP这类极其严格的策略都有可能被绕过,前端开发中安全也需要考虑成本,应该选用性价比高的安全策略。安全也不是独立的,应该和服务器、甚至操作系统层面联合考虑,例如后端提供的资源应该是通过ID不可枚举的,上传文件的时候也应该嗅探内容和MIME信息决定文件类型。
参考链接:
- https://github.com/jshanson7/npm-audit/blob/master/blacklist.json
- https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CSP
- 业界安全组织 OSSTM, OWASP, TCSEC