囫囵吞桃的个人博客 Home

在uni-app微信小程序嵌入ALTCHA组件的尝试

#技术

ALTCHA 是一种新型的防滥用技术,能够避免恶意用户对站点的恶意爆破或暴力尝试。与传统的图形验证码要求用户根据生成的图片写出其中的字符不同,ALTCHA 要求在用户的设备上计算一个数学难题。只有得到答案才能通过验证。有点类似加密货币。

背景

近期在使用Agent开发一个前后端分离架构的课设,用户前端使用uni-app开发微信小程序,后端使用ElysiaJS+bun。

为了实现一个“阻拦恶意用户爆破”的创新点,我尝试引入了ALTCHA。但是,ALTCHA并没有官方提供的uni-app组件或微信小程序组件。因此,我进行了一系列的尝试。

后端维护H5页面,小程序WebView访问

使用bun提供一个携带了ALTCHA组件的H5页面的访问,并部署于我的服务器。在小程序中,通过WebView去访问这个页面。起初在微信开发者工具和iOS真机上测试体验良好。由于这个页面可以设计成和小程序已有的UI风格类似,因此用户对跳转的感知也不强。

但是,在我后续访问了几次这个页面后,这个页面被微信拦截跳转了。用户必须要点一次“继续跳转”才能访问这个页面。这给用户带来了很强的割裂感。因此只能放弃这个方案。

uni-app重写ALTCHA组件

考虑到如今LLM Agent的便捷,完全可以短时间内让Agent根据ALTCHA项目已有的代码来复刻一个uni-app ALTCHA组件。

为了避免引入大量的多数内容都用不上的密码学库,我令Agent自行使用TS编写验证需要用到PBKDF2算法代码。

完工后,我在微信开发者工具上调试,体验良好,在5秒内能够进行近800次尝试,最终得到结果。

但是,问题又来了:我在iOS真机上调试,发现一直直到超时,也才进行了8次尝试,效率极低。

要求Agent对算法代码进行了两次优化,但是效果还是不尽人意。

后来,Agent分析问题,认为是iOS的JavascriptCore的运行此算法的效率低下,远不如在微信开发者工具上使用的V8引擎好。

对此我又心生疑惑,当初使用WebView的时候,页面上的JS代码不也是在JavascriptCore上运行的吗?为什么效率没有这么慢呢?

Agent回答,WebView上访问那个H5页面,会调用系统硬件的PBKDF2。由于硬件加速的加持,效率固然非常高。

引入WASM

为了解决此问题,Agent告诉我微信提议可以使用C语言编写PBKDF2算法中关键的SHA-256算法模块,编译成WASM,可以大大提升效率。

微信小程序从基础库 2.13.0 起支持 WASM,通过 WXWebAssembly.instantiate() 加载。iOS 从微信 8.0.49 起可用。

采用此方案,在iOS真机上实测后,效率果然大大提高。在20秒内进行了约500次尝试并通过验证。

我将此结果反馈给Agent,Agent认为效率还可以提升。其认为:20 秒的主要瓶颈已经不在 SHA-256 计算本身了——一半时间花在 JS ↔ WASM 边界跨越。每次 sha256_hash() 调用需要:复制输入到 WASM 内存 → 调用 WASM → 复制 32 字节输出。PBKDF2(cost=1000) 调用 2000 次 SHA-256,就是 2000 次跨越 JS↔WASM 边界的开销。

把 HMAC + PBKDF2 的整个循环搬进 WASM,JS 只调用一次 pbkdf2(password_ptr, salt_ptr, cost, keylen) → 32 bytes。这样 2000 次边界跨越变成 1 次。

经过测试,改进后尝试约500次只需要8s。

再次反馈给Agent,Agent认为还有提升空间。这一次,Agent给出的方案是,把整个验证模块的代码全部搬入WASM。果然,经过实测,在iOS真机上,进行近四百次计算总共耗时不到1s。不过也留下一个问题,即网络请求延迟反而成了这个验证过程的最大瓶颈。

总结

经过这一次尝试,得到的经验大概是:

  • 微信小程序访问外部网页(特别是自己的小众站点)可能会被拦截,因此最好不要在小程序中尝试访问外部网页。
  • 微信小程序支持WASM,可以适当用于高性能要求的模块。
  • 微信小程序的WASM模块运行的瓶颈可能往往在JS与WASM的通信。因此要尽量减少它们之间的通信,即减少边界跨越次数。

此外,由于我起初没有详细记录每一步改进的实际运行耗时,也没有大量重复测试取平均值,因此文中提到的测试数据可能并不严谨。

题外话

微信小程序+uni-app的开发体验有待提升。我使用npm run dev:mp-weixin,打包的dist/dev/mp-weixin倒入微信开发者工具。

一般来说,源代码发生更改,都会触发npm的差量编译,然后在微信开发者工具也会触发重新构建。但有的时候似乎不会触发,或者说即使触发后,某些更改还是不能应用。我点击编译按钮后,有的时候也不能看到最新的更改。必须要点“全部清除”后才能看到效果。

甚至有的时候,小程序还不能正常加载,提示渲染层错误。但是再次点编译后又可以。

甚至还有一次,遇到了微信登陆态失效,我重新扫码登录还是不会获取新的登陆态的问题。重启微信开发者工具后再次扫码登录又恢复。

其余可能有用的背景信息:

时间:2026年6月12日~6月13日 开发设备、微信开发者工具运行设备:Macbook Pro M1 Pro , macOS 15.7.2 , 微信开发者工具2.01.2510290 测试时用的iOS设备:iPhone 12 pro max , iOS 18.5 , 微信8.0.73 , 小程序基础库版本3.16.1 前端技术栈:uni-app , TypeScript , vue3 , vite , NodeJS