在博客间随机游走的时候,无意间看到了一个博客的评论区中部分评论是带着锁的 [Secret Message]. 非常有趣。更有趣的是这个私密评论是站长回复其他人的评论——如果是其他人回复站长的私密评论实现起来应该不难,但是反过来的话,要怎么靠谱地实现这个东西呢?
容易知道这是一个 WordPress 站点。在 WordPress 下,实现私密评论的方法自然很多。毕竟有 PHP 作为中间层来隔离,我这种喜欢戳来戳去的人通常不会带来什么实质性的危害——后端只要返回尽可能有限的数据,比如这条评论只返回一个特殊的标记,正文什么都不返回就可以了。
不过,撬开开发者工具分析一下请求,发现这个评论区的请求并没有给这条私密评论安排一个特殊标记。它的内容就只是简单的 [Secret Message], 而已。看来这个小锁图标应该是纯前端的工作了。(不过这样不会很易碎么?它确实很易碎啊,因为你的代码里确实是真的在 === '[Secret Message]' 啊!)
抛开这个站点的实现不谈,如果说我需要实现这个功能,我们有哪些选项呢?
L0. 我加密?我装的
最简单的方案是:不实现。从后台拿到邮箱之后写一封邮件作为回复,然后再简单地往评论系统里塞一条模板消息表示俺回复过了就好了。[Secret Message] 什么的只是保持神秘感的桥段而已。
L1. Cookie/LocalStorage
我们或许可以借助 Cookie 来搞定这个问题:每位留言的访客都会收到一个 Cookie. 后台则可以根据 Cookie 信息来决定返回实际的内容或者返回提示文字。
但这么做的话有一个缺陷:如果访客换了一个浏览器或者换了一个设备,比如之前是用电脑发的评论,现在是在手机上看,那就只能干瞪眼了,因为 Cookie 变了。
这个缺陷当然也可以通过 OOB 的方法绕过,比如邮件通知里携带一个特殊的参数来设置 Cookie 或者解密;但这样的话还得小小地祈祷一下邮件不被扔到垃圾桶或者直接被退信。而且有些时候访客不愿意填一个真实的邮箱进来,那这封带着解密链接的邮件就彻底石沉大海了。
实际观察评论加载的行为时,我发现这个站点也并没有使用 Cookie. 不过,它确实在 LocalStorage 里面塞了一个访客 ID 给我。这个访客 ID 我简单翻找了一下,只和加载页面的文字有一些关系。当然,或许可能这个站点的实现是随着访客 ID 的不同,实际返回的前端脚本也会不一样,进而导致请求本身也会不一样?这么做显然是可以的,虽然对于服务器负载确实有点不友好。
要求访客为了能读评论而注册账号并登录显然有点不太现实,有没有更无感而且不依赖 Cookie 这种可能会要求你做一堆合规改造的方法呢?
L2. SubtleCrypto
现代浏览器里是可以通过 JS 做一些通行的密码学操作的,比如 SHA-256 和 AES-256-CBC. 它们都可以通过 crypto.subtle 来调取。
或许我可以把邮箱作为密钥,来加密这个评论。比如我可以取邮箱的 SHA-256 作为密钥,用户名的 MD5 作为 IV[1], 使用 AES-256-CBC+PKCS#7 算法来加密评论的正文。这样,其他人加载评论时只能看到类似于
-----BEGIN ENCRYPTED MESSAGE-----
deMtJS/ZgZqrVt0hzXih1Q==
-----END ENCRYPTED MESSAGE-----
这样的内容;而如果在评论区的用户名栏中填写 John Doe 并邮箱栏中填写 someone@example.com, 则这个评论就会被前端脚本自动识别并解密成
Hello, world!
这样我们就不需要浏览器端保存任何状态,也实现私密评论了;同时这样的数据甚至可以安全地缓存在 CDN 中,对服务器的负载也相当友好——前提是这位访客认真地填写了他的用户名和邮箱,而不是脸滚键盘随便生成了这些内容。这样的话那真的是谁都救不了。
当然,这么做会带来另一个比较头大的问题:很难保证邮箱本身没有泄露。评论旁边就挂着 Gravatar 的情况下,用一点小小的社工手段把邮箱解出来或许并没有想象中那样困难。
我们当然可以让访客自己填一个密码之类的,但那样的话「注册账号」交互的阻力就又回来了。
Welp. That's why we can't have good things. 还是应该直接写邮件的。
L3. 跨设备追踪?
Eww... 但这事可能还真能成,因为广告 ID 可以提供另一层身份验证。我不知道你具体是谁,但是我知道你可能对什么感兴趣。如果我能拿到 32b 的数据,或者说了解你对于 32 样东西是否感兴趣,那么唯一地识别一个人应该是足够的。整个过程甚至不需要你动手,它在后台会自动完成。
除了广告 ID, 还有 IP 地址、粗略位置、浏览器窗体大小、系统中安装的字体、系统中可调用的程序、可用的环境传感器、加速计的漂移特征、利用超声波进行跨设备的数据传输……
想法很疯狂,但我应该是没啥技术实力来实现这个。况且也已经决定了不追踪,那么就还是说到做到。
另:后端直接把所有该返回的不该返回的数据一并扔出来(比如明文的邮箱甚至是浏览器 UA),可能不是一个比较理想的选择。但我注意到代码里有检查邮箱具体值的一些操作,主要是用于标识站长……好吧,好吧,或许这是一个合理的答案。Thanks for the provisioning. smile and wink
我知道 MD5 不是一个安全的散列函数,但这总好过用一个固定的 IV. ↩︎




















