Google Chrome 拡張機能で利用可能な JavaScript の圧縮・難読化ツール
はじめに
JavaScript のソースコードの圧縮・難読化ツールについて調べたり試したりしたので記事にまとめた。
Google chrome の拡張機能の開発で javascript-obfuscator/javascript-obfuscator を使って minify をしていたが、Google さんから「Policy 違反」と言われてしまったため、何がまずかったのかと代替できそうなツールを探した。
※2019/09/28 現在では、新しいツール(terser/terser を選択)を使って minify したソースをまだ提出していないので、もし Chrome 拡張機能の JS 圧縮について調べていてこのページにたどり着いた場合は鵜呑みにしないように注意。
TL;DR
- 2019/09 時点で使えそうなのは以下の4つ
- Chrome 拡張機能で使えそうなのは terser/terser
- 自分で管理するウェブサイトとかなら javascript-obfuscator/javascript-obfuscator が良さそう?
目次
環境・条件
1 | $ sw_vers |
詳細
javascript-obfuscator による圧縮で Chrome 拡張機能のポリシー違反
Google chrome の拡張機能を javascript-obfuscator/javascript-obfuscator で圧縮していたら、ポリシー違反 のメールを受信した。
Chrome 拡張機能で使用しても良い圧縮は以下の通り。
Code Readability Requirements:
Developers must not obfuscate code or conceal functionality of their extension. This also applies to any external code or resource fetched by the extension package. Minification is allowed, including the following forms:
- Removal of whitespace, newlines, code comments, and block delimiters
- Shortening of variable and function names
- Collapsing files together
https://developer.chrome.com/webstore/program_policies#content_policies より
以下、Google 翻訳結果。
コードの読みやすさの要件:
開発者は、コードを難読化したり、拡張機能を隠したりしないでください。 これは、拡張パッケージによってフェッチされる外部コードまたはリソースにも適用されます。 次のフォームを含む縮小が許可されます。
- 空白、改行、コードコメント、ブロック区切り文字の削除
- 変数名と関数名の短縮
- ファイルをまとめて折りたたむ
実際に使用していたコマンドやオプションは以下の通り。
1 | $ javascript-obfuscator XXX.js --compact true --string-array false --identifier-names-generator mangled --output XXX.js |
以下のコードを↑のコマンドで変換してみる。
1 | // 変換前 |
1 | // 変換後 |
変換結果を見てみると、半角空白が \x20
という特殊文字(16進数での ASCII コード指定)に置き換わっている。(Google から明確な回答が得られたわけではないので推測にはなるが) おそらくこの部分がまずかったものと思われる。
なお、--string-array false
と --identifier-names-generator mangled
のオプションを指定しなかった場合はこうなる。(読みやすいように --compact false
とした)
1 | var _0x3c77 = ['hello\x20']; |
確かにこのコードだと悪意のあるコードが紛れていないかの検証は厳しい。
他ツールの調査と検証結果
javascript-obfuscator/javascript-obfuscator に代わるツールを探した。
なお、各ツールで日本語(などのマルチバイト文字)の変換を制御できないかオプションを探したが、それらしいオプションは見つけられなかった。
terser/terser
最終的に terser/terser を使うことにした。
インストール(開発環境のみ)
1 | $ npm i -D terser |
圧縮コマンド。
1 | $ terser --compress --mangle --ascii_only true --output XXX.js -- XXX.js |
変換結果。
1 | function hello(){console.log("hello ワールド")}hello(); |
javascript-obfuscator/javascript-obfuscator
再掲。実は --reserved-strings
というオプションがあり、それを使うと半角スペースの変換を抑制はできるので紹介だけしておく。
インストール(開発環境のみ)
1 | $ npm i -D javascript-obfuscator |
圧縮コマンド。
1 | $ javascript-obfuscator XXX.js --compact true --string-array false --identifier-names-generator mangled --reserved-strings " " --output XXX.js |
変換結果。
1 | function hello(){let c='hello ';let d=' ワールド';console['log'](c+d);}hello(); |
これだけ見ると問題無さそうに見えるが、さらに次のコードを変換してみる。文字列の中でさらに引用符を付けて出力するだけのコード。
1 | console.log("' hello '"); |
変換結果がこちら。
1 | console['log']('' hello '');console['log']('" wolrd "'); |
見て分かる通り、このコードは SyntaxError: missing ) after argument list
でエラーになる。
コード中の全ての箇所で、シングルクォーテーションを使っているなら良いが場合によってはダブルクォーテーションを使うこともあるだろうし、正直そんなことを気にしながら開発したくないし、既存コードを変換するのもダルいので却下。
ちなみに --reserved-strings " '\""
とするとこうなる。そうじゃないんだよなぁ…
1 | console['log']('\x27\x20hello\x20\x27');console['log']('\x22\x20wolrd\x20\x22'); |
babel/minify
日本語文字列が unicode に変換されるので今回の目的には合わず。
インストール(開発環境のみ)
1 | $ npm i -D babel-minify |
圧縮コマンド。
1 | $ minify XXX.js --mangle --out-file XXX.js |
変換結果。
1 | function hello(){console.log("hello "+" \u30EF\u30FC\u30EB\u30C9")}hello(); |
google/closure-compiler-npm
日本語文字列が unicode に変換されるので今回の目的には合わず。
インストール(開発環境のみ)
1 | $ npm i -D google-closure-compiler |
圧縮コマンド。
1 | $ google-closure-compiler --js XXX.js --js_output_file XXX.js |
変換結果。
1 | function hello(){console.log("hello \u30ef\u30fc\u30eb\u30c9")}hello(); |
mishoo/UglifyJS2
ECMAScript2015 以降未対応なので、圧縮実行時にエラーとなる。
インストール(開発環境のみ)
1 | $ npm i -D uglify-js |
圧縮コマンド、let
に対応していないのでエラー。
1 | $ uglifyjs --compress --mangle --output XXX.js XXX.js |
まとめ
- 2019/09 時点で使えそうなのは以下の4つ
- Chrome 拡張機能で使えそうなのは terser/terser
- 自分で管理するウェブサイトとかなら javascript-obfuscator/javascript-obfuscator が良さそう?
参考文献
- terser/terser
- javascript-obfuscator/javascript-obfuscator
- babel/minify
- google/closure-compiler-npm
- mishoo/UglifyJS2
関連記事
- webpack & Babel を使って Chrome 拡張機能を開発するためのテンプレート(Hot Reload 付き)
- Vue.js を使ってメインページ側で動作する Chrome 拡張機能を開発する方法
- axios で添付ファイルありのリクエスト(multipart/form-data の POST)
- Chart.js(vue-chartjs) でツールチップの表示内容を変更
- Vue.js で日本語変換での誤発火を抑止しつつ Enter キーで Submit
- ツールチップを簡単に表示できる Tippy.js の使い方
- JavaScript で URL のクエリパラメータを操作する方法
- jQuery Select2 で、初期値の設定と選択状態のクリア