【CEP 专题】 插件的安全防护

首先和各位小伙伴道个歉,本博客断更了有半年之久。去年我成功的考上了研究生,然后从今年9月份开始,每周一到周五要上班,周六周日要去上课,基本就是全天候不停歇,整个人处于焦麻的状态。同时在今天 6 月份的时候,工作职责上有一些变更,导致主业更忙了,就很难分出心思来写博客了。

录取通知书

不过经过最近的一些主动调整,重新梳理了日常工作作息,偶尔也能抽出一丢丢时间,静下心来继续写一些东西,沉淀和总结,也是促进成长重要的一环。Anyway,胡汉三我又杀回来啦!

插件安全防护

今天这篇文章给大家分享插件的安全防护相关的话题,因为在群里头,小伙伴们经常会聊到这个话题,上次因为这个话题还吵了起来……正好我自己在前些年针对这块也做了一些工作,在这里总结出来分享给大家。这篇文章不会具体贴代码加密token 验证等详细的实现代码,更多的是从思路、机制和各种方案的优劣来给大家提供思路,本身软件安全就是一个很大的课题,方法和路径不止一种,更多的是挑选合适自己的方案。

当我们呕心沥血终于把自己的插件开发出来,准备开始大卖,靠它发家致富从此走上人生巅峰的时候,突然想到:

  • 我这个插件这么牛逼,万一被别人抄去代码复刻了一个怎么办?
  • 我的插件卖这么好,要是有人给破解了挂淘宝上,一个卖我一半价,可怎么办?
  • 购买的用户把自己的账号分享给别人用户怎么办?
  • ……

你会发现,把代码写完打包发布,事情并没有结束。那我们如何来面对这种问题呢?

虽然上面这些问题,看起来好像都是插件防破解有关的,但是我们还是可以拆分到两个维度来分析:

  1. 代码安全
  2. 授权安全

第一个维度代码安全的目标是防止自己的核心代码、算法、策略被他人知道,从而造成对自己负面的影响,它的目标对象更多的是同行,比如程序员,黑客,盗版破解者等等。第二个维度授权安全更多指的是软件售卖过程中的安全,诸如防止用户将自己的账号给他人使用,或者利用软件的漏洞来绕过收费等等,它会更多面向用户,当然也会一定程度面向盗版破解者。

虽然最终你的方案都可以适用来解决这两个问题,但是从分析和方案角度,两个维度还是有一些差异的,我们现在从这两个维度分别来展开分析:

1. 代码安全

在群里头,大家有讨论过各种方案,比如:

  • 代码混淆
  • 将代码放到服务器
  • 用 exe 将安装包加密起来

等等各种骚操作都有……我想说的是,这些方法都有效,但是都无法完全解决问题,这是因为这个问题还无法解决的

这个问题本质上无法解决,是由CEP插件的结构决定的:

  1. CEP 插件是由 html,css,javascript,extendscript 组成的,这些都是脚本语言,它不需要进行编译,你写的时候是什么样,最后执行的时候,大体也是什么样。
  2. 插件的安装位置是固定的,这直接就给破解者指明了入口,无论你的插件怎么个变换多样,你总是需要有一个入口在 extensions 目录下,对方只要顺藤摸瓜就可以了。
  3. 执行环境是透明的,html/js 都运行在浏览器环境,jsx 运行在 PS 的脚本环境,他们都能够很容被侵入,很容易拿到你运行的代码,所以,无论你怎么加密,只要对方能够拿到执行环境,就总是能够拿到你的代码。

对于任何一款插件,你只要在它的插件文件夹中放进去一个 .debug 文件,就可以打开 chrome 开发者工具对它进行调试了

debug file

devtoos

目前市面上很多插件,都会把代码放到服务器上,然后通过动态下载放到本地的某个目录下,插件主目录下其实只是一个壳子,以防止核心代码泄露的目的,然而其实你只要打开 devtools,所有的文件在哪里都一目了然。

file located

针对 JSX 而言,很多小伙伴都知道它可以进行加密成jsxbin文件,在早期它确实挺安全的,直到有大佬将 jsxbin 的加密算法破译了出来,于是网上也有了 jsxbin2jsx 的工具,秒秒钟就将 jsxbin 文件反解出来了。

jsx bin file

