難読化とはコードを分かりにくい形に変換し、読み手に理解し辛くする事です。難読化しても変換前のコードと同じように動作します。そのためプログラム自体の機能は変かわりません。
難読化の必要性
かつて、こんな記事を書きました。
ウェブブラウザーで F12キーを押すと、ウェブページの検証画面が表示されます。そしてコンソールから値の書き換えをすることができます。
ためしにこんなページを作りました。変数 score の値を表示するだけです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>テスト</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> </head> <body> <div id = "score"></div> <script> let score = 0; let $score = document.getElementById('score'); setInterval(()=>{ showScore(); }, 100); function showScore() { $score.innerText = score; } </script> </body> </html> |
F12キーを押してコンソールに以下を入力してみます。
1 |
score = 100 |
これでスコアを変更することができます。
またF12キーを押してコンソールに以下を入力すると関数の定義を変更することもできてしまいます。
1 2 3 |
function showScore(){ $score.innerText = 100; } |
function ~ という書き方は改ざんされる危険性があります。アロー関数にすると関数の書き換えを防ぐことはできます。
1 2 3 4 5 6 7 |
// function showScore(){ // $score.innerText = score; // } const showScore = () => { $score.innerText = score; } |
これだと上記の方法でshowScore関数を書き換えようとしてもエラーが出てうまくいきません。ただ変数 score が改ざん可能であるという事実はかわりません。
そこで不正な改ざんをさせないために難読化で対抗します。難読化は読んで字のごとし、改ざんをしようとする者にとってコードを読みづらくする事です。だから100%防ぐことはできません。
JavaScript Obfuscator Toolを使ってみる
JavaScript Obfuscator Toolは難読化をするのに役立ちます。
設定を変更しないで以下を難読化してみます。
1 2 3 4 5 |
// Paste your JavaScript code here function hi() { console.log("Hello World!"); } hi(); |
するといい感じに難読化してくれます。
ただよく見ると関数hi()を呼び出していることがわかります。
どのように難読化されるかを検証するためには「String Array」とCompactは外しておいたほうがいいかもしれません。
以下を難読化してみましょう。
1 2 3 4 5 6 7 8 9 |
let score = 0; let $score = document.getElementById('score'); setInterval(()=>{ showScore(); }, 100); const showScore = () => { $score.innerText = score; } |
すると以下のようになります。文字列に関する操作をややこしくしていただけでほとんどなにも変わっていません。
1 2 3 4 5 6 7 |
let score = 0x0, $score = document['getElementById']('score'); setInterval(() => { showScore(); }, 0x64); const showScore = () => { $score['innerText'] = score; }; |
そこでRename Globalsのオプションを有効にします。
1 2 3 4 5 6 7 |
let _0x4c73e4 = 0x0, _0x46b881 = document['getElementById']('score'); setInterval(() => { _0x233e92(); }, 0x64); const _0x233e92 = () => { _0x46b881['innerText'] = _0x4c73e4; }; |
関数名が置き換わったので解読が難しくなったように思えます。
Rename Propertiesのオプションを有効にするとライブラリを使用している場合、うまく動かなくなるので選択しないほうがよいでしょう。
1 2 |
// ThreeJSでシーンを生成 let scene = new THREE.Scene(); |
1 2 |
// THREE._0x4b85edはコンストラクタではないとエラーが出る let _0x48fdf7 = new THREE['_0x4b85ed'](); |
document[‘getElementById’](‘score’)となっていると、ここにスコアが表示されることがわかるので、
1 2 3 4 5 6 7 8 9 10 11 12 |
let $score = document.createElement('div'); // 要素をJavaScript内でつくる document.body.appendChild($score); let score = 0; setInterval(()=>{ showScore(); }, 100); const showScore = () => { $score.innerText = score; } |
これだと難読化したときバレにくいと思います。
1 2 3 4 5 6 7 8 9 |
let _0x59ede1 = document['createElement']('div'); document['body']['appendChild'](_0x59ede1); let _0x177412 = 0x9; setInterval(() => { _0x521bd8(); }, 0x64); const _0x521bd8 = () => { _0x59ede1['innerText'] = _0x177412; }; |