前端安全小结

前段时间我们的前端项目被公司安全部门扫出很多漏洞,涨了很多姿势,特意总结一下,对于安全这块也要开始重视起来了。

测试文件与必需文件控制

我们的前端项目在编译完成后,需要压缩成一个 zip 包才能部署到生产服务器上。压缩 zip 包是利用了一个 maven 的 assembly 插件,它有一个对应的 assembly.xml 配置文件,其中可以配置任意的规则用来剔除/包含文件。配置好插件后,可以在命令行中输入命令就能自动压缩了。

在生产环境下,我们不能将一些无关的文件/文件夹打到 zip 里,例如一些测试入口页面,不然被别人知道了就有很大的风险。

为此在每个移动前端项目的www目录下面都放一个assembly_prod.xml文件,把它配置为生产构建时的资源白名单,只有在名单中的文件/文件夹才会打包到生产包中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<fileSets>
<fileSet>
<directory>/</directory>
<!--<outputDirectory>/</outputDirectory>-->
<includes>

<!--文件夹-->
<include>build/</include>

<!--文件-->
<include>index.html</include>

</includes>
<excludes>
<!--<exclude>assembly.xml</exclude>-->
</excludes>
</fileSet>
</fileSets>

只需在白名单中把想要留存的文件配置好就行。

js 安全措施: 主要是eval函数

描述

此函数会将参数中的字符串当做javascript来解析, 会有潜在的 XSS 攻击风险,另外效率也非常低。 在我们前端项目中,有一些古老的 js 文件中使用到了它,其中最常见的是利用它来把字符串解析成json对象

防范

重写eval对应的代码,所有用eval的逻辑都可以使用更安全的语句来替代。 例如解析 JSON 可以利用JSON.parse函数。

重要cookie设为 HTTP only

此类 cookie 可以从浏览器或服务器端设置,但只能从服务端读取,客户端JavaScript无法获取他们的值。

nginx.conf 文件泄露

描述

之前我们项目的 nginx 配置有一些风险,配置如下:

我们将所有的错误日志、访问日志都放到了和应用代码同级目录中。这样如果知道了域名,就尝试几次得到我们的这些日志:

1
2
myhost/nginx/logs/error.log  # 这样可以得到错误日志
myhost/nginx/logs/access.log # 这样可以得到访问日志

知道访问日志是蛮危险的一件事,因为可以知道所有资源的 URL,别人就可以随意拿到我们网站的内容了。

防范

修改 nginx 配置

将错误日志输出到标准输出、关闭访问日志、将应用代码和nginx.conf放到不同的目录中。这样用户就不能通过 url 拿到我们的 nginx 配置了。

另外,添加一条 nginx 配置,如果请求 conf 文件,一律全部禁止:

1
2
3
location ~/.*\.conf {
deny all;
}

此时如果直接通过 URL 请求 nginx.conf 文件,会返回403 Forbidden响应

另外如果请求中有认证串,需要给串设置过期机制,超过期间无效。

XSS 攻击

何为 XSS 攻击

跨站点脚本(Cross-site scripting,XSS)是一种允许攻击者在另一个用户的浏览器中执行恶意脚本的脚本注入式攻击。

攻击者并不直接锁定受害者。而是利用一个受害者可能会访问的存在漏洞的网站,通过这个网站间接把恶意代码呈递给受害者。对于受害者的浏览器而言,这些恶意代码看上去就是网站正常的一部分,而网站也就无意中成了攻击者的帮凶。

也就是攻击者借助一个有漏洞的网站来攻击别人,窃取他的各种信息

XSS 最关键的地方就是需要在漏洞网站上执行精心构造的恶意脚本。

恶意脚本后果

这种在其他用户的浏览器中执行任意脚本的权限,赋予了攻击者有能力发动以下几类攻击:

  • Cookie 窃取:攻击者能够通过 document.cookie 访问受害者与网站关联的 cookie,然后传送到攻击者自己的服务器,接着从这些 cookie 中提取敏感信息,如 Session ID。
  • 记录用户行为:攻击者可以使用 addEventListener 方法注册用于监听键盘事件的回调函数,并且把所有用户的敲击行为发送到他自己的服务器,这些敲击行为可能记录着用户的敏感信息,比如密码和信用卡号码。
  • 钓鱼网站:攻击者可以通过修改 DOM 在页面上插入一个假的登陆框,也可以把表单的 action 属性指向他自己的服务器地址,然后欺骗用户提交自己的敏感信息。

XSS 攻击种类

主要有 3 种:

  • 存储型 XSS 攻击:恶意文本来源于网站的数据库
  • 反射型 XSS 攻击:恶意文本来源于受害者的请求
  • 基于 DOM 的 XSS 攻击:利用客户端而不是服务端代码漏洞发动攻击

存储型 XSS 攻击

数据库中存有的存在 XSS 攻击的数据,返回给客户端,若数据未经过任何转义,被浏览器渲染,就可能导致 XSS 攻击。示意图如下:

存储型XSS示意

具体步骤

  1. 攻击者利用 HTTP 请求将一段恶意文本插入网站的数据库中
  2. 受害者向网站请求页面
  3. 网站从数据库中取出恶意文本把它包含进返回给受害者的页面中
  4. 受害者的浏览器执行返回页面中的恶意脚本

