一開始聽到這個需求挺懵的,做爲一個聊天軟件,代碼裏並無所謂核心算法和商業機密,爲何須要保護源碼。何況Electron自己在打包時提供了
asar
這種archive文件格式,會將全部源碼和依賴封裝。node
一陣分析後,Electron項目源碼保護仍是有必要的。git
asar e
的命令,能夠很簡單地進行解壓和獲得源碼。翻github和Stack Overflow,發現對Electron源碼保護方案討論由來已久。github
Source Code Protection #3041算法
總結下來,官方並無打算提供解決方案。做者們認爲,不管用什麼形式去加密打包文件,密鑰總歸是須要放置在包裏的。。shell
繼續翻,國內論壇上一些大佬有嘗試解決過這個問題,是從asar打包這塊切入,然而,並無看懂。。數據庫
簡單理解下大佬的思路:對asar源碼進行分析,在 asar 打包時寫入文件以前, 經過加密算法把寫入的文件進行加密;在asar.js讀取文件處添加對應文件解密算法;同時對asar文件頭部 json 進行加密,使得官方的 asar 就無法解包了。canvas
思路我是看懂了,怎麼下手徹底不知。有興趣的童鞋能夠主動去留言詢問。。安全
electron issue裏有人提出能夠利用nodejs的addons來封裝核心代碼。addons是nodejs實現跨平臺調用原生代碼的插件,由於保護源碼的主要目的是爲了提升安全性,將數據庫密鑰等關鍵字段存儲在原生代碼中,提升破解門檻。
C++語法基本忘光了,先實現一個簡單業務練練手:JS傳入用戶信息對象,C++讀取對象,處理後,返回數據庫對應的密鑰。
Nodejs與C++之間的類型轉換由V8 API提供,具體可參考Node.js 和 C++ 之間的類型轉換。
// key.cc
#include <node.h>
namespace key {
using v8::FunctionCallbackInfo;
using v8::Isolate;
using v8::Local;
using v8::NewStringType;
using v8::Object;
using v8::String;
using v8::Value;
void GetKeys(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
// 判斷js傳遞的參數是否爲對象
if (!args[0]->IsObject()) {
printf("not Object\n");
}
// 新建對象,將cfg和id綁定到對象
Local<String> cfgKey = v8::String::NewFromUtf8(isolate, "testxxx");
Local<Object> keyObj = v8::Object::New(isolate);
keyObj->Set(v8::String::NewFromUtf8(isolate, "cfgKey"), cfgKey);
// 讀取js傳遞的對象
Local<Object> userObj = Local<Object>::Cast(args[0]);
Local<Value> id = userObj->Get(String::NewFromUtf8(isolate, "id"));
keyObj->Set(v8::String::NewFromUtf8(isolate, "id"), id);
args.GetReturnValue().Set(keyObj);
}
void GetUidByUserInfo(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
Local<Object> userObj = Local<Object>::Cast(args[0]);
Local<Value> id = userObj->Get(String::NewFromUtf8(isolate, "id"));
args.GetReturnValue().Set(id);
}
void Initialize(Local<Object> exports) {
NODE_SET_METHOD(exports, "getKey", GetKeys);
NODE_SET_METHOD(exports, "getUserKey", GetUidByUserInfo);
}
NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize)
}
複製代碼
key.cc
中暴露了兩個簡單的方法,分別是獲取全部key的對象和獲取單獨用戶的key,固然,這裏只是簡單的業務邏輯展現。在Nodejs Addons中,接口是經過這種模式的初始化函數:
void Initialize(Local<Object> exports);
NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize)
複製代碼
NODE_GYP_MODULE_NAME
,是在binding.gyp中設定的模塊名稱。Nodejs不能直接調用C++文件,須要先經過node-gyp將其編譯爲二進制文件,binding.gyp則是相似JSON格式的構建配置文件。在根目錄下新建該文件:
{
"targets": [
{
"target_name": "dbkey",
"sources": [ "key.cc" ]
}
]
}
複製代碼
安裝好node-gyp和相關依賴。前後輸入命令 node-gyp configure
, node-gyp build
成功後,生成build目錄,獲得二進制文件dbkey.node。
而後,咱們寫個js測試下。
const dbKey = require('./build/Release/dbkey');
const userInfo = {
id: '123456',
};
console.log(dbKey.getKey()); // { cfgKey: 'testxxx', id: '123456' }
console.log(dbKey.getUserKey(userInfo)); // 123456
複製代碼
經過require(),咱們就能夠調用C++模塊。
但此時的dbkey.node並不能直接扔進electron中使用,咱們須要用electron相關頭文件對該插件進行重編譯。
node-gyp rebuild --target=1.7.11 --arch=x64 --target_platform=darwin --dist-url=https://atom.io/download/atom-shell
根據你的electron版本號(target)和平臺(target_platform)分別重編譯。
ps. 由於Nodejs版本不少,其V8 API也不徹底一致,C++邏輯建議使用NAN,NAN對V8 API作了封裝,使咱們不用關心版本問題。咱們項目中使用的Native模塊,如canvas,sqlite等,其源碼也都是使用NAN。
利用C++ Addons封裝核心業務代碼,能必定程度提高源碼的安全性。但須要修改以前的打包流程,開發調試上也會帶來一些不便。仍是看業務上如何取捨吧。