最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 建议收藏,使用WebAssembly保护前端JS核心代码实战

    正文概述 掘金(大帅_全能老猿)   2021-01-11   506

    前言

    上个月我花了2天开发了一个全新的VSCode插件叫“我爱掘金”,让所有掘友可以化身为小蝌蚪,在VSCode里实时聊天。使用的是一个开源项目 workerman-todpole ,在原项目的基础上我做了大量的修改和优化。也曾试图添加一些房管功能,比如只有管理员可以使用大红色,只有管理员可以发光等等。

    建议收藏,使用WebAssembly保护前端JS核心代码实战

    但正义的掘友们很快就开始尝试各种能打破这种【不平等】待遇的方法,其中 @那猫小帅 同学还开发了各种 池塘机器人,可以在池塘里扮演给新手用户提供帮助的NPC。现在小蝌蚪大眼睛的创意也是那猫小帅同学提出的,简直棒棒哒?。

    需求

    要实现房管功能,我需要实现一个方法,来生成和激活高级命令,激活后则可以获得高级权限。显然,我不想被别人看到这段代码里我是如何生成命令和校验命令的。

    我也不想放到服务器上做成一个后端接口,因为每次都去请求接口会对服务器产生压力。而websocket每次收到消息时都需要去验证这个高级命令

    所以,我确定了两个核心诉求

    • 高级命令的生成和激活直接在前端完成,避免请求服务器。
    • 不能让人看到生成和激活的核心逻辑,否则就失去了意义。

    一番调研之后,发现WebAssembly可以满足我的这两点需求,接下来就是实战了。

    WebAssembly介绍

    运行仍然是在浏览器里,但使用一种二进制格式的.wasm文件 ,这完全能满足我的需求,并且主流浏览器对WebAssembly的兼容性很好。

    比如下面这段代码,大家可以复制粘贴到浏览器的调试窗口中感受一下

    WebAssembly.compile(new Uint8Array(`
      00 61 73 6d  01 00 00 00  01 0c 02 60  02 7f 7f 01
      7f 60 01 7f  01 7f 03 03  02 00 01 07  10 02 03 61
      64 64 00 00  06 73 71 75  61 72 65 00  01 0a 13 02
      08 00 20 00  20 01 6a 0f  0b 08 00 20  00 20 00 6c
      0f 0b`.trim().split(/[\s\r\n]+/g).map(str => parseInt(str, 16))
    )).then(module => {
      const instance = new WebAssembly.Instance(module)
      const { add, square } = instance.exports
    
      console.log('2 + 4 =', add(2, 4))
      console.log('3^2 =', square(3))
      console.log('(2 + 5)^2 =', square(add(2 + 5)))
    
    })
    

    建议收藏,使用WebAssembly保护前端JS核心代码实战

    显然,你无法轻易的得知那段Uint8Array里的算法是具体如何实现的...

    但是要编写代码,并编译成.wasm文件可不算太简单,比如在本案中,我选择C++来编写代码,并通过emscripten工具链编译成.wasm文件。

    emscripten

    建议收藏,使用WebAssembly保护前端JS核心代码实战

    安装 emscripten 环境

    # Get the emsdk repo
    git clone https://github.com/emscripten-core/emsdk.git
    
    # Enter that directory
    cd emsdk
    
    # Fetch the latest version of the emsdk (not needed the first time you clone)
    git pull
    
    # Download and install the latest SDK tools.
    ./emsdk install latest
    
    # Make the "latest" SDK "active" for the current user. (writes .emscripten file)
    ./emsdk activate latest
    
    # Activate PATH and other environment variables in the current terminal
    source ./emsdk_env.sh
    

    WebAssembly官方文档

    环境安装好之后,我们立刻来写一段C语言,并编译成.wasm让它运行在浏览器里吧。

    #include <stdio.h>
    
    int main(int argc, char ** argv) {
        printf("掘友们好");
        printf("\n");
        printf("这是一段来自C语言的问候");
        printf("\n");
    }
    

    编译命令

    emcc helloworld.c -s WASM=1 -o helloworld.html
    

    编译完成后,记得起一个服务,比如live-server,因为我们需要读取本地的.wasm文件。这样我们就可以在浏览器里看到这段来自C语言的问候了

    建议收藏,使用WebAssembly保护前端JS核心代码实战

    但是坦白说,我并不认为WebAssembly可以减小代码量,比如这段简单的代码编译成.wasm后竟然需要12KB

    建议收藏,使用WebAssembly保护前端JS核心代码实战

    实战代码保护

    比如原本我需要的JS逻辑如下,每天根据零点的时间戳转36进制得到一个6位长度的字符串(这个长度的输入体验还算友好),用户输入这个命令后即可激活高级功能。

    蝌蚪池塘里的算法已变更,不要天真的以为我会爆料自己的算法哦:P

    JS的写法

    function verifyCommand(cmd){
    	var t_cmd = (new Date(new Date().setHours(0, 0, 0, 0)).getTime()/1000).toString(36);
    	if(cmd == t_cmd)return 1;
    	else return 0;
    }
    

    但JS在浏览器中是完全暴露的,在浏览器调试模式下,非常容易就可以知晓高级命令是如何生成的。

    当然如文章开头所说的,我们也可以放到后端接口中去校验

    php的写法

    function MyEncode($var,$targetBit){    
        $dic = array(
                0 => '0', 1 => '1', 2 => '2', 3 => '3', 4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9',
                10 => 'A', 11 => 'B', 12 => 'C', 13 => 'D', 14 => 'E', 15 => 'F', 16 => 'G', 17 => 'H', 18 => 'I',
                19 => 'J', 20 => 'K', 21 => 'L', 22 => 'M', 23 => 'N', 24 => 'O', 25 => 'P', 26 => 'Q', 27 => 'R',
                28 => 'S', 29 => 'T', 30 => 'U', 31 => 'V', 32 => 'W', 33 => 'X', 34 => 'Y', 35 => 'Z'
        );
        $yushu=bcmod($var,$targetBit);
        $shang=floor(bcdiv($var, $targetBit));
    
        if($shang==0){
            return  $dic[$yushu];
        }
        else {
            return MyEncode($shang,$targetBit).$dic[$yushu];
        }
    }
    
    date_default_timezone_set('PRC');
    $time =strtotime(date("Y-m-d 00:00:00"));
    $v_cmd = MyEncode(intval($time),36);
    
    $cmd = strtoupper($_GET["cmd"]);
    
    if($cmd==$v_cmd){
        echo 1;
    }else{
        echo 0;
    }
    

    但是利用服务端的话,那每次校验都必须请求接口,无疑会对服务器造成压力。

    现在我们用C语言来写一下,并最终编译为WebAssembly的.wasm文件

    //获得今天零点的时间戳,秒
    unsigned int getTodayZeroTime() 
    {  
    	//太长了,函数的实现请看源码
    }  
    
    //将数字转为指定进制,decimal为进制值
    void f( long int x, char *p ,int decimal)
    {
    	//太长了,函数的实现请看源码
    }
    
    int verifyCommand(std::string cmd) {
      int t_time = getTodayZeroTime();
      char t_cmd[MAXN]="";
      f(t_time,t_cmd,36);
    
      if(strcmp(t_cmd, cmd.data()) == 0){
        return 1;
      }else{
        return 0;
      }
     }
    

    JS调用.wasm里的C方法并传参

    要想在JS中调用WebAssembly里的函数,我们在函数外面用一个extern "C"来包裹

    extern "C" {
      int verifyCommand(std::string cmd) {
        int t_time = getTodayZeroTime();
        char t_cmd[MAXN]="";
        f(t_time,t_cmd,36);
    
        if(strcmp(t_cmd, cmd.data()) == 0){
          return 1;
        }else{
          return 0;
        }
      }
    }
    

    在编译.wasm时添加命令

    emcc verify.cc -o verify.html -s EXPORTED_FUNCTIONS="['_verifyCommand']" -s EXPORTED_RUNTIME_METHODS='["ccall"]'
    

    重点是这两个参数,注意下划线和引号

    • -s EXPORTED_FUNCTIONS="['_verifyCommand']"
    • -s EXPORTED_RUNTIME_METHODS='["ccall"]'

    编译完成后我们就可以使用JS来调用verifyCommand这个C语言里的函数了

    //第一个参数,函数名称
    //第二个参数,函数返回值类型
    //第三个参数,函数受参类型数组
    //第四个参数,传递的参数数组
    Module.ccall("verifyCommand",'number',['string'],['qmocg0']);
    

    以下是调试窗口截图,我特意输对和输错来看了看返回值 建议收藏,使用WebAssembly保护前端JS核心代码实战

    现在我们就完成了这个高级命令的校验函数,直接在前端同步完成校验,无须请求后台接口异步获得校验结果。因为核心代码都在二进制的.wasm文件中,有心人也无法【轻松】的看到函数内的逻辑是如何实现的。为什么重点标记了一下【轻松】二字呢?

    因为能编译,就能反编译...

    本文就到这里把,各位小蝌蚪们池塘见,记得点赞收藏,早晚你会用到的!

    源码下载

    微信搜索 “ezfullstack” ,关注并回复 “webassembly” 即可获得源码下载链接

    关注大帅,一个全能的老程序猿

    建议收藏,使用WebAssembly保护前端JS核心代码实战


    起源地下载网 » 建议收藏,使用WebAssembly保护前端JS核心代码实战

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元