Git Product home page Git Product logo

securityworker's Introduction

SecurityWorker文档(V1.0.1)

⚠️ SecurityWorker核心已开源

⚠️ SecurityWorker不再维护,你可以选择更好的代替方案 sablejs

SecurityWorker提供完全隐匿且兼容ECMAScript 5.1的类WebWorker的安全可信环境,帮助保护你的核心Javascript代码不被破解。 SecurityWorker不同于普通的Javascript代码混淆,我们使用 独立Javascript VM + 二进制混淆opcode核心执行 的方式防止您的代码被开发者工具调试、代码反向。

0. 特性

  • 完整的ECMAScript 5.1标准兼容性
  • 极小的SecruityWorker VM文件体积(~160kb)
  • 保密性极强,执行逻辑及核心算法完全隐匿不可逆
  • 可选择支持多种环境,Browser/NodeJS/小程序(默认不允许NodeJS黑盒运行)
  • 良好的浏览器兼容性,主流浏览器全覆盖
  • 易于使用,API兼容WebWorker(不允许访问DOM/BOM)
  • 易于调试,被保护代码不做混淆,报错信息准确

1. 兼容性

  • IE11
  • Chrome 24+
  • Safari 6.2+
  • Firefox 16+
  • Edge 15+
  • Android 4.4.2+
  • iOS Safari 8+
  • iOS WebView 9+

2. 快速开始

我们以一个Ping-Pong的示例程序讲解SecurityWorker的基本使用,首先我们创建sw.js用于实现SecurityWorker VM的内部业务逻辑:

// sw.js
onmessage = function(data) {
  if(data === 'ping'){
    console.log('SecurityWorker recv: ' + data);
    postMessage('pong');
  }
};

接着我们在使用bin文件对sw.js进行编译得到保护文件loader.js:

> npm run compile ${youfile}

然后我们创建index.html加载loader.js并完成调用逻辑:

<html>
<head>
    <!--编译sw.js得到保护文件loader.js-->
    <script src="loader.js"></script>
</head>
<body>
    <!--.....-->
    <script>
        // 等待SecurityWorker初始化完成
        SecurityWorker.ready(function() {
          var sw = new SecurityWorker();
          
          // SecurityWorker正常创建,可被使用
          sw.oncreate = function() {
            console.log('ready');
            sw.postMessage('ping');
          };
          
          // 获取SecurityWorker传递的数据
          sw.onmessage = function(data) {
            console.log('MainThread recv: ' + data);
            sw.terminate();
          };
          
          // SecurityWorker销毁事件
          sw.onterminate = function() {
            console.log('terminate');
          };
        });
    </script>
</body>
</html>

最后我们访问index.html即可看到控制台完整打印出如下结果:

> ready
> [LOG] SecurityWorker recv: ping
> MainThread recv: pong
> terminate

恭喜你已经完全掌握SecurityWorker的使用了,进一步查看SecurityWorkerSecurityWorker VM提供的API帮助你更好的运用SecurityWorker。

3. SecurityWorker API

SecurityWorker.runMode

设定SecurityWorker VM执行的环境,有3个选择:

  • SecurityWorker.AUTO_MODE: 自动选择运行于WebWorker还是浏览器主线程中,推荐的默认值
  • SecurityWorker.WORKER_THREAD_MODE: 强制运行于WebWorker环境中,由于某些浏览器的WebWorker环境有一些兼容性问题,因此对于需要兼容性的应用不推荐此选项
  • SecurityWorker.MAIN_THREAD_MODE: 强制运行于浏览器主线程中,兼容性很好,但是对于较老的设备可能会导致页面较长时间无响应

SecurityWorker(void)

SecurityWorker构造函数,用于创建一个SecurityWorker实例。但请注意,实例的创建需要在SecurityWorker.ready调用后进行创建,否则将有几率导致SecurityWorker VM内存分配失败。

SecurityWorker.ready(function(){
  var sw = new SecurityWorker();
  // ......
});

SecurityWorker.ready(Function)

