JSDefender の構成ファイルは JSON の形式になっています。既定では、JSDefender は現在の作業ディレクトリから jsdefender.config.json
というファイルを検索します。
次の JSON コード スニペットは、構成ファイルの概要を示しています。
{
// 入力ファイルとその保護を指定します
"inputs": [],
// 入力ファイルの取得元となるソース ディレクトリを指定します
"sourceDir": "<mySourceDir>",
// 保護されたファイルの移動先となるフォルダー
"outDir": "<myOutput>",
// 割り当てファイルを指定するか、または割り当てファイルを無効にします
"mapFile": "<my-map.json> または 'off'",
// JSDefender Runtime をこのファイルに出力します
"runtimeFile": "<myRuntime>",
// JSDefender Runtime を差し込む方法を指定します
"runtimeInjection": "<injection mode>"
// JSDefender をメッセージ非表示モードで実行します。既定:無効
"quietMode": true,
// ライセンス キーを指定します。ライセンス キーは通常、環境変数または CLI 引数で指定することをお勧めします。
"license": "<license key>",
// 同じコマンド ライン オプションの設定
"esTarget": "<es5、es2015、など>",
"randomSeed": 12345,
"ignoreUnsafeConstructs": false,
"disableInline": false,
// グローバルに使用する保護オプション
"settings": {},
// 名前付き構成セット(説明は後出)
"namedSets": {}
// 入力ファイルを指定するためのパターン サポート(省略可能)
"glob": true,
}
-
inputs
:このセクションは、入力ソース ファイルの一覧を定義し、ファイルごとの保護を構成します。コマンド ライン引数に 1 つの入力ファイルと 1 つの出力ファイルだけを指定する場合を除き、このセクションには少なくとも 1 つのエントリを指定する必要があります。前者の場合は、inputs
を省略したり空の一覧を指定したりできます。 -
sourceDir
:任意の入力ディレクトリ。指定した場合、入力ファイルは作業ディレクトリ直下にあるこのフォルダーからの相対パスにあるものと見なされます。このセクションを省略した場合は、入力ファイルは作業ディレクトリからの相対パスにあるものと見なされます。メモ:このフォルダー内の JavaScript ファイルが JSDefender によって収集されることはありません。それらのファイルはinputs
セクションに 1 つずつ指定する必要があります。 -
outDir
:任意の出力ディレクトリ。作業ディレクトリからの相対位置になります。JSDefender は、保護されたファイルを元の名前でこのフォルダーに格納します。既定値:protected
。 -
mapFile
:任意の割り当てファイル名。この値を指定することで、既定の割り当てファイル名(lexical-map.json
)が変更されます。"off"
を指定した場合は、割り当てファイルの生成が無効になります。 -
runtimeFile
:任意のファイル名。JSDefender Runtime を別のファイルに入れたい場合にのみ、この値を設定します。runtimeFile
オプションを設定した場合、runtimeInjection
にはseparateSource
のみ設定できます。これが既定で使用されます。 -
runtimeInjection
:保護されたコードに JSDefender Runtime を差し込む方法を指定します。 -
quietMode
、license
、estarget
、randomSeed
、ignoreUnsafeConstructs
、disableInline
、glob
:これらはすべて省略可能(任意)です。それぞれ類似のコマンド ライン オプションと同じ操作を意味しています。 -
settings
:任意の保護構成。この構成はすべての入力ファイルに対して使用されますが、特定ファイルの特定構成があれば、それによって上書きされます。この値は、保護設定で説明されているいくつかのプロパティを持つオブジェクトです。 -
namedSets
:名前付き構成セットの、任意のハッシュ オブジェクト。頻繁に使用される構成設定をグループ化するのに使用できます。詳細については、名前付き構成セット セクションを参照してください。
入力ファイル
構成ファイルの inputs
プロパティは、入力ファイルを配列として表します。この配列の各要素は、文字列か、詳細を指定するオブジェクトのいずれかになります。要素が文字列の場合は、値には sourceDir
内の場所を指定します。単純な文字列の代わりに、次のプロパティを持つ記述子オブジェクトを使用することもできます。
-
in
:必須。単純な文字列を使用する場合と同様に、入力ファイルの名前を指定します。 -
out
:任意。outDir
内の既定の場所と名前の代替となる、入力の名前またはパスを指定します。 -
protection
:任意。名前付きセットの文字列キーを使用できるようにします。このプロパティを設定すると、settings
プロパティ内の保護オプションの代わりに、名前付きセット内の保護オプションが使用されます。 -
isModule
:任意のブール値。対応する入力ファイルをモジュールとして読み込む(たとえば、<script src="..." type="module"></script>
タグを使用する)場合は、true に設定します。 -
ignore
:任意。入力パターンに一致するファイルを除外するためのパターン。このフィールドは、glob
パターン サポートが有効になっている場合に考慮されます。
例:
// 単純なシナリオ
{
"inputs": [ "code1.js", "code2.js" ]
}
// vendor1.js ファイルを保護しません
{
"inputs": [
"code1.js",
"code2.js",
{
"in": "vendor1.js",
"protection": "off"
},
"vendor2.js"
]
}
// vendor ファイルを出力のサブフォルダーに移動します
{
"inputs": [
"code.js",
{
"in": "vendor1.js",
"out": "/vendors/vendor1.js"
},
{
"in": "vendor2.js",
"out": "/vendors/vendor2.js"
},
]
}
// dist および lib フォルダー内のすべての .js ファイルを保護し、元のフォルダー構造を保持したまま、保護されたファイルを保護されたフォルダーに出力します
{
"inputs": [ "dist//*.js", "lib//*.js"], "glob": true
}
// dist フォルダーのうち、dist/vendor フォルダーを除くフォルダー内のすべての .js ファイルを保護し、保護されたファイルを "protected/dist//*.js" パスに出力します
{
"inputs": [
{
"in": "dist//.js", "ignore": "dist/vendor/**/.js"
}
],
"glob": true
}
ランタイムの差し込み
runtimeInjection
構成オプションを使用すると、保護されたソース コードに JSDefender Runtime(コアな保護ユーティリティのコレクション)を差し込む方法を指定できます。オプションの値には文字列またはオブジェクトを指定できます。
文字列を使用する場合は、以下のいずれかの値を指定できます。
-
firstNonModule
:モジュール以外の最初のソース ファイルに JSDefender Runtime を差し込みます(既定、runtimeFile
を設定しない場合)。 -
separateSource
:runtimeFile
オプションで指定されたファイルに JSDefender Runtime を差し込みます(既定、明示的なruntimeFile
を使用する場合)。このオプションを使用する場合は、明示的に--runtime
CLI フラグを設定するか、またはruntimeFile
構成ファイル オプションにランタイム ファイルの名前を設定する必要があります。 -
all
:JSDefender Runtime を、必要とするすべての保護されたファイルに差し込みます。
文字列の代わりに、次のプロパティを持つオブジェクトを使用することもできます。
-
mode
:差し込みモードを定義するための値を持つ任意のプロパティ。 -
options
:必須の文字列。以下の値のいずれかを指定できます。-
self-defending
:コード改ざんを監視する自己防御型の変換で JSDefender Runtime を保護します。 -
no-eval
:JSDefender Runtime に対する自己防御型の保護を無効にします。
-
eval
関数を呼び出します。攻撃者が JSDefender Runtime のコードを改ざんしていないかどうかを監視するために eval
の呼び出しが必要です。エンジンがこのように自己防御型の変換を使用すれば、eval
を通して、悪意のあるコードが差し込まれることを防ぐことができます。 CSP コンプライアンス規則によって JavaScript コード内の eval
が許可されていない場合は、no-eval
オプションを使用できます。no-eval
オプションを使用した JSDefender Runtime の保護強度は、既定の保護(自己防御型)ほど高くないので注意してください。可能な限り no-eval
を使用しないようにしてください。 --estarget
が es5
モードに設定されている場合、差し込まれた JSDefender Runtime は変数宣言に let
や const
ではなく var
を使用します。例:
// JSDefender Runtime をすべての入力ファイルへ差し込みます
{
"runtimeInjection": "all"
}
// 保護エンジンに対して自己防御型の
// JSDefender Runtime を無効にするよう指示します
{
"runtimeInjection": {
"options": "no-eval"
}
}
glob
が有効になっており、runtimeInjection
モードが firstNonModule
に設定されている場合、JSDefender Runtime は入力パターンに一致する最初の入力に差し込まれます。 一致するファイルの中から JSDefender Runtime を差し込む特定の入力ファイルを選択するには、その入力を最初に明示的に指定したあと、2 番目の入力パターンですべての入力を指定し、かつ最初のファイルを無視します。 // `dist` フォルダー内のすべての .js ファイルを保護し、`dist/index.js` ファイルに JSDefender Runtime を差し込みます
{
"inputs":
[
"dist/index.js",
{
"in": "dist/**/*.js", "ignore": "dist/index.js"
}
],
"glob": true
}
保護設定
構成ファイルの settings
プロパティが保持するオブジェクトでは、各プロパティは保護の変換を指名し、プロパティの値はその変換のパラメーターを指定します。次の JSON コード スニペットは、settings
のスキーマを示しています。
{
"booleanLiterals": "<value>",
"constantArgument": "<value>",
"consoleCloaking": "<value>",
"controlFlow": "<value>",
"dateLock": "<value>",
"debuggerRemoval": "<value>",
"variableGrouping": "<value>"
"domainLock": "<value>",
"exprSequence": "<value>",
"functionReorder": "<value>",
"globalObjectHiding": "<value>",
"integerLiterals": "<value>",
"localDeclarations": "<value>",
"propertyIndirection": "<value>",
"propertySparsing": "<value>",
"selfDefending": "<value>",
"stringLiterals": "<value>",
"variableGrouping": "<value>"
}
ここで、"<value>"
はプロパティ値のプレースホルダーに過ぎず、実際にはプロパティごとに異なる値が入ります。すべてのプロパティは任意です。構成から除外することもできます。プロパティを除外すると、保護エンジンはプロパティ値を別の構成エンティティから継承します。 <value>
を null
または false
に設定すると、関連する保護が無効になります。<value>
に true
を指定すると、その保護が既定の設定で有効になります。ただし、既定値がない domainLock
と dateLock
は除きます。
プロパティ constantArgument
、debuggerRemoval
、exprSequence
、propertyIndirection
、propertySparsing
、stringLiterals
、variableGrouping
には、それぞれブール値のみを設定できます。したがって、これらのプロパティは、有効にするか無効にするかを選択できる以外のオプションはありません。
--estarget
による保護オプションの変更
--estarget
が es5
モードに設定されている場合、以下の保護オプションが変更されます。
propertyIndirection
:--estarget
が es5
モードに設定されている場合は、オブジェクト プロパティに対して機能しません。たとえば、x = { a: 2 } は x = { ["a"]:2 } に変換されず、そのままの状態になります。
stringLiterals
:--estarget
が es5
モードに設定されている場合は、文字リテラルのオブジェクト プロパティ名に対して機能しません。たとえば、x = { "hi": 2 } は x = { [extractedStringObject]: 2 }(extractedStringObject は変数)に変換されず、x = { hi: 2 } になります。
以下に、他のプロパティの構成の詳細を示します。
booleanLiterals
このプロパティの値には、ブール値か、またはこの変換のランダム化を有効または無効(既定)にする randomize
プロパティを持つオブジェクトを指定できます。いくつかの例を見てみましょう。
// この変換を無効にします
{
"booleanLiterals": false
}
// この変換をランダム化なしで有効にします
{
"booleanLiterals": true
}
// この変換をランダム化なしで有効にします
{
"booleanLiterals": {}
}
// この変換をランダム化ありで有効にします
{
"booleanLiterals": {
"randomize": true
}
}
// この変換をランダム化なしで有効にします
{
"booleanLiterals": {
"randomize": false
}
}
consoleCloaking
この値には、ブール値または exclude
プロパティを持つオブジェクトを指定できます。オブジェクトを使用する場合、exclude
は単独の文字列または文字列の配列を指定できます。JSDefender はこれらの文字列値を console
関連メソッドの名前として使用し、コンソールのクローキングの保護から除外します。いくつかの例を見てみましょう。
// この変換を無効にします
{
"consoleCloaking": false
}
// この変換を有効にして、すべてのコンソール メソッドをクローキングします
{
"consoleCloaking": true
}
// この変換を有効にして、"error" をクローキングから除外します
{
"consoleCloaking": {
"exclude": "error"
}
}
// この変換を有効にして、"error" および "table" メソッドをクローキングから除外します
{
"consoleCloaking": {
"exclude": ["error", "table"]
}
}
コンソールのクローキングの保護によってサポートされるメソッド:assert
、clear
、count
、countReset
、debug
、dir
、dirxml
、error
、group
、groupCollapsed
、groupEnd
、info
、log
、profile
、profileEnd
、table
、time
、timeEnd
、timeLog
、timeStamp
、trace
、および warn
。
controlFlow
このプロパティの値には、ブール値(関連する変換を有効または無効にする)か、または以下のプロパティを持つオブジェクトを指定できます。
-
randomize
:制御フローの難読化に使用される値のランダム化を有効または無効(既定)にするブール値を指定できます。 -
injectFakeCode
:変換される制御フローへフェイクの条件を差し込むことを有効または無効にするブール値を指定できます。既定では、 このオプションは無効になっています。
例:
// この変換を無効にします
{
"controlFlow": false
}
// この変換を有効にします(No. 1)
{
"controlFlow": true
}
// この変換を有効にします(No. 2)
{
"controlFlow": {}
}
// ランダム化を有効にします
{
"controlFlow": {
"randomize": true
}
}
// ランダム化を使用し、フェイクの条件をフローに差し込みます
{
"controlFlow": {
"randomize": true,
"injectFakeCode": true,
}
}
dateLock
この保護オプションは、値を false
に設定することで無効にはできますが、true
に設定しても、適用する既定値がないため有効にはできません。以下を記述すると、エラーが発生します。
{
"dateLock": true
}
dateLock は、3 つの任意のプロパティを持つ構成オブジェクトを受け付けます。
-
startDate
:ロック期間の開始日(当日を含む)。指定しない場合は、endDate
以前のすべての日付が受け付けられます。 -
endDate
:ロック期間の終了日(当日を含まない)。指定しない場合は、startDate
以後のすべての日付が受け付けられます。 -
errorScript
:任意の文字列。このプロパティには、有効な JavaScript コード スニペットを指定できます。このスクリプトは、コードが指定された日付範囲外で実行されたことが保護によって検出されたときに実行されます。詳細については、エラー スクリプト セクションを参照してください。
どのプロパティも省略可能ですが、少なくとも 1 つは指定する必要があります。どちらの日付プロパティも、ISO-8601 の日付書式(YYYY-MM-DDTHH:MM:SSZ
。T
は日付と時刻の区切り文字)を使用する文字列を必要とします。プロパティ値の時刻部分は省略可能です。
例:
// 2020 年 4 月 30 日 11:00 AM UTC からコードをロックします
{
"dateLock": {
"endDate": "2020-04-30T11:00"
}
}
// 2020 年 5 月 15 日 3 時 PM UTC からこのコードの実行を許可します
{
"dateLock": {
"startDate": "2020-05-15T15:00"
}
}
// 2020 年 4 月 1 日にのみ、このコードの実行を許可します
{
"dateLock": {
"startDate": "2020-04-1",
"endDate": "2020-04-02"
}
}
// 2020 年にのみ、このコードの実行を許可します。その期間以外の日付に
// 実行した場合には、警告ダイアログを表示します
{
"dateLock": {
"startDate": "2020-01-1",
"endDate": "2021-01-01",
"errorScript": "alert('It is not 2020 anymore')"
}
}
devToolsBlocking
この値には、ブール値または patience
プロパティを持つオブジェクトを指定できます。オブジェクトを使用する場合、patience
には 0 から 1000 までの間の必要な数字を指定します(既定は 5)。この数字は、ブレークポイントでストップさせる回数を示します。この回数に達すると、JSDefender は保護されたコードの無効化を宣言します。いくつかの例を見てみましょう。
// この変換を無効にします
{
"devToolsBlocking": false
}
// この変換を有効にし、デバッグが 5 回ストップしたらコードの無効化を宣言します。
{
"devToolsBlocking": true
}
// この変換を有効にし、デバッグが 10 回ストップしたらコードの無効化を宣言します。
{
"devToolsBlocking": {
"patience": 10
}
}
domainLock
この保護オプションは、値を false
に設定することで無効にはできますが、true
に設定しても、適用する既定値がないため有効にはできません。以下を記述すると、エラーが発生します。
{
"domainLock": true
}
このプロパティは、文字列を受け付けるか、以下のプロパティを持つ構成オブジェクトを受け付けます。
-
domainPattern
:必須の文字列。このプロパティに完全なドメイン名(例:www.mydomain.net
)または部分的なドメイン名(例:.mydomain.net
)を指定することで、mydomain.net
の任意のサブドメインでコードを実行できます。 -
errorScript
:任意の文字列。このプロパティには、有効な JavaScript コード スニペットを指定できます。このスクリプトは、コードが無効なドメインで実行されたことが保護によって検出されたときに実行されます。詳細については、エラー スクリプト セクションを参照してください。
.mydomain.net
の場合は、www.mydomain.net
と一致します。しかし、ハッカーが www.mydomain.net.example.com
などのドメインを登録して、この保護を破ることはできません。この名前では指定されたパターンと一致しないためです。パターンに mydomain.net
を指定すると、mydomain.net
にあるコードが読み込まれますが、そのサブドメインにあるコードは読み込まれません。文字列値を使用した場合は、domainPattern
プロパティと同じ意味になります。複数のドメインにコードをバインドできます。複数のドメイン パターンはセミコロンで区切って指定します。
例:
// preemptive.com で実行されるコード(No. 1)
{
"domainLock": {
"domainPattern": "preemptive.com"
}
}
// preemptive.com で実行されるコード(No. 2)
{
"domainLock": "preemptive.com"
}
// mycompany.com と myorg.org の任意のサブドメインで実行されるコード(No. 1)
{
"domainLock": {
"domainPattern": "mycompany.com;.myorg.org"
}
}
// mycompany.com と myorg.org の任意のサブドメインで実行されるコード(No. 2)
{
"domainLock": "domainPattern": "mycompany.com;.myorg.org"
}
// Acme Company の Web サイトで実行され、
// このサイト以外の場所で実行されるとアラートを表示するコード
{
{
"domainLock": {
"domainPattern": ".acmecompany.com",
"errorScript": "alert('Invalid domain')"
}
}
functionReorder
このプロパティの値には、ブール値か、またはこの変換のランダム化を有効または無効(既定)にする randomize
プロパティを持つオブジェクトを指定できます。いくつかの例を見てみましょう。
// この変換を無効にします
{
"functionReorder": false
}
// この変換を有効にします(No. 1)
{
"functionReorder": true
}
// この変換を有効にします(No. 2)
{
"functionReorder": {}
}
// ランダム化を有効にします
{
"functionReorder": {
"randomize": true
}
}
// ランダム化を無効にします
{
"functionReorder": {
"randomize": false
}
}
globalObjectHiding
このプロパティの値には、変換を有効または無効にするブール値か、または以下の詳細な設定を提供するオブジェクトを指定できます。
-
usePredefined
:この変換では、既定で 20 数個のグローバル オブジェクトを非表示にします。この構成値にfalse
を設定すると、事前に定義されたオブジェクトではなく、独自のグローバル オブジェクトを定義できます(既定はtrue
)。 -
include
: 単独の文字列値または文字列の配列を、グローバル オブジェクトの非表示の設定として使用します。各文字列がグローバルな識別子で、サフィックス(接尾語):g
を任意で付けます。このサフィックスは、グローバル関数をグローバル オブジェクトにバインドする必要があることを示します。 -
exclude
: 単独の文字列または文字列のリストです。保護エンジンはこれらのグローバル識別子を非表示にしません。
JSDefender では、グローバル コンテキストで宣言されたオブジェクトと、フェーズの解析後に未解決のままになっているオブジェクトに対してのみ、この変換を適用します。たとえば、次のようなソース コードの場合、JSDefender は myGlobal
オブジェクトに対して変換を適用します。
console.log(myGlobal.myProp);
この場合、ソース コードには myGlobal
に対する宣言が含まれていないので、このオブジェクトは未解決のままです。しかし、次のソース コードでは、保護エンジンは myGlobal
に対してグローバル オブジェクトの非表示を適用しません。
const myGlobal = {
myProp: "Hello"
};
// ...
console.log(myGlobal.myProp);
いくつかの構成例を見てみましょう。
// 定義済みの全オブジェクトに対してグローバル オブジェクトの非表示を有効にします
{
"globalObjectHiding": true
}
// 定義済みのオブジェクトにカスタム グローバル変数を追加します
{
"globalObjectHiding": {
"usePredefined": true,
"include": "myGlobalVar"
}
}
// 定義済みのオブジェクトにカスタム グローバル変数と関数を追加します
{
"globalObjectHiding": {
"usePredefined": true,
"include": ["myGlobalVar", "myGlobalFunc"]
}
}
// さらに、グローバルなコンテキスト バインド関数を使用します
{
"globalObjectHiding": {
"usePredefined": true,
"include": "myGlobalFunc:g"
}
}
// "window" および "setTimeout" のみを非表示にします
// メモ:setTimeout にはグローバル コンテキストが必要
{
"globalObjectHiding": {
"usePredefined": false,
"include": ["window", "setTimeout:g"]
}
}
// "window" および "setTimeout" 以外の定義済みのグローバル オブジェクトをすべて非表示にします
// メモ:setTimeout にはグローバル コンテキストが必要
{
"globalObjectHiding": {
"exclude": ["window", "setTimeout"]
}
}
include
オプションにサフィックス :g
を追加すると、JSDefender Runtime がそのバインドを保証します。 JSDefender はカスタム グローバル オブジェクトについて何も知らないので、グローバル オブジェクトの非表示がそれらのオブジェクトに対し、すべてのコンテキストで適切に動作するかは保証できません。しかし、JSDefender は定義済みのすべてのグローバル オブジェクトを正しく処理します。そのような状況を見つけた場合は、この変換からそのオブジェクトを除外してください。
この変換がサポートする定義済みのグローバル オブジェクトは以下のとおりです。
-
window
、console
、document
、history
、location
、navigator
、origin
、parent
、performance
、screen
、self
-
alert
、clearInmediate
、clearInterval
、clearTimeout
、setInterval
、setTimeout
-
isFinite
、isNaN
、parseFloat
、parseInt
、encodeURI
、encodeURIComponent
、decodeURI
、decodeURIComponent
-
Object
、Function
、Boolean
、Symbol
、Number
、BigInt
、Math
、Date
、String
、RegExp
-
Array
、Int8Array
、Uint8Array
、Uint8ClampedArray
、Int16Array
、Uint16Array
、Float32Array
、Float64Array
、BigInt64Array
、BigUint64Array
-
Map
、Set
、WeakMap
、WeakSet
、ArrayBuffer
、SharedArrayBuffer
-
JSON
、Promise
integerLiterals
このプロパティの値には、変換を有効または無効にするブール値か、または以下の設定を持つオブジェクトを指定できます。
-
randomize
:整数リテラルの置き換えの制御に使用される値のランダム化を有効または無効(既定)にする、任意のブール値を指定できます。 -
radix
:任意の文字列または文字列の配列。これにより整数リテラルの置き換え時に使用する基数を指定します。指定できる値は"binary"
、"decimal"
、"hexadecimal"
、"octal"
です。複数の基数を指定する場合、保護エンジンはそのうちの 1 つをランダムに選び、各リテラルを変換します。 -
lower
:任意の値。難読化対象となる最小の整数リテラル(0 ~ 65535。既定値:0)を指定できます。 -
upper
:任意の値。難読化対象となる最大の整数リテラル(0 ~ 65535。既定値:8)を指定できます。
いくつかの例を見てみましょう。
// 整数リテラルをランダム化なしで置き換えます
{
"integerLiterals": true
}
// 整数リテラルをランダム化ありで置き換えます
{
"integerLiterals": {
"randomize": true
}
}
// 整数リテラルを 8 進法形式に置き換えます
{
"integerLiterals": {
"radix": "octal"
}
}
// 整数リテラルを、ランダムに 8 進法または 2 進法形式へ置き換えます
{
"integerLiterals": {
"radix": ["octal", "binary"]
}
}
localDeclarations
このプロパティでは、変換を有効または無効にするブール値か、または以下の設定を持つオブジェクトを受け付けます。
-
nameMangling
:変数の名前変更時に使用する変換を記述する任意の文字列を指定できます。使用可能な値は、"sequential"
、"hexadecimal"
、"base52"
、"base62"
、"runic"
、"glagolitic"
、"tifinagh"
です。既定値は"base52"
です。 -
excludeIds
:任意の識別子文字列の一覧。指定した名前を持つ最上位の宣言は、名前変更されません。
"runic"
、"glagolitic"
、および "tifinagh"
の名前変更モードは、ᚲᚠᛋᚬ
、ⰆⰡⰜⰌ
または ⴲⵋⴳⴻ
のように、変数名に Unicode 文字を使用します。この動作は ECMAScript 2015 に準拠していますが、古いバージョンのブラウザーや Node.js ではサポートしない可能性があります。例:
// ローカル宣言を有効にします
{
"localDeclarations": true
}
// 名前改変 "base62" を使用します
{
"localDeclarations": {
"nameMangling": "base62"
}
}
// 名前改変 "sequential" を使用する際に
// 最上位の識別子 "myFunc" および "acmeVar" を保持しておきます
{
"localDeclarations": {
"nameMangling": "sequential",
"excludeIds": [ "myFunc", "acmeVar" ]
}
}
処理対象から除外する識別子が 1 つである場合は、配列の区切り文字を省略できます。
{
"localDeclarations": {
"nameMangling": "base62",
"excludeIds": "getName"
}
}
excludeIds
オプションでは、名前変更対象からファイル レベルのすべての宣言の識別子を除外するショートカットとして、"*" 文字列を使用できます。
{
"localDeclarations": {
"nameMangling": "base62",
"excludeIds": "*"
}
}
より柔軟性を与えるために、文字列ではなく正規表現を使用することができます。識別子パターンが "r:" で始まっている場合、JSDefender はそれを、変数名と照合する正規表現であると解釈します。たとえば、次の構成は、識別子に her
が含まれるすべての宣言の名前変更を許可しません。
{
"localDeclarations": {
"nameMangling": "base62",
"excludeIds": "r:her"
}
}
次の例では、Func
で終わる識別子が除外されます。
{
"localDeclarations": {
"nameMangling": "base62",
"excludeIds": "r:Func$"
}
}
シナリオによっては、特定の最上位の識別子の名前を変更すると、実行されるコードが失敗する可能性があるため、名前を保持しておく必要があります。この目的のために、関数識別子 getName
を保持したいとします。保持する ID の名前を一覧表示するには、localDeclarations
設定の excludeIds
プロパティを使用します。
jsdefender.config.json
:
{
"inputs": ["file1.js", "file2.js"],
"settings": {
"localDeclarations": {
"nameMangling": "sequential",
"excludeIds": ["getName"]
},
"stringLiterals": false
}
}
保護された出力に反映されているとおり、JSDefender は getName
を保持しています。
file1.js
:
function _0x000000() {
var _0x000001 = getName(); // 保持される ID
return "Hello, " + _0x000001 + ", from multifile demo!";
}
file2.js
:
function getName() {
// 保持される ID
return "Developer";
}
var _0x000003 = document.getElementById("msg");
_0x000003.textContent = _0x000000();
lexical-map.json
ファイルが既定で作成されます。この機能の詳細については、語彙割り当てセクションを参照してください。selfDefending
この保護オプションは、ブール値、0 ~ 4 の整数、または構成オブジェクトを受け付けます。値 false
および 0
が保護を無効にするのに対し、true
(1 と等価)と 0 以外の整数は保護を有効にします。
次のプロパティを持つ構成オブジェクトを使用することもできます。
-
level
:0 ~ 4 の整数。 -
errorScript
:任意の文字列。このプロパティには、有効な JavaScript コード スニペットを指定できます。このスクリプトは、コードが改ざんされたことが保護によって検出されたときに実行されます。詳細については、エラー スクリプト セクションを参照してください。
1
を指定すると単一のラッパーが使用され、2
、3
、4
を指定すると、マトリョーシカ人形のようにコードの周囲にラッパーが追加されます。例:
// 自己防御型の保護を有効にします(No. 1)
{
"selfDefending": true
}
// 自己防御型の保護を有効にします(No. 2)
{
"selfDefending": 1
}
// 自己防御型の保護を有効にします(No. 3)
{
"selfDefending": {
"level": 1
}
}
// 3 重の自己防御型の保護を有効にします
{
"selfDefending": 3
}
// ... または
{
"selfDefending": {
"level": 3
}
}
// 2 重の保護(エラー メッセージあり)
{
"selfDefending": {
"level": 3,
"errorScript": "alert('Code tampered!!!')"
}
}
この特別な保護には、保護されたコードが元のコードより何倍も長くなるという代償が伴います。入れ子になったコードの整合性がチェックされるため、関数実行のオーバーヘッドがかなり増える可能性があります。
JSDefender は、一度きりの実行用として認識された即時関数にのみこの保護を適用します。他の関数(または関数のような構成体)がこの保護オプションを使用できるようにするには、コードに明示的なインラインの保護ディレクティブを追加します。例を挙げると、まず、構成ファイルで selfDefending
を true
に設定していても、次の最上位関数は保護されません。
function add(a, b) {
return a + b;
}
この関数を保護するには、明示的に保護を有効にします。
function add(a, b) {
"@jsdefender { selfDefending: true }";
return a + b;
}
名前付き構成セット
保護の変換の一連のオプションを定義できる場所は、構成の settings
プロパティだけではありません。部分的な保護を実行するには、インライン ディレクティブを使って、ソース コード内の複数の場所でそれらのオプションを宣言します。コードでは、適切に構成された少数の構成オプション セットのみを使用するのが普通です。コードの至るところで同じオプション セットを何度も使用するのは、非常に非効率的でエラーが発生しやすくなります。
このため、JSDefender では、名前付き構成セットを利用できるようにしています。オプションの namedSets
プロパティはハッシュ オブジェクトです。このオブジェクト内の各プロパティ名は一意の構成キーです。プロパティ値は、settings
と同じ構造を持つオブジェクトであり、キーに関連付けられている一連の保護の変換を宣言します。例を見てみましょう。
{
"namedSets": {
// パフォーマンスへの影響を最小限に抑えます
"light": {
"booleanLiterals": true
},
// パフォーマンスへの影響は軽微です
"medium": {
"booleanLiterals": {
"randomize": true
},
"integerLiterals": {
"radix": "hexadecimal"
},
"stringLiterals": true
},
// より強力な保護。ただし、パフォーマンスに影響します
"heavy": {
"booleanLiterals": {
"randomize": true
},
"integerLiterals": {
"radix": "hexadecimal"
},
"stringLiterals": true,
"propertyIndicrection": true,
"localDeclarations": {
"nameMangling": "sequential"
},
"controlFlow": true
}
}
}
この構成スニペットでは 3 つの名前付きセット、light
、medium
、heavy
を指定しています。これらのキーの値を何度も使用する代わりに、プロパティ名を使用するだけでよいのです。いくつかの例を見てみましょう。
inputs
セクションに、特定のファイルに適用する保護設定を定義できます。
{
"inputs": [
"functions.js",
{
"in": "code.js",
"protection": "medium"
},
{
"in": "myIP.js",
"protection": "heavy"
}
]
}
この入力宣言は、code.js
ファイルで medium
設定を使用する一方で、myIP.js
で heavy
オプションを使用するように指定しています。 myIP.js
内のあるコード セクションは、真の知的財産を含んでいないため、medium
設定と併用できるとします。コードでは、インラインの保護ディレクティブを使用して、レベルを設定することができます。
function mySimpleFunctionWithNoRealIP() {
"@jsdefender medium"; // ここで "medium" オプションを設定します
// ...
}
後で、medium
では 8 進数の整数リテラルを使用すると決めた場合、この変更は、構成プロパティ namedSets
内の単一の場所で実行できます。
{
"namedSets": {
// ...
"medium": {
"booleanLiterals": {
"randomize": true
},
"integerLiterals": {
"radix": "octal" // この行を変更しました
},
"stringLiterals": true
},
// ...
}
}
特別な名前付きセット
JSDefender には、構成ファイル内とインラインの保護ディレクティブ内の両方で、名前付きセットのキーが必要とされるあらゆる場所に適用できるいくつかの特別な名前付きセットが定義されています。
-
off
:保護を無効にします。 -
disable-inline
:インラインの保護ディレクティブを無視します。 -
default
:JSDefender の既定の構成オプションを使用します。
構成のマージ
JSDefender は実行される際、コードの特定部分に適用する保護構成設定を決定するために、次の手順を使用します。
- JSDefender が構成ファイルを読み取る:
- コマンド ライン オプションで明示的な構成ファイルが指定された場合、JSDefender はそのファイルを読み取ります。
- 明示的に指定されておらず、現在の作業ディレクトリに
jsdefender.config.json
ファイルが存在する場合は、JSDefender はそのファイルを取り込みます。 - 上記以外の場合、JSDefender は既定の設定を使用します。
- JSDefender は、上記のステップで読み取られた構成オプションを、コマンド ライン オプションに指定された構成オプションで上書きします。このため、コマンド ライン引数は構成ファイルの設定よりも優先されます。
- 入力ファイルごとに最上位の構成設定を選択する:
- 入力ファイルの設定で
protection
プロパティが使用されている場合、JSDefender はそのプロパティに指定された名前付きセットを使用します。 - そのプロパティが使用されていない場合は、ステップ 1 と 2 で計算された構成の
settings
部分の保護を使用します。
- 入力ファイルの設定で
- 特定の入力ファイルに最上位のインライン保護構成を適用する:
protection
に対してdisable-inline
を指定していない限り、最上位のインラインの保護ディレクティブは、直前のステップで計算された最上位のファイル保護設定とマージされます。インラインの保護ディレクティブは、最上位のファイル設定よりも優先されます。 - 入れ子になったインライン保護構成を適用する:入れ子になったスコープ(例:関数内のスコープ)のインライン保護構成ディレクティブは、その親スコープとマージされます。入れ子になったスコープが優先されます。