部署深度学习模型时的全流程加密方案探索
本文用于探索深度学习模型在部署全流程过程中的整体方案
部署场景
涉及程序端及编程语言:界面端(C#)、服务端(C#)、训练端(Python)
剥离加密之后的流程:
- 界面端根据训练配置调用训练端
- 训练端训练结束后保存模型(结构、文件)
- 服务端加载模型
加密要求
由C#编写的程序部署时会将其编译为二进制,无需加密保护,主要是针对训练端的Python及训练得到的模型,有以下要求:
- 无法明文看到Python代码
- 无法获得模型(结构与权重)
- windows上部署
- 加密方案不能大幅度增加部署成本
加密方案
针对Python加密以及模型的加密,调查了主流的加密方案
网络收集的Python加密思路
序号 | 工具 | 方法描述 | 加密及解密 | 优缺点 |
---|---|---|---|---|
1 | Nuitka[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中间加入一定空格,重命名函数、类、变量,在空白行插入无效代码 | 提高了一点源码破解门槛。兼容性好,只要源码逻辑能做到兼容,混淆代码亦能;只能对单个文件混淆,无法做到多个互相有联系的源码文件的联动混淆 | |
4 | py2exe[3:2] | 将源码编译为 .pyc 文件,加之必要的依赖文件,一起打包成一个可执行文件。最终 py2exe 打包出的是二进制文件。 | 直接打包成 exe,方便分发和执行。破解门槛比 .pyc 更高一些;兼容性差,只能运行在 Windows 系统上。生成的可执行文件内的布局是明确、公开的,可以找到源码对应的 .pyc 文件,进而反编译出源码。 | |
5 | Cython[3:3][4] | 将 .py/.pyx 编译为 .c 文件,再将 .c 文件编译为 .so(Unix) 或 .pyd(Windows) | 生成的二进制 .so 或 .pyd 文件难以破解。同时带来了性能提升;兼容性稍差,对于不同版本的操作系统,可能需要重新编译。虽然支持大多数 Python 代码,但如果一旦发现部分代码不支持,完善成本较高。 | |
6 | Pyinstaller[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] | 特定场景不适合 |
参考资料: