在浏览器中攻破JavaScript到底有多轻松?

译文
开发 前端
请大家想象一下,如果您正在某种认证系统中使用JavaScript框架(例如Backbone或者AngularJS),而且需要端点验证机制以保障安全。这样的情景似乎没什么难度,毕竟决定权始终掌握在服务器手中、而且在我们进行任何操作前都会通过验证检查其是否符合规定。

如何挫败攻击者的入侵企图。

[[83508]]

Jesus Rodriguez发问:

我的问题涉及JavaScript安全性。

请大家想象一下,如果您正在某种认证系统中使用JavaScript框架(例如Backbone或者AngularJS),而且需要端点验证机制以保障安全。这样的情景似乎没什么难度,毕竟决定权始终掌握在服务器手中、而且在我们进行任何操作前都会通过验证检查其是否符合规定。

但是如果整个流程不涉及服务器,对安全性的要求还能不能成为现实?

举例来说,如果您拥有一套客户端路由系统而且打算通过保护具体线路来巩固用户登录的安全性。在这种情况下,大家需要首先向服务器发送ping指令来查询自己是否有权访问受保护的线路,接着再执行后续操作。问题在于,当我们ping向服务器时,系统会将服务器响应结果保存为一个变量。这样在下一次使用这条专有线路时,系统会自动检查您是否已经登录(而不再重复ping向服务器),而仅仅通过上一次响应结果决定您的申请是否通过。

听起来似乎有机可乘,那么通过修改变量来达成访问到底可不可行?

坦白讲,我对于安全及JavaScript方面的知识了解不太多,但这样变量只存在于模块模式中的专有部分而非全局环境当中。这里只涉及获取方而无关于设定方,如果看来,我们似乎有机会针对其加以破解。

发送机密数据

Joachim Sauer的回答(获得66票支持):

破解过程非常简单:任何依赖于客户端的安全机制都会按我们的要求进行操作,因此只要攻击者获得控制权、接下来的入侵任务将手到擒来。

大家可以在客户端上进行安全检查,但检查的对象实际上只涉及“缓存”内容(这是为了避免与服务器之间的高昂往返成本,如果客户端已经了解到响应内容为‘否’,那么直接从缓存内读取结果即可、无需再与服务器进行通信)。

如果大家希望保障一组用户的安全,请确保这些用户的客户端永远无法获取服务器响应信息。因为一旦大家向客户端发送“机密数据”,那么即使是加上了“请不要显示其内容”的指示,攻击者也能够轻松通过禁用对应代码对请求内容进行检查。

如大家所见,这项答案并没有真正提到任何与JavaScript及浏览器有关的细节。这是因为无论您所使用的是哪一种客户端,其基本概念都是相通的。事实上,无论是胖客户端(即传统客户端-服务器应用)、传统的Web应用程序还是承载着大量客户端JavaScript信息的单页面应用,都适用于前面所提到的假设。

一旦数据离开服务器端,大家必须做好攻击者有能力对其进行全面访问的准备。

另辟蹊径

Benjamin Gruenbaum的回答(获得17票支持):

在首先阅读Joachim的答案,然后再转到这里。前一条答案涵盖了客户端安全漏洞的一般性出现原因,现在我们一起来看Benjamin对于解决这项问题的建议。

这是一项无需手动将每条请求发送至服务器端进行验证的客户端-服务器通信安全规划:

大家仍然允许服务器拥有掌控权,服务器也仍然对客户端发来的请求进行验证,但整个过程都以透明化形式进行。

假设通过HTTPS协议防止MITM(即中间人)攻击。

  • 客户端与服务器间进行首次握手时,服务器会针对客户端生成一个公共密钥,同时生成一个私有密钥,从而构成一套非对称式加密方案。客户端在本地保存的是服务器的“公共”密钥,并利用一条不会保存在任何位置的安全密码进行加密。
  • 客户端现在已经离线。客户端希望执行受信操作。客户端通过输入密码以获取服务器的公共密钥。
  • 客户端现在会根据对数据的了解执行操作,并在服务器对应公共密钥的辅助下对每项操作进行加密。
  • 当客户端处于在线状态时,客户端会向受公共密钥加密的服务器发送自身ID以及所有相关操作。
  •  服务器负责对操作进行加密,如果加密格式无误,则信任这些操作确实来自客户端。