SecurityWorker已经初始化完毕,可以安全的进行SecurityWorker实例的创建。

SecurityWorker.ready(function(){
  // ......
});

SecurityWorker.prototype.postMessage(String|Object)

发送消息给SecurityWorker VM对象。

var sw = new SecurityWorker();
sw.oncreate = function() {
  sw.postMessage("Hello World!");
  sw.postMessage({
    now: Date.now()
  })
}

SecurityWorker.prototype.terminate(void)

销毁SecurityWorker实例和SecurityWorker VM对象,此后将无法进行消息发送。

var sw = new SecurityWorker();
sw.oncreate = function() {
  sw.terminate();
  sw.postMessage(); // error, can't postMessage after terminate
}

SecurityWorkerInstance.oncreate

SecurityWorker实例创建成功的回调,此后可以安全的与SecurityWorker VM进行数据通信。

var sw = new SecurityWorker();
sw.oncreate = function() {
  console.log('ready')
}

SecurityWorkerInstance.onmessage

接收到SecurityWorker VM传递的相关数据的回调。

var sw = new SecurityWorker();
sw.onmessage = function(data) {
  if(typeof data == 'string') {
    console.log(data);
  }else if(typeof data == 'object') {
    console.log(JSON.stringify(data));
  }
}

SecurityWorkerInstance.onterminate

SecurityWorker实例及SecurityWorker VM已经成功销毁的回调,此后将不能再进行postMessage的调用。

var sw = new SecurityWorker();
sw.onterminate = function() {
  sw.postMessage(); // error, can't postMessage after terminate
}

4. SecurityWorker VM API

以下所有API只能在SecurityWorker VM中使用,外部环境的SecurityWorker实例及原型并未提供此类API。

onmessage

获取SecurityWorker实例发送的相关数据