jsxbin converter

还有,你可能会说:那我的代码不存在本地,我就存在服务器,然后通过请求获取后放到内存里头执行,这样是不是就安全了呢?

这也是一些插件采用的方案,然而其实也没有什么卵用,通过 devtools 可以抓取你插件的所有请求,你的代码在网络传输的过程中仍然可以被获取和解密,我在很早期的时候,尝试过将整个插件都放到服务器,再壳里头嵌入一个 iframe 直接访问服务器的远程地址,就好比一个web页面,这样所有的代码都在服务器(当时这么做并不是为了安全,而是为了插件更新),然后就算是这样,你的 jsx 文件依然得从服务器拉取到本地加载到内存中然后进行 evalScript 执行。所以理论上,只要重写了 csInterface 的evalScript 方法,就可以将你的代码拿到手了。

那说了这么多,我们真的没有办法保护自己的代码了么?答案是:真没有。 那我做的那加密混淆的工作都白做了么?那到也不是。 大家需要记住一点的是,我们做的所有的软件安全工作目的都是为了让破解的成本更高,但并不能阻挡破解本身,破解永远都是存在的,只是时间和成本的问题,所以你增加的安全等级都会带来破解者的破解成本,当这个成本高于它破解带来的收益时,就没有人来破解你的产品了。

所以,作为软件安全的一部分,我们理论上应该尽量提升软件的安全性,让破解的成本越高越好,但是,提升软件安全本身也是有成本的,你将代码混淆之后,问题定位就会变得困难;你把安装包做加密,可能会导致安装失败率提升;将代码都放到服务器上,对于网络不好的用户就没法正常使用……总之,你需要为了更高的插件安全而牺牲一些其它的特性,所以需要权衡。

那我在这里,还可以给大家提供一些常见和不常见的代码安全保护措施:

  1. JS代码混淆。这属于基本操作了,能够有效的防止用户轻易读懂你的代码,并且它的成本很低,通常你都可以将这个过程加入到打包的脚本当中。这里需要提醒的是,JS 的混淆方式有很多种,有一些混淆比较初级,用简单的 jsbeautify 就能很容易的恢复出原始状态,而有一些混淆会做的比较好,将你的代码打碎,就算反解了,阅读起来也会比较困难。

js 代码混淆

  1. jsx 文件一定要转jsxbin,这个也属于基本操作,它和 js 混淆一样,也可以结合到打包过程当中,成本比较低。
  2. 动态下发代码,虽然我在上面说了这个方法并不能防止破解,但是它依然是有正向价值的,我个人认为它最大的优势不在于安全级别有多高,而是它能够更快的进行更新修复。只不过这个方案对你的插件整体架构的变动是很大的,同时也引入了更多的环节,会带来质量的下降。所以如果从一开始做,可以考虑在架构上就把这个方案考虑进去,而不是在最后改。
  3. 既然 Js 容易被破解,那我能不能用不JS?答案是可以的,Ps 支持 C++的插件扩展,你完全可以把自己的核心代码用 C++来实现,然后通过 CEP 面板去调用。还有 CEP 支持 nodejs,所以你可以将你的 js 代码,通过 ffi 的方式编译成.node 文件,这样也是可以的在面板中加载执行的。 我之前帮一个小伙伴定位一段代码的时候,查了个底朝天,硬是没找到对应代码,经过一番探查之后,发现它将核心的 jsx 代码放到了 C++扩展当中了,我当时也是大为一惊,直呼社会社会!这个方案可以比较有效的保护你的代码,毕竟反编译 C++还是难度很高的,但是同样,对于你的开发要求也变的很高,大家可以尝试一下。

c++ plugin

hidden code

2. 授权安全

聊完了代码安全,我们来聊一下授权安全,这种通常出现在我的插件要卖钱的时候出现,一个客户给我 100 块钱,我给你它一份软件,那他把软件分享给别人怎么办?我们在这里不讨论别人把我们的代码破解了从而绕过你的授权这种情况,这属于上面讨论的点了。我们主要讨论面向用户进行授权的安全,防止用户对我们的售卖规则进行破坏。我们来讨论一下常见的方法:

1. 一机一码

这是做传统软件比较常用的方法,我们在交付给用户的时候,通过在安装包中加入一些特定的信息,这些信息和用户的设备信息绑定,以阻止用户将安装程序分发到其它的电脑当中。 然而这种方法对 CEP 插件其实效用有限,安装包只能在安装的过程进行限制,安装完成之后,用户依然可以拷贝插件目录分发给他人。而且这种方式,在交付上也效率很低,需要和用户进行沟通,手动交付程序,运营成本比较高。它更多适合传统的桌面软件的场景。

2. 密钥/激活码

这种方式和第一种不太一样的地方在于,它不在安装包上做文章,而是用户安装完成之后,使用的时候需要填入我们提供的激活码进行激活。这种激活码的方式,核心理念和一机一码的思路是一样的,插件先获取用户设备的信息,将设备信息用特定算法做加密生成一个串密钥,用户将这个密钥传给我们,我们通过这串密钥用相对的算法重新生成激活码,将激活码给用户,用户填入激活码,通过算法映射关系验证成功,激活成功。

激活码激活过程

上面这两种方式,都通过获取用户设备信息,并在此基础上做加密/激活而成,从而实现了对用户终端设备绑定的目的,标识用户设备的信息有许多种,通常我们可以获取用户的mac 地址,系统用户名,甚至是一些硬件参数等信息,这些信息你可以通过 nodejs 的 os 模块去获取。里头需要注意的是,1. 它不一定准/唯一,比如用户如果安装了虚拟机或者多网卡的时候,就会存在多个 mac 地址; 2. 是有可能取不到,有时候是系统原因,权限问题等,你会获取不到用户的设备信息,那你需要有备选方案。

以上这两种方法,都需要我们和用户之间进行激活码的交付,效率不高。

3. 账号登录

上面两种方法都可以说是离线方案,效率比较低,安全性也不高,而且有一个缺点就是和设备绑定,当用户有多个设备的时候,就很麻烦。当我们采用账号登录之后,就理论上不和设备绑定了,只和账号绑定,我们的授权都通过账号进行关联,可以完全做到在线化,大幅提升交付效率。我的 Cutterman 系列插件都采用了这个方法:

但是这种方法并没有解决设备绑定的问题,用户仍然可以将自己的账号和密码共享给他人使用,甚至倒卖,那该怎么办呢?有两种方法:

  1. 单点登录,也就是账号互踢,当这个账号在另外一个设备上登录的时候,之前的登录状态会被踢出来。这也是市面上常见的互联网产品采用的机制,它能够在一定程度上改善账号共享的问题,但是缺点也是如果用户要求支持多个设备同时登录的化,就不能用了。
  2. 做设备记录,当用户要求一个账号可以同时登录几个设备的时候,你就得在服务器做设备记录,当超过之后,禁止登录这样的策略。

4. 微信/手机验证码登录

传统的账号/密码的方式,分享的成本比较低,那如果我们加入了微信扫码登录,或者手机号验证码登录,则用户的账号分享成本就更高了,再加上第三点提到的账号互 T 策略,基本上就很难被用户钻空子了。

3,4 两种方法,需要你具备服务端的能力,支持用户的账号存储,设备管理等功能,对你的开发要求会更高一些,但是这种方案是比较推荐的,灵活性和扩展性都非常的好。

总结

这篇文章给大家介绍了插件安全方面的一些问题和思路,希望能够对大家的产品交付起到一些帮助。还是那句话,软件安全是一个很大的话题,你很有可能因为代码写的有 bug,从而带来漏洞给用户发现了,造成销售上损失,这些点都不在你的预期内,所以除了上面我提到的一些前置性的操作手段之外,你还需要一些后置的监控和定期摸排,比如通过日志发现了一些账号本应该过期了,但是还在正常使用,那你就需要特别关注了。

另外,继续强调那个观念:软件的安全投入是必要的,但不能无止境的追求,因为安全投入是需要成本的,你需要在自己能够承受的范围内,最大化提升自己的软件安全。

最后,上面也只是我自己的思考和经历,未必就是全面的,如果你还有一些自己的奇思妙想,也欢迎在评论中分享出来给大家。

我们下一期见。

【CEP 专题】 插件的安全防护

https://uiscripting.com/2023/11/02/plugin-safety/

作者

小强

发布于

2023-11-02

更新于

2023-11-04

许可协议

评论