在电商行业,线上的营销活动特别多。在移动互联网时代,一般为了活动的快速上线和内容的即时更新,大部分的业务场景仍然通过 Web 页面来承载。但由于 Web 页面天生“环境透明”,相较于移动客户端页面在安全性上存在更大的挑战。本文主要以移动端 Web 页面为基础来讲述如何提升页面安全性。
活动 Web 页面的安全挑战
对于营销活动类的 Web 页面,领券、领红包、抽奖等活动方式很常见。此类活动对于普通用户来说大多数时候就是“拼手气”,而对于非正常用户来说,可以通过直接刷活动 API 接口的“作弊”方式来提升“手气”。这样的话,对普通用户来说,就变得很不公平。
对于活动运营的主办方来说,如果风控措施做的不好,这类刷接口的“拼手气”方式可能会对企业造成较大的损失。如本来计划按 7 天发放的红包,在上线 1 天就被刷光了,活动的营销成本就会被意外提升。主办方想发放给用户的满减券、红包,却大部分被黄牛使用自动脚本刷走,而真正想参与活动的用,却无法享受活动优惠。
终端用户到底是人还是机器,网络请求是否为真实用户发起,是否存在安全漏洞并且已被“羊毛党”恶意利用等等,这些都是运营主办方要担心的问题。
安全防范的基本流程
为了提升活动 Web 页面的安全性,通常会引入专业的风控服务。引入风控服务后,安全防护的流程大致如图所示。
Web 前端:用户通过 Web 页面来参与活动,同时 Web 前端也会收集用于人机识别验证的用户交互行为数据。由于不同终端(移动端 H5 页面和 PC 端页面)交互形式不同,收集用户交互行为数据的侧重点也会有所不同。
风控服务:一般大公司都会有专业的风控团队来提供风控服务,在美团内部有智能反爬系统来基于地理位置、IP 地址等大数据来提供频次限制、黑白名单限制等常规的基础风控拦截服务。甚至还有依托于海量的全业务场景的用户大数据,使用贝叶斯模型、神经网络等来构建专业度较深的服务。风控服务可以为 Web 前端提供通用的独立验证 SDK:验证码、滑块验证等区分人机的“图灵验证”,也可以为服务端提供 Web API 接口的验证等。
后端业务服务:负责处理活动业务逻辑,如给用户发券、发红包,处理用户抽奖等。请求需要经过风控服务的验证,确保其安全性,然后再来处理实际业务逻辑,通常,在处理完实际业务逻辑时,还会有针对业务本身的风控防范。
对于活动 Web 页面来说,加入的风控服务主要为了做人机识别验证。在人机识别验证的专业领域上,我们可以先看看业界巨头 Google 是怎么做的。
Google 如何处理人机验证
Google 使用的人机验证服务是著名的 reCAPTCHA(Completely Automated Public Turing Test To Tell Computers and Humans Apart,区分人机的全自动图灵测试系统),也是应用最广的验证码系统。早年的 reCAPTCHA 验证码是这样的:
如今的 reCAPTCHA 已经不再需要人工输入难以识别的字符,它会检测用户的终端环境,追踪用户的鼠标轨迹,只需用户点击“我不是机器人”就能进行人机验证(reCAPTCHA 骗用户进行数据标注而进行 AI 训练的验证另说)。
reCAPTCHA 的验证方式从早先的输入字符到现在的轻点按钮,在用户体验上,有了较大的提升。
而在活动场景中引入人机识别验证,如果只是简单粗暴地增加验证码,或者只是像 reCAPTCHA 那样增加点击“我不是机器人”的验证,都会牺牲用户体验,降低用户参加活动的积极性。
Google 的普通 Web 页面的浏览和有强交互的活动 Web 页面虽是不同的业务场景,但对于活动 Web 页面来说,强交互恰好为人机识别验证提供了用户交互行为数据收集的契机。
人机识别验证的技术挑战
理想的方案是在用户无感知的情况下做人机识别验证,这样既确保了安全又对用户体验无损伤。
从实际的业务场景出发再结合 Web 本身的环境,如果想实现理想的方案,可能会面临如下的技术挑战:
(1)需要根据用户的使用场景来定制人机识别验证的算法:Web 前端负责收集、上报用户交互行为数据,风控服务端校验上报的数据是否符合正常的用户行为逻辑。
(2)确保 Web 前端和风控服务端之间通信和数据传输的安全性。
(3)确保上述两大挑战中提到的逻辑和算法不会被代码反编译来破解。
在上述的三个挑战中,(1)已经实现了人机识别验证的功能,而(2)和(3)都是为了确保人机识别验证不被破解而做的安全防范。接下来,本文会分别针对这三个技术挑战来说明如何设计技术方案。
挑战一:根据用户使用场景来定制人机识别验证算法
先来分析一下用户的使用场景,正常用户参与活动的步骤是用户进入活动页面后,会有短暂的停留,然后点击按钮参与活动。这里所说的“参与活动”,最终都会在活动页面发起一个接口的请求。如果是非正常用户,可以直接跳过以上的实际动作而去直接请求参与活动的接口。
那么区别于正常用户和非正常用户就是那些被跳过的动作,对实际动作进一步归纳如下:
进入页面。
短暂的停留。
滚动页面。
点击按钮。
以上的动作又可以分为必需的操作和可选的操作。对这一连串动作产生的日志数据进行收集,在请求参与活动的接口时,将这些数据提交至后端,验证其合法性。这就是一个简单的人机识别验证。
在验证动作的合法性时,需要考虑到这些动作数据是不是能被轻易模拟。另外,动作的发生应该有一条时间线,可以给每个动作都增加一个时间戳,比如点击按钮肯定是在进入页面之后发生的。
一些特定的动作的日志数据也会有合理的区间,进入页面的动作如果以 JS 资源加载的时间为基准,那么加载时间可能大于 100 毫秒,小于 5 秒。而对于移动端的按钮点击,点击时记录的坐标值也会有对应的合理区间,这些合理的区间会根据实际的环境和情况来进行设置。
除此之外,设备环境的数据也可以进行收集,包括用户参与活动时使用的终端类型、浏览器的类型、浏览器是否为客户端的容器等,如果使用了客户端,客户端是否会携带特殊的标识等。
最后,还可以收集一些“无效”的数据,这些数据用于障人耳目,验证算法会将其忽略。尽管收集数据的动作是透明的,但是验证数据合法性不是透明的,攻击者无法知道,验证的算法中怎么区分哪些是有效、哪些是无效。这已经有点“蜜罐数据”的意思了。
挑战二:确保通信的安全性
收集的敏感数据要发送给风控服务端,进而确保通信过程的安全。
Web API 接口不能被中途拦截和篡改,通信协议使用 HTTPS 是最基本的要求;同时还要让服务端生成唯一的 Token,在通信过程中都要携带该 Token。
接口携带的敏感数据不能是明文的,敏感数据要进行加密,这样攻击者无法通过网络抓包来详细了解敏感数据的内容。
Token 的设计
Token 是一个简短的字符串,主要为了确保通信的安全。用户进入活动 Web 页面后,请求参与活动的接口之前,会从服务端获取 Token。该 Token 的生成算法要确保 Token 的唯一性,通过接口或 Cookie 传递给前端,然后,前端在真正请求参与活动的接口时需要带上该 Token,风控服务端需要验证 Token 的合法性。也就是说,Token 由服务端生成,传给前端,前端再原封不动的回传给服务端。一旦加入了 Token 的步骤,攻击者就不能直接去请求参与活动的接口了。
Token 由风控服务端基于用户的身份,根据一定的算法来生成,无法伪造,为了提升安全等级,Token 需要具有时效性,比如 10 分钟。可以使用 Redis 这类缓存服务来存储 Token,使用用户身份标识和 Token 建立 KV 映射表,并设置过期时间为 10 分钟。
虽然前端在 Cookie 中可以获取到 Token,但是前端不能对 Token 做持久化的缓存。一旦在 Cookie 中获取到了 Token,那么前端可以立即从 Cookie 中删除该 Token,这样能尽量确保 Token 的安全性和时效性。Token 存储在 Redis 中,也不会因为用户在参与活动时频繁的切换页面请求,而对服务造成太大的压力。
另外,Token 还可以有更多的用处:
标识参与活动用户的有效性。
敏感数据对称加密时生成动态密钥。
API 接口的数字签名。
敏感数据加密
通信时,传递的敏感数据可以使用常见的对称加密算法进行加密。
为了提升加密的安全等级,加密时的密钥可以动态生成,前端和风控服务端约定好动态密钥的生成规则即可。加密的算法和密钥也要确保不被暴露。
通过对敏感数据加密,攻击者在不了解敏感数据内容的前提下就更别提模拟构造请求内容了。
挑战三:化解纸老虎的尴尬
有经验的 Web 开发者看到这里,可能已经开始质疑了:在透明的前端环境中折腾安全不是白折腾吗?这就好比费了很大的劲却只是造了一个“纸老虎”,质疑是有道理的,但是且慢,通过一些安全机制的加强是可以让“纸老虎”尽可能的逼真。
本文一再提及的 Web 环境的透明性,是因为在实际的生产环境中的问题:前端的代码在压缩后,通过使用浏览器自带的格式化工具和断点工具,仍然具备一定的可读性,花点时间仍然可以理解代码的逻辑,这就给攻击者提供了大好的代码反编译机会。
如果要化解“纸老虎”的尴尬,就要对前端的代码进行混淆。
前端代码混淆
前端的 JS 代码压缩工具基本都是对变量、函数名称等进行缩短,压缩对于混淆的作用是比较弱。除了对代码进行压缩,还需要进行专门的混淆。
对代码进行混淆可以降低可读性,混淆工具有条件的话最好自研,开源的工具要慎用。或者基于 Uglify.js 来自定义混淆的规则,混淆程度越高可读性就越低。
代码混淆也需要把握一个度,太复杂的混淆可能会让代码无法运行,也有可能会影响本身的执行效率。同时还需要兼顾混淆后的代码体积,混淆前后的体积不能有太大的差距,合理的混淆程度很重要。
断点工具的防范会更麻烦些。在使用断点工具时通常都会导致代码延迟执行,而正常的业务逻辑都会立即执行,这是一个可以利用的点,可以考虑在代码执行间隔上来防范断点工具。
通过代码混淆和对代码进行特殊的处理,可以让格式化工具和断点工具变得没有用武之地。唯一有些小遗憾,就是处理后的代码也不能正常使用 Source Map 的功能了。
有了代码混淆,反编译的成本会非常高,这样“纸老虎”已经变得很逼真了。
技术方案设计
在讲解完如何解决关键的技术挑战后,就可以把相应的方案串起来,然后设计成一套可以实施的技术方案了。相对理想的技术方案架构图如下:
下面会按步骤来讲解技术方案的处理流程:
Step 0 基础风控拦截
基础风控拦截是上面提到的频次、名单等的拦截限制,在 Nginx 层就能直接实施拦截。如果发现是恶意请求,直接将请求过滤返回 403,这是初步的拦截,用户在请求 Web 页面的时候就开始起作用了。
Step 1 风控服务端生成 Token 后传给前端
Step 0 可能还没进入到活动 Web 页面,进入活动 Web 页面后才真正开始人机识别验证的流程,前端会先开始获取 Token。
Step 2 前端生成敏感数据
敏感数据应包含用户交互行为数据、设备环境数据、活动业务逻辑数据以及无效数据。
Step 3 使用 HTTPS 的签名接口发送数据
Token 可以作为 Authorization 的值添加到 Header 中,数据接口的签名可以有效防止 CSRF 的攻击。
Step 4 数据接口的校验
风控服务端收到请求后,会先验证数据接口签名中的 Token 是否有效。验证完 Token,才会对敏感数据进行解密。数据解密成功,再进一步对人机识别的数据合法性进行校验。
Step 5 业务逻辑的处理
前面的步骤为了做人机识别验证,这些验证不涉及到业务逻辑。在所有这些验证都通过后,后端业务服务才会开始处理实际的活动业务逻辑。处理完活动业务逻辑,最终才会返回用户参与活动的结果。
总结
为了提升活动 Web 页面的安全性,使用了各种各样的技术方案,我们将这些技术方案组合起来才能发挥安全防范的作用,如果其中某个环节处理不当,都可能会被当作漏洞来利用,从而导致整个验证方案被攻破。
为了验证技术方案的有效性,可以持续观察活动 API 接口的请求成功率。从请求成功率的数据中进一步分析“误伤”和“拦截”的数据,以进一步确定是否要对方案进行调优。
通过上述的人机识别验证的组合方案,可以大幅提升活动 Web 页面的安全性。在活动 Web 页面应作为一个标准化的安全防范流程,除了美团,像淘宝和天猫也有类似的流程。由于活动运营的环节和方法多且复杂,仅仅提升了 Web 页面也不敢保证就是铁板一块,安全需要关注的环节还很多,安全攻防是一项长期的“拉锯升级战”,安全防范措施也需要持续地优化升级。
参考资料
https://www.google.com/recaptcha/intro/v3.html
https://segmentfault.com/a/1190000006226236
https://www.freebuf.com/articles/web/102269.html
作者简介
益国,美团点评 Web 前端开发工程师。2015 年加入美团,曾先后负责过风控前端 SDK 和活动运营平台的研发,现负责大数据平台的研发工作。