皆さまこんにちは。初見の方は、初めまして。普段はマネジメントや別事業をしつつ、インフラからアプリまで何でもこなす現場のIT何でも屋です。
現在連載で【Hike軽速化(けいそくか)】シリーズをお届けしていますが、今回は、せっかく作ったこのツールが誰かの役に少しでも立てばと思い、急遽単発の特別編・別企画記事をお届けします!
なお、オープンソースとしての公開自体が初めての試みとなります。つたない部分や至らない点もあるかと思いますが、温かく見守っていただけますと幸いです!
1. 背景と課題:Wasm×JS難読化でアプリがクラッシュする壁
今回、Hike プログラミング言語 (hike-lang) でコンパイルした WebAssembly(*.wasm)および JavaScript グルーコード(runtime.js)を組み込んだプロダクトを配布・運用することになりました。
プロダクトやモジュールの配布にあたって、ソースコードや知財保護のための難読化(逆アセンブル防止)は必須のセキュリティ要件となります。
しかし、ここで大きな技術的壁にぶつかりました。
JS難読化ツールをかけるとWasm境界が壊れる
通常の JS 難読化ツール(Terser、UglifyJS、一般的な javascript-obfuscator など)を適用すると、Wasm と JS が相互接続するために使う重要なインターフェース識別子(関数名)まで無差別に難読化・変名されてしまいます:
-
instance.exports.gen_qr_lite→instance.exports._0x4a12(Wasm側に存在しない関数呼び出しでクラッシュ) -
imports.env.hike_matrix_create→imports.env._0x8b39(Wasmリンク時に TypeError) - メモリ割り当て関数
malloc,free,calloc等の改名
普通の難読化ツールでは Wasm と JS の境界を守ることができず、アプリが起動直後にクラッシュしてしまう状態でした。
2. 自作ツールでの格闘:AST静的解析の限界
最初からスマートな解決策があったわけではありません。
まずは自前で 「Babel AST 静的解析で runtime.js 内のソースコードを片っ端から解析し、hike_ プレフィックスや malloc/free などのキーワードを抽出して難読化の予約語に自動登録する解析ツール」 を自作して対応を始めました。
しかし、推測ベースの JS 静的解析だけでは以下のような限界がありました:
- 動的に参照される関数名やオブジェクトプロパティを 100% 漏れなく特定するのが難しい。
- 将来 Hike 言語側で追加される新機能や構文を全自動で保護しきれない。
「JavaScript 側から推測して解析するアプローチには限界がある」と壁にぶつかっていました。
3. 神展開:kanryuさんへの提案と「即日レスポンス&即日実装」
「そもそも、Wasm/JS の接続境界(関数名)を一番正確に知っているのはコンパイラ自身だ」
そう考えた私は、Hike 言語の開発者である kanryu さんへ「コンパイラ本体に JS 難読化機能を組み込んでもらえないか」と提案を送りました。すると、なんとその日のうちに kanryu さんから 「難読化はコンパイラ本体ではなく外部ツールで役割分離してやるべき」 という明確な回答をいただきました。
納得した私が「それなら、外部ツールで安全に難読化できるよう、Wasm/JS 接続用の関数名リスト(シンボル情報)だけをコンパイラから出力してもらえないか」と再提案したところ、驚くべきことに kanryu さんがその日のうちに --export-symbols オプションを即日実装 してくださいました!
この kanryu さんの爆速神対応のおかげで、以下のような完璧かつ安全なビルドパイプラインが完成したのです。
[Hike Source (.hike)]
│
├─► hikec build --export-symbols ──► symbols.json (関数名リスト)
│ │
▼ ▼
[main.wasm] & [runtime.js] ──────────► [hike-obfuscate Pipeline]
│
▼ (100% Wasmクラッシュ防止)
[runtime.min.js]
4. 解決:AIペアプロと自由フォークの結末
こうして、Wasm 境界を 100% 破壊しない完全自動の難読化パイプラインが完成しました。
ベンチマーク検証結果(200回連続実行)
実際にマトリクス生成処理を 200 回連続実行した比較結果でも、処理速度(+1.13ms 差)やスループットを維持したまま 100% 完全な精度と安全性を達成しました。
| 検証項目 | オリジナル(未難読化) | 難読化後 (クラッシュ防止保護) | 判定・差分 |
|---|---|---|---|
| Wasm バイナリサイズ | 12.70 KB | 12.70 KB | 完全一致(Wasm無改変・完全保護) |
| JSコードサイズ | 15.69 KB | 36.75 KB | 約 2.3 倍(制御フロー平坦化・暗号化) |
| 実行時間 (200ops) | 1.19 ms | 2.32 ms | +1.13 ms(実用上超高速) |
Node不要の多角的な実行サポート
Node.js や npm install がない環境でも動くよう、複数環境の実行方法を用意しました:
- Bash シェルスクリプト版 (
build-obfuscate.sh) - Node依存なし Pure JS (
build-obfuscate-standalone.js) - Deno 単一バイナリ (.exe) 化
5. 今後の運用方針とOSSとしての活用について
なお、今回のオープンソース化にあたり、Git / GitHub での管理は私自身にとってもほぼ初めての挑戦でした。ライセンスの選び方、Git コマンドの手順などはすべて AI パートナー(Gemini)と相談しながらペアプロ感覚で仕上げています。
そのため、私自身は今後この難読化ツールを一人で継続的に保守・メンテナンスし続けていく技術的・時間的な余裕が十分にあるわけではありません。
だからこそ、本ツールは誰もが制限なく利用・改善できるように MIT License で公開することにしました。
興味を持ってくださったエンジニアの皆さんが、ご自身の用途に合わせて自由にフォーク・改変・機能拡張して使っていただけたらなと思っています!
どうか Hike 言語が、これからの Web フロントエンド開発における新しい選択肢の一つとなれば嬉しいです。本ツールもあわせて、ぜひ気軽に試してみてください!
- リポジトリ: Kinoshita-Keita3/hike-obfuscate-proposal
- Hike 言語公式: hike-lang/hike-lang
関連リンク(本編シリーズ)
謝辞
本ツールの作成にあたり、優れた難読化基盤 javascript-obfuscator や Babel の開発者様、そして何より提案に応じて即日関数名出力機能(--export-symbols)を実装してくださった Hike 言語の開発者である kanryu さんに心より深く感謝申し上げます。
また、本記事の構成案の壁打ちや技術検証のサポートには生成AI(Gemini)を活用しました。AIの強力なアシストを受けながら、スムーズに検証から執筆までをやり切ることができました。
軽量なWasm環境やエッジな開発、AIを活用した開発に興味がある方は、ぜひ Hike を触ってみてください!