onmessage = function(data) {
  if(typeof data == 'string') {
    console.log(data);
  }else if(typeof data == 'object') {
    console.log(JSON.stringify(data);
  }
}

// or use self

self.onmessage = function() {
  // something to do...
}

postMessage(String|Object)

发送数据给SecurityWorker实例。

postMessage('Hello World!');
postMessage({
  now: Date.now()
});

btoa(String)

对字符串进行Base64编码。

var b64 = btoa('Hello World!');
console.log(b64); // SGVsbG8gV29ybGQh

atob(String)

对用Base64编码过的字符串进行解码。

var str = atob('SGVsbG8gV29ybGQh');
console.log(str); // 'Hello World!'

setTimeout(Function, Number)

延迟执行函数

setTimeout(function(){
  console.log('Hello World!');
}, 1000);

setInterval(Function, Number)

循环执行函数(注意:setInterval内部实现采用setTimeout)

setInterval(function(){
  console.log('Hello World!');
}, 1000);

Console相关函数

SecurityWorker VM支持如下的Console函数:

  • console.log
  • console.info
  • console.debug
  • console.error
  • console.time
  • console.timeEnd

request(Object)

发送Ajax请求,其接收一个对象所含参数为:

  • String uri: 请求地址
  • String method: 请求方法,可使用GET/POST/DELETE/HEAD/PUT,默认GET
  • String body: POST/DELETE/PUT的请求参数,可选
  • Object headers: 附加的HTTP Header信息, 可选
  • Function success: 请求成功的回调,可选
  • Function error: 请求失败的回调,可选
// GET请求
request({
  uri: 'http://www.baidu.com',
  method: 'GET',
  headers: {
    'X-AUTH': 'THIS IS YOUR AUTH KEY'
  },
  success: function(data){
    console.log("status: " + data.status);
    console.log("statusText: " + data.statusText);
    console.log("text: " + data.text);
  },
  error: function(err){
    console.log(err);
  }
});

// POST请求
request({
  uri: 'http://www.baidu.com',
  method: 'POST',
  body: JSON.stringify({id: 1}),
  success: function(data){
    console.log("status: " + data.status);
    console.log("statusText: " + data.statusText);
    console.log("text: " + data.text);
  },
  error: function(err){
    console.log(err);
  }
});

WebSocket

WebSocket(String url [, String protocols])

WebSocket构造函数。

var ws = new WebSocket('wss://www.baidu.com');
WebSocket.prototype.send(String|TypeArray)

向服务器发送字符串或二进制数据。

var ws = new WebSocket('wss://www.baidu.com');
ws.addEventListener('open', function(){
  ws.send('Hello World!');
  ws.send(new Uint8Array([1,2,3]));
});
WebSocket.prototype.close(void)

关闭WebSocket连接。

var ws = new WebSocket('wss://www.baidu.com');
ws.close();
WebSocketInstance.addEventListener(String eventName, Function handler)

支持4种标准事件:

  • open: 连接打开
  • message: 获得服务器发送的数据
  • error: 发生相关错误
  • close: 连接关闭
var ws = new WebSocket('wss://www.baidu.com');
ws.addEventListener('open', function(){
  ws.send('ready');
});

ws.addEventListener('message', function(message){
  if(message === 'close'){
    ws.close();
  }
});

ws.addEventListener('error', function(error){
  console.log(error);
});

ws.addEventListener('close', function(){
  // 不需要进行removeEventListener操作,
  // 当调用close事件后自动解绑所有事件回调
  console.log('ws client open')
});
WebSocketInstance.removeEventListener(String eventName, Function handler)

支持4种标准事件:

  • open: 连接打开
  • message: 获得服务器发送的数据
  • error: 发生相关错误
  • close: 连接关闭
var ws = new WebSocket('wss://www.baidu.com');
ws.addEventListener('open', function(){
  ws.send('ready');
});

function onmessage(message){
  console.log(message);
}

ws.addEventListener('message', onmessage);

setTimeout(function(){
  ws.removeEventListener('message', onmessage);
}, 1000);

5. 有一定安全风险的API

$(String|Function) -> String

$函数是SecurityWorker VM内部的类预处理函数,其可以方便的在外部环境执行代码。它不同于提供的postMessage和onmessage方法,它是同步的,在编译阶段你的代码会被编译成属性访问,例如:

// sw.js
var location = $('window.location.href');

// 编译后实际代码为
var location = $[0];

此预处理函数的出现主要是针对使用postMessage容易暴露行为的场景。假设我们的SecurityWorker VM的代码需要首先判断当前的Domain后再决定是否进行数据请求,当我们不使用$时,我们的代码如下:

// sw.js
onmessage = function(data){
  if(data.indexOf('your domain') > -1){
    request({
      uri: 'your url', 
      success: function(data){
        postMessage(data.text);
    }});
  }  
}
// your index.html
SecurityWorker.ready(function(){
  var sw = new SecurityWorker();
  sw.oncreate = function(){
    sw.postMessage(location.href);
  }
  sw.onmessage = function(data){
    console.log(data);
  }
});

这里我们可以看到,攻击者很容易发现我们index.html中有传递location.href值的逻辑。但当我们使用$预处理函数后,我们最终的代码会依靠VM转换为opcode经过LLVM处理并进行高强度混淆后嵌入到编译后的代码之中,增强了隐匿性(但需要注意的是,由于$的整个逻辑涉及到从不隐匿环境(Browser)到隐匿环境(SecurityWorker VM)的数据传递,代码仍然在最终编译后的文件中出现,无法做到完全保密,因此可能带来不安全的风险,请斟酌使用)。

onmessage = function(data){
  var location = $(function(){
    // 以下代码在宿主环境中运行
    // SecurityWorker会在编译期进行混淆并嵌入到生成代码中
    // 尽管只是代码的混淆,但是我们再隐藏下真正的数据序列
    var l = location.href;
    return l.split('').map(function(v){
      return v.charCodeAt(0) << 4 + 128;
    }).join(',');
  });

  // 下面的代码已经被编译为SecurityWorker VM的opcode,执行在安全环境
  location = location.split(',').map(function(v){
    return String.fromCharCode((v - 128) >> 4);
  });

  if(location.indexOf('your domain') > -1){
    request({
      uri: 'your url',
      success: function(data){
        postMessage(data.text);
      }
    });
  }
}
SecurityWorker.ready(function(){
  var sw = new SecurityWorker();
  sw.oncreate = function(){
    sw.postMessage('What Ever You Want');
  }
  sw.onmessage = function(data){
    console.log(data);
  }
});

6. 性能优化建议

SecurityWorker VM与V8等强调性能的Javascript引擎不同,SecurityWorker VM主要目标是更小的emscripten生成体积以及更少的内存使用。对于SecurityWorker VM来说,我们并没有集成类似V8一样的JIT机制,而是使用通过离线翻译你的Javascript代码为SecurityWorker VM指令,然后在运行时解释执行的方式,因此在性能上会有一定的损失。
相较于最新版本的V8 JIT优化后的代码,纯CPU计算性能相差7-8倍(执行10000次),I/O任务由于使用了原生环境的功能,性能大体持平。在实际应用中,我们使用SecurityWorker VM的WebSocket每20ms接收10k加密字符串并进行纯Javascript的AES256的解密操作这一任务与原生环境测试结果相比并不大( Mac Pro 2017, Intel Core i5 2.3GHz 平均占用:2.3% vs 1.8% )。

尽可能减少指令

由于SecurityWorker VM并没有JIT,因此你所熟悉的一些优化手段可能会在SecurityWorker VM中失效。不要寄希望于SecurityWorker会优化你的代码,他目前并不智能(逃),任何多余的Javascript代码操作都会增加运行时的开销,例如:

var i = 1000, x = 0;
while( i-- ) x++;

相比于

var x = 0;
for( var j = 0; j < 1000; j++ ) x++;

在SecurityWorker VM中将会快15%,因为for循环中我们额外的引入了比较操作(j < 1000)。但对于此并不需要感到紧张,我们的建议是仍然按照你的方式编写代码,在需要深度优化的时候再进行考虑,因为在SecurityWorker VM中我们持续运行CPU密集型任务的场景并不多,大部分是等待I/O,这很难成为你代码的性能瓶颈。

浮点数有很高的代价

Javascript的Number类型包含了Int和Float,同时根据ECMA-262标准的要求,我们需要通过一个浮点数指针来实现64-bit IEEE Math操作。但是对于SecurityWorker VM内部,我们考虑到内存占用的问题对Int和Float实际上进行了更细的区分,因此在大部分测试下Int的相关操作相比于Float会更快,占用内存会更少。

var a = 1; // 4 bytes
var a = 0.7; // 12 bytes

尽可能使用TypedArray

当你数组中的类型明确为Number时,我们强烈建议你使用TypedArray来解决你的问题。对于TypedArray来说,我们可以明确类型同时省去类型包装的花销,并且不需要自动的进行数组的resize操作,因此相比与普通数组来说会更快更省内存。

var b = new Array( 2048 ); // 4KB for the array with values
for( var i = 0; i++; i < 2048 ) b[ i ] = i + 0.6; // + 8KB with floats

// Just 4KB allocated
var a = new Float32Array( 2048 );

当出现无法解决的性能问题

反复测试并联系我们,帮助我们让SecurityWorker变得更好(笑)。

7. Roadmap

  • 增加SecurityWorker的onerror回调,返回SecurityWorker VM内部的未被捕获的错误
  • 提供小程序的环境支持
  • 提供小游戏的环境支持
  • 提供NodeJS的环境支持
  • 进一步优化生成的opcode大小

securityworker's People

Contributors

dependabot[bot] avatar eroszy avatar tommyshore avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

securityworker's Issues

questions about the library

Hi

Thank you for sharing your work, couple questions, hope it is ok to ask here.

  1. Seems the file size increases after encryption (100B -> 300B), is this a constant overhead or always 3 times the original size?
  2. Is this a pure JS solution or it uses WASM?
  3. How much works are left for supporting WeChat mini program?
  4. The 官网 link is broken.

thanks

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.