此类型的攻击持续性很长,只要数据库中还存有恶意文本,那么用户就可能受害。

可能发生的场景
在一个拥有评论或者搜索的页面,后端将前端输入的文本不加校验就存储到数据库中,若攻击者故意输入精心构造的恶意文本,那么就可能发生此种攻击。

反射型 XSS 攻击

在一个反射型 XSS 攻击中,恶意文本属于受害者发送给网站的请求中的一部分。随后网站又把恶意文本包含进用于响应用户的返回页面中,发还给用户。示意图:

攻击步骤

  1. 攻击者构造了一个包含恶意文本的 URL 发送给受害者

  2. 受害者被攻击者欺骗,通过访问这个 URL 向网站发出请求

  3. 网站给受害者的返回中包含了来自 URL 的的恶意文本
  4. 受害者的浏览器执行了来自返回中的恶意脚本

此类攻击最关键之处是要诱导受害者去点击恶意的 URL。

可能发生的场景
发送包含恶意 URL 链接的短信、邮件给受害者,一般还会带有一些诱导性的语言,譬如红包、中奖之类。

DOM 型 XSS 攻击

在基于 DOM 的 XSS 的攻击中,除非网站自身的合法脚本被执行,否则恶意文本不会被受害者的浏览器解析。

攻击步骤

  1. 攻击者构造一个包含恶意文本的 URL 发送受害者
  2. 受害者被攻击者欺骗,通过访问这个 URL 向网站发出请求
  3. 网站收到请求,但是恶意文本并没有包含在给受害者的返回页面中
  4. 受害者的浏览器执行来自网站返回页面里的合法脚本,导致恶意脚本被插入进页面中
  5. 受害者的浏览器执行插入进页面的恶意脚本

DOM 型 XSS 前两种攻击最大的不同就在于执行恶意代码的时机不同,前两个都是在恶意代码一返回到受害者浏览器就会执行,DOM-XSS 会在浏览器解析 DOM 的时候才会执行攻击。

可能发生的场景
与反射性类似

防范 XSS 攻击

编码转义用户的输入

  1. 编码让浏览器把一些 html 实体当做普通的文本,而不是当做特殊代码。
  2. 将 url 编码
1
<a href="/site/search?value=UNTRUSTED DATA">clickme</a>

局限性

  1. 不能防范类似javascript:这样的恶意代码
  2. 如果页面需要让用户使用 HTML 自定义一些样式时,会无法工作

校验,对用户的输入进行过滤

使用黑名单、白名单的机制。

黑名单

如果前端的输入匹配到了黑名单中的某一条,那么就认为非法。譬如<script>javascript:

缺点:难以维护,例如<Script>或者JavaScript:;需要不断进行维护以避免过时。

白名单

只允许在名单内的才算通过。例如只允许<em><strong>这两种 HTML 标签存入数据库,或者如果是 url 的,只允许httphttps两种格式

相对来说比黑名单更好维护。

利用框架自带的安全策略

我们项目中前端使用的是 Angular 框架,它内置了防护 XSS 和 XSRF 攻击的机制。参考Angular 安全,只要不人为违反它的建议,应当来说可以防范大部分常见的攻击。

针对常见的漏洞和攻击,比如跨站脚本攻击 XSS,Angular 提供了一些内置的保护措施。


Angular 内置了一些支持来防范两个常见的 HTTP 漏洞:跨站请求伪造(XSRF)和跨站脚本包含(XSSI)。 这两个漏洞主要在服务器端防范,但是 Angular 也自带了一些辅助特性,可以让客户端的集成变得更容易。

CSRF 跨站请求伪造攻击

描述

当存心不良的 Web 站点导致用户的浏览器在可信的站点上进行非意愿的活动时,我们就说发生了跨站请求伪造(CSRF)攻击。

关于 CSRF 可以参见这两篇博客:

  1. 浅谈 CSRF 攻击方式
  2. 浅谈 CSRF

防范

常见的反 XSRF 技术是服务器随机生成一个用户认证令牌到 cookie 中。 客户端代码获取这个 cookie,并用它为接下来所有的请求添加自定义请求页头。 服务器比较收到的 cookie 值与请求页头的值,如果它们不匹配,便拒绝请求。
这个技术之所以有效,是因为所有浏览器都实现了同源策略。只有设置 cookie 的网站的代码可以访问该站的 cookie,并为该站的请求设置自定义页头。 这就是说,只有你的应用程序可以获取这个 cookie 令牌和设置自定义页头。evil.com 的恶意代码不能。

未授权访问

描述

如果资源请求不对用户的身份进行认证,那么任何人只要知道资源的 URL 就能获取。例如文件下载,如果请求的 URL 是

1
www.test.com/file.do?fileId=1

任何知道它的人都可以拿到这个文件。

这里另一个潜在的风险是这里的 fileId,如果后台存储文件时的主键 id 是自增的,那么这里将 fileId 设为 2 就能拿到另一个文件,也就是说我们所有的文件都可以被别人拿到。

防范

  • 访问权限认证,所有请求都要经过权限认证,可以在 url 上添加各种校验认证串,只有通过认证才能返回资源
  • 对于这样的资源,不要将主键弄成简单的规则,可以使用类似 uuid 这样的串

参考

  1. 如何让前端更安全
  2. 了解 XSS
  3. JavaScript 防 http 劫持与 XSS