备注:

  • 我们绝不能将客户端的密码保存在任何位置,否则攻击者将有机会取得密钥并实施自己的恶意操作。这套方案的安全性依赖于服务器为客户端所生成的密钥的完整性。当需要使用该密钥时,服务器仍然会对客户端进行验证。
  • 我们实际上仍然需要仰仗服务器的安全机制而非客户端,客户端执行的每项操作都需要经过服务器的验证。
  •  我们可以在Web Workers中运行外部脚本。请注意,每一条JSONP请求如今都有可能引发严重的安全问题。我们需要不计一切代价保护密钥。一旦密钥丢失,攻击者可以轻而易举地伪装成用户并实施恶意活动。
  •  这样的处理方式符合“不ping向服务器”的要求。攻击者无法简单利用伪造数据模仿HTTP请求,只要密钥没有泄露、一切恶意活动都不可能实现。
  •  Joachim的回答仍然正确,我们事实上仍然将所有验证工作留给了服务器。惟一的区别在于,现在我们不必在每次操作时都要求服务器验证密码。大家只需要在提交内容或者更新数据时才直接面向服务器。我们所做的一切工作都是为了在客户端保存受信密钥,并且保证客户端重新验证的顺利进行。
  •  对于单页面应用(例如Angular)而言,这是一套相当通行的控制方案。
  •  我之所以将服务器的公共密钥称为“公共”,是因为RSA等方案使用过同样的形容字眼,而且意味着该密钥属于敏感信息、应该受到严格保护。
  •  我不会把密码保存在任何存储机制当中。我需要确保用户每一次运行离线代码时都必须提交“离线”密码。
  • •不要使用自己的加密机制:请务必使用知名度较高的可靠加密库,例如斯坦福大学的认证机制。

请认真阅读并掌握这套推荐方案。在大家将此认证机制推向实际操作之前,请先向安全专家咨询其可行性及潜在影响。这项问题非常严重,实施过程并不轻松而且很容易在执行中出现错误。

最关键的一点在于,千万不要让其它脚本介入到页面当中,这意味着我们只能通过Web Workers引入外部脚本。我们绝不能轻信其它外部脚本,因为其中很可能潜伏着可能在用户登陆时截获密码的恶意内容。

如果大家无法完全肯定脚本安全性或者推迟其执行(也就是说,该脚本不应该成为事件的访问对象,而只作为同步代码),请务必向用户发出提示而且不要使用内嵌密码字段。另外,不要把密码内容保存在变量当中——再次强调,只有百分之百确信用户的计算机不存在安全漏洞后才能这么做(当然,这又会涉及到服务器对客户端的验证)。

我还是要再次提醒各位,我们要以怀疑的态度审视客户端,这一点在Joachim的回答中也已经表达得非常清楚。在新机制下,我们只不过可以在工作开始之前摆脱ping服务器这一步骤。

任何人都不可信

nvoigt的回答(获得7票支持):

游戏行业有一句名言:“客户端掌握在敌人手中”。任何运行在外部安全区域(例如服务器)中的代码都有遭到侵袭的可能。要不要真正运行我们提供的“安全代码”完全由客户端决定,用户只能在有限的范围内做出选择。不过在本地代码方面,我们至少可以通过自动混淆机制保护代码内容;也就是说,必须是拥有出色编程能力的攻击者才有可能跨过这道障碍,从混淆机制的重重迷雾下看清JS与纯文本信息的真面目。

攻击活动所需要的准备工作非常简明:一台代理服务器外加一个文本编辑器,基本上已经足够了。诚然,攻击者需要对编程拥有相当程度的了解,但利用文本编辑器修改脚本内容可比直接编写一套可以执行的注入代码简单得多。

 

责任编辑:陈四芳 来源: 51CTO
相关推荐

2010-09-16 12:48:17

JavascriptIE6

2017-01-13 16:26:56

开发

2017-01-05 09:07:25

JavaScript浏览器驱动

2017-12-28 09:22:24

机器学习应用生活

2011-06-07 10:15:38

GNULinux

2016-10-09 08:38:01

JavaScript浏览器事件

2020-10-20 09:38:29

微软浏览器JavaScript

2020-12-15 11:05:21

JavascriptChrome浏览器

2022-07-07 07:22:01

浏览器JavaScript工具

2022-09-01 21:02:31

手机卫星5G

2016-09-22 16:47:55

iOSAndroidWindows Pho

2022-04-25 10:26:11

Python代码浏览器

2010-09-15 09:12:03

JavaScript浏览器兼容

2019-12-06 10:21:50

2023-05-17 00:10:55

GPU浏览器解锁

2021-04-21 08:28:06

微软EdgeGoogle

2022-09-27 09:43:08

物联网设备物联网

2021-03-09 05:49:32

通信女性从业者通信行业

2012-05-31 09:24:55

云计算云存储

2020-08-05 07:00:00

SSD硬盘存储
点赞
收藏

51CTO技术栈公众号