部署深度学习模型时的全流程加密方案探索

本文用于探索深度学习模型在部署全流程过程中的整体方案

部署场景

涉及程序端及编程语言:界面端(C#)、服务端(C#)、训练端(Python)

剥离加密之后的流程

  1. 界面端根据训练配置调用训练端
  2. 训练端训练结束后保存模型(结构、文件)
  3. 服务端加载模型

加密要求

由C#编写的程序部署时会将其编译为二进制,无需加密保护,主要是针对训练端的Python及训练得到的模型,有以下要求:

  1. 无法明文看到Python代码
  2. 无法获得模型(结构与权重)
  3. windows上部署
  4. 加密方案不能大幅度增加部署成本

加密方案

针对Python加密以及模型的加密,调查了主流的加密方案

网络收集的Python加密思路

序号工具方法描述加密及解密优缺点
1Nuitka[1][2].py 文件先被转成了 .c 文件,然后被编译成 .o 文件,最后合并成 .bin 可执行文件,从 bin 到 C 是不可逆的,从 C 到 Python 也是不可逆的,因此代码是安全的编译为bin,或者编译为动态链接库.so 文件工作量小,安全性高,使用加密之后的Python便捷;编译时间长,过程复杂
2发行.pyc文件[3]通过compileall模块将.py文件转为.pyc文件,该文件是二进制,无法直接看源代码,而python解释器可以直接执行.pyc文件台兼容性好,.py 能在哪里运行,.pyc 就能在哪里运行;解释器兼容性差,.pyc 只能在特定版本的解释器上运行。有现成的反编译工具,破解成本低
3代码混淆(oxyry,pyobfuscate)[3:1]让人看不懂代码,移除注释和文档,改变缩进,在tokens中间加入一定空格,重命名函数、类、变量,在空白行插入无效代码提高了一点源码破解门槛。兼容性好,只要源码逻辑能做到兼容,混淆代码亦能;只能对单个文件混淆,无法做到多个互相有联系的源码文件的联动混淆
4py2exe[3:2]将源码编译为 .pyc 文件,加之必要的依赖文件,一起打包成一个可执行文件。最终 py2exe 打包出的是二进制文件。直接打包成 exe,方便分发和执行。破解门槛比 .pyc 更高一些;兼容性差,只能运行在 Windows 系统上。生成的可执行文件内的布局是明确、公开的,可以找到源码对应的 .pyc 文件,进而反编译出源码。
5Cython[3:3][4]将 .py/.pyx 编译为 .c 文件,再将 .c 文件编译为 .so(Unix) 或 .pyd(Windows)生成的二进制 .so 或 .pyd 文件难以破解。同时带来了性能提升;兼容性稍差,对于不同版本的操作系统,可能需要重新编译。虽然支持大多数 Python 代码,但如果一旦发现部分代码不支持,完善成本较高。
6Pyinstaller[5][6]打包为exe文件,将Python文件转换为exe文件,以及dist文件夹和build文件夹,如果要移植到其他电脑上运行,也是只需要将这两个文件夹复制到对方电脑上,即使对方没有python环境,也可以运行程序,具有较好的兼容性;pyinstxtractor.py可以进行反编译

注:py是源文件,pyc是源文件编译后的文件,pyo是源文件优化编译后的文件,pyd是其他语言写的python库[7]

网络收集的模型加密思路

序号方法描述加密及解密优缺点
1将模型转换为二进制,直接打开看不见原始内容[8]ncnn2mem工具可以将ncnn模型转为二进制的:ncnn2mem resnet.param resnet.bin使用netron可以查看文件,反编译成本很低
2将模型打包为C code,并嵌入到程序中[8:1]ncnn2mem resnet.param resnet.id.h resnet.mem.h,把这个文件 include 进来,用内存加载接口,把模型当作代码直接嵌入编译进程序中分发exe即可,虽然不能直接获得模型,但是能用 objdump 或者十六进制编辑器从 exe 静态区中把模型抠出来
3使用专用加密库对模型加密[8:2]用 openssl,把 param.bin 和 bin 两个文件用 AES 加密成 param.bin.enc 和 bin.enc;程序实现以下三步,加载加密模型:读enc文件、解密到内存、从内存加载模型可以从算法中xor pattern或获得密钥;堆内存上暴力查找 enc 大小左右的连续内存和关键字,把模型从内存里抠出来
4自定义加密算法和数据读取[8:3]用普通 xor 混淆实现任意时刻内存中都不会存在完整的模型内容,边解密边加载
5给模型加些自定义 op[8:4]cnn 可以自定义 op,可以运行时注册自定义 op,可以直接改 param即便看到了明文的 param,也容易被名字欺骗
6将外部文件嵌入二进制文件(exe,dll),并加壳保护该文件[9]直接程序之间调用这种方法开发量小,仅需要将资源文件嵌入并在运行时加载。
7自定义的外部文件加密方式[9:1]在加载模型文件前解密,考虑到安全性,防止解密后的模型文件暴露于内存被轻易dump,考虑使用流式加密的方法进行加解密,由此相对安全一点。
8用protobuf自定义一种格式呀,没有协议文件[10]工程量比较大
9部署到云端给客户api接口调用[10:1]特定场景不适合

参考资料:


  1. 如何防止商用的深度学习模型源码泄露? ↩︎

  2. Nuitka-Python打包exe - 知乎 ↩︎

  3. 现有Python 代码加密方案 - 运维之路 ↩︎ ↩︎ ↩︎ ↩︎

  4. 用Cython加密打包python项目 ↩︎

  5. 利用pyinstaller打包加密Python项目 ↩︎

  6. Pyinstaller 打包加密python项目 ↩︎

  7. python py、pyc、pyo、pyd文件区别 ↩︎

  8. 如何加密ncnn模型 - 知乎 ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  9. 浅谈深度学习模型如何保护–AES加密文件流的实现(带源码) ↩︎ ↩︎

  10. 如何防止商用的深度学习模型源码泄露? - 知乎 ↩︎ ↩︎