How to protect your JS code by WebAssembly

2021-01-22 22:25:51

参考地址  How to protect your JS code by WebAssembly

基本概念

一、代码安全

Ref: 前端核心代码保护技术面面观

 对于iOS或是Android来说,我们可以将相关的算法通过C/C++进行编写,然后编译为dylib或是so并进行混淆以此来增加破解的复杂度,但是对于前端来说,并没有类似的技术可以使用。当然,自从asm.js及WebAssembly的全面推进后,我们可以使用其进一步增强我们核心代码的安全性,但由于asm.js以及WebAssembly标准的开放,其安全强度也并非想象中的那么美好。

 

  • 使用Javascript的混淆器

对于Javascript的混淆器我们是不陌生的,我们常常使用其进行代码的压缩以及混淆以此来减少代码体积并增加人为阅读代码的复杂度。

 

  • 使用Flash的C/C++扩展方式

在Flash还大行其道的时期,为了更好的方便引擎开发者使用C/C++来提升Flash游戏相关引擎的性能,Adobe开源了 CrossBridge 这个技术。在这种过程中,原有的C/C++代码经过LLVM IR变为Flash运行时所需要的目标代码,不管是从效率提升上还是从安全性上都有了非常大的提升。对于目前的开源的反编译器来说,很难反编译由CorssBridge编译的C/C++代码,并且由于Flash运行时生产环境中禁用调试,因此也很难进行对应的单步调试。 

 

  • 使用asm.js或WebAssembly

目前看起来WebAssembly是目前最理想的前端核心代码保护的方案了,我们可以使用C/C++编写相关的代码,使用Emscripten相关工具链编译为asm.js和wasm,根据不同的浏览器的支持情况选择使用asm.js还是wasm。并且对于PC端IE10以下的浏览器,我们还可以通过CrossBridge复用其C/C++代码,产出对应的Flash目标代码,从而达到非常好的浏览器兼容性。

然而使用asm.js/wasm后对于前端核心代码的保护就可以高枕无忧了么?

由于asm.js以及wasm的标准规范都是完全公开的,因此对于asm.js/wasm标准实现良好反编译器来说,完全可以尽可能的产出阅读性较强的代码从而分析出其中的核心算法代码。但幸运的是,目前作者还暂时没有找到实现良好的asm.js/wasm反编译器,因此我暂时认为使用此种方法在保护前端核心代码的安全性上已经可堪重用了。

复制代码

#include <string>#include <emscripten.h>#include <emscripten/bind.h>#include "md5.h"#define SALTKEY "md5 salt key"std::string sign(std::string str){    return md5(str + string(SALTKEY));
}// 此处导出sign方法供Javascript外部环境使用EMSCRIPTEN_BIND(my_module){
    emscripten::function("sign", &sign);
}

复制代码

Compile and export js.

em++ -std=c++11 -Oz --bind \    -I ./md5 ./md5/md5.cpp ./sign.cpp \    -o ./sign.js

复制代码

<body>
    <script src="./sign.js"></script>
    <script>        // output: 0b57e921e8f28593d1c8290abed09ab2
        Module.sign("This is a test string");    </script>
</body>

复制代码

 

  • SecurityWorker - 更好的思路及其实现

/* 作者自己的策略,详见原链接 */

 

Ref: 发现比起混淆, emscripten 或 webassenbly 才是保护代码的好办法

开发者在遇到较大代码量时,可以考虑上 emscripten 或者 webassenbly 来保护自己的代码。
虽说前端没有破不了的防护,但这两样东西真的是靠很简单的方法就能达到相当好的保护目的能。而且尤其是当你代码混淆+这些之后,对攻击者来说,那酸爽,简直了。

 

二、概念理解

Ref: Javascript支持3D游戏,asm.js 和 Emscripten工具,以及和WebAssembly的区别

2012年,Mozilla 的工程师 Alon Zakai 在研究 LLVM 编译器时突发奇想:许多 3D 游戏都是用 C / C++ 语言写的,如果能将 C / C++ 语言编译成 JavaScript 代码,它们不就能在浏览器里运行了吗?众所周知,JavaScript 的基本语法与 C 语言高度相似。为此专门做了一个编译器项目 Emscripten。这个编译器可以将 C / C++ 代码编译成 JS 代码,但不是普通的 JS,而是一种叫做 asm.js 的 JavaScript 变体

 

  • asm.js 与 WebAssembly 的异同

还有一种叫做 WebAssembly 的技术。两者的功能基本一致,就是转出来的代码不一样:asm.js 是文本,WebAssembly 是二进制字节码,因此运行速度更快、体积更小。

但是,这并不意味着 asm.js 肯定会被淘汰,因为它有两个优点:首先,它是文本,人类可读,比较直观;其次,所有浏览器都支持 asm.js,不会有兼容性问题。 

 

  • 编译执行流程

Emscripten 的底层是 LLVM 编译器,理论上任何可以生成 LLVM IR(Intermediate Representation)的语言,都可以编译生成 asm.js。 但是实际上,Emscripten 几乎只用于将 C / C++ 代码编译生成 asm.js。

C/C++ ⇒ LLVM ==> LLVM IR ⇒ Emscripten ⇒ asm.js

 

三、新书资源

我的新书《深入浅出WebAssembly》出版啦(。・ω・。)ノ [有点意思]

  • 未来技术变革

https://myslide.cn/slides/21201#

mxnet也是一个不错的选择。

 

 

安装配置

一、Developer’s Guide

https://webassembly.org/getting-started/developers-guide/

https://www.wasm.com.cn/getting-started/developers-guide/

 

二、安装

想要编译成WebAssembly,你首先需要先编译 LLVM。

 

  • 安装LLVM

Ref: LLVM概述——介绍与安装

Goto: https://releases.llvm.org/

选择本地编译安装。

 

  • 安装emcc

How to install the latest emscripten on Ubuntu using command line?

复制代码

From the emscripten/. Make the  SDK //

复制代码

 

这里报错如下,但先不管,上述步骤后,已经可以使用emcc命令。

$ ./emsdk install sdk-incoming-64bit binaryen-master-64bit
Error: No tool or SDK found by name 'sdk-incoming-64bit'.

  

三、测试环境

  • C语言

下面这些命令可能让你创建一个简单的“hello word”程序,并且编译它。

复制代码

$ mkdir hello
$ cd hello
$ echo '#include <stdio.h>' > hello.c
$ echo 'int main(int argc, char ** argv) {' >> hello.c
$ echo 'printf("Hello, world!\n");' >> hello.c
$ echo '}' >> hello.c
$ emcc hello.c -s WASM=1 -o hello.html

复制代码

我们可以使用 emrun 命令来创建一个 http 协议的 web server 来展示我们编译后的文件。

$ emrun --no_browser --port 8080 .

HTTP 服务开启后,您可以在浏览器中打开。如果你看到了“Hello,word!”输出到了 Emscripten 的 控制面板,恭喜你!你的 WebAssembly 程序编译成功了!

 

  • C++语言

要先执行自动环境变量配置,或者直接手动在 ~/.bashrc 中设置也可。

复制代码

$ emsdk_env.sh --build=Release
Adding directories to PATH:
PATH += /usr/local/emsdk/upstream/emscripten
PATH += /usr/local/emsdk/node/12.9.1_64bit/bin

Setting environment variables:
EMSDK = /usr/local/emsdk
EM_CONFIG = /home/jeffrey/.emscripten
EM_CACHE = /usr/local/emsdk/upstream/emscripten/cache
EMSDK_NODE = /usr/local/emsdk/node/12.9.1_64bit/bin/node

复制代码

步骤与上一条的C语言操作类似。

em++ hello.cpp -s WASM=1 -o hello.html

$ ls
hello.cpp hello.html hello.js hello.wasm

emrun --no_browser --port 8080 .

 

 

原理剖析

一、基本JS解析步骤

Ref: 几张图让你看懂WebAssembly 

Parsing讲源码转换成解释器可以运行的东西所用的事情。
Compiling + optimizing花费在基础编译和优化编译上的时间。有一些优化编译的工作不在主线程,所以这里并不包括这些时间。
Re-optimizing当预先编译优化的代码不能被优化的情况下,JIT 将这些代码重新优化,如果不能重新优化那么久丢给基础编译去做。这个过程叫做重新优化。
Execution执行代码的过程
Garbage collection清理内存的时间

 

一个重要的事情要注意:这些任务不会发生在离散块或特定的序列中。相反,它们将被交叉执行。

比如正在做一些代码解析时,还执行者一些其他的逻辑,有些代码编译完成后,引擎又做了一些解析,然后又执行了一些逻辑,等等。

 

WebAssembly 和别的汇编语言是有一些不同的。所以他是一个概念机上的机器语言,不是在一个真正存在的物理机上运行的机器语言。

正因如此,WebAssembly 指令有时候被称为虚拟指令。它比 JavaScript 代码更快更直接的转换成机器代码,但它们不直接和特定硬件的特定机器代码对应。

在浏览器下载 WebAssembly后,使 WebAssembly 的迅速转换成目标机器的汇编代码。

 


  • 2020-12-06 16:46:11

    git撤销pull

    刚刚不小心pull了一下,有错误,想撤回怎么办。

  • 2020-12-06 19:05:13

    visual studio 配置 intellij idea快捷键

    我原本从intellij idea转换到visual studio是因为webstorm没办法远程开发,而visual studio有remote wsl,和remote ssh,看着挺不错的样子。

  • 2020-12-06 20:38:30

    intellij idea远程开发remote

    开发时一般的平台都是windows,但windows对开发极其不友好,一般都会在本地开启虚拟机,安装上linux环境进行项目的部署测试。下面介绍一种windows主机与linux虚拟机代码同步的方法。这个工具适用于jerbrains公司旗下的很多产品,比如idea、webstrom、phpstrom等。但是要注意你安装的IDE必须是专业版的,社区版的IDE是没有这个代码同步功能的哦!

  • 2020-12-07 05:46:56

    npm设置和取消代理的方法

    有时候是设置了全局代理对npm并不生效,不如直接给npm设置代理,至少在mac电脑我是有这种感觉的。

  • 2020-12-07 15:04:03

    node开发邮件系统总结

    因为multipart这种形式比较复杂,因此要利用boundary分割符,将邮件体分割成不同段来进行解析,boundary分为父段和子段,父段一般出现0次或1次,出现在末尾,每个子段中也有content-type和boundary,需要在进行解析,如果遇到i,iii里面的情况可直接解析,如果遇到ii中的情况,再按ii中的步骤进行解析

  • 2020-12-07 15:17:45

    email-templates + mjml 发送邮件

    mjml 是一个很不错的响应式邮件html 内容标签库,email-templates 是一个灵活强大的邮件发送框架,两者集成起来我们 可以设计灵活强大的邮件发送系统,以下是一个简单的集成使用,实际使用还有好多地方需要完善

  • 2020-12-07 15:19:00

    响应式邮件的编写插件介绍mjml

    以前做项目碰到发邮件的需求,邮件模板的编辑就是一件头疼的事。因为虽说邮件是支持 HTML 的,但是确是 HTML 子集程度的支持,所以存在必须通过 <table> 排版的恶心之处,还有很多兼容性的坑。本质上是各家邮件商的标准有差异吧。

  • 2020-12-07 16:14:22

    nodejs队列实现amqplib,rabbitmq

    其中StartConsumer 会在项目启动时启动,在整个生命周期中一直保持监听状态,在程序结束时mq的链接关闭。需要注意的是 noAck 这个参数,当为false是表示消息出队后不会自动删除,如果设置成true,则无论消息处理成功与否此消息会被删除。注意到在消息不成功是,调用了ch.nack(msg)),此方法是将消息重新入队。