コードセキュリティの強化
コードセキュリティを強化するためのベストプラクティスです。
Babel Obfuscator は、コードを堅牢に保護する強力なツールです。難読化の細部をすべて理解するには時間がかかるため、最小限の手間で最適な難読化結果を得るためのヒントをまとめました。これらの推奨事項に従えば、細部に迷い込むことなく、コードのセキュリティを大きく高められます。
強力な保護は多層防御から生まれます。単独で攻撃者を止めることを想定した変換は 1 つもありません。リネームは意図を隠し、暗号化はデータを隠し、制御フローと動的プロキシはロジックを隠し、実行時のチェックは改ざんとデバッグを抑止します。複数のレイヤーを組み合わせれば、1 つが破られてもほかのレイヤーが残ります。次のヒントは、各レイヤーが何を保護するかによって分類しています。
シンボルをリネームして再構成する
C# では型を internal にし、VB では Friend としてマークすることを検討する
型を internal(VB の用語では Friend)として宣言すると、その型は外部のアセンブリから参照できなくなり、難読化で安全にリネームできます。この手順により、コードを詮索の目から効果的に守れます。
Babel Obfuscator では、XML ルールを使用して、パブリックな型や、アセンブリの外部から参照できるシンボルをリネームできます。ただし、そのシンボルを外部のアセンブリが使用しているとアプリケーションが動作しなくなるため、慎重な確認が必要です。型を internal にする、または Friend としてマークする方法はセキュリティを高める簡単な手段ですが、パブリックシンボルのリネームを強制する場合は、十分な検討とテストが必要です。
リネームを強化する
既定のシンボルのリネームに加えて、いくつかのオプションを使うと、リネーム後のメタデータがかなり読みにくくなります。
- オーバーロードリネーム:関連のない多数のメンバーに 1 つの難読化された名前を再利用するため、メタデータとメンバーが 1 対 1 で対応しなくなります。
- 名前空間のフラット化:名前空間の階層をまとめ、デコンパイラーが手がかりにする構造上のヒントを消します。
- 仮想メンバーのリネーム:仮想メンバーとインターフェイスメンバーを、型階層全体で一貫してリネームします。
Babel CLI
babel.exe myapp.dll --overloaded --flatns --virtualmembers可能なかぎり依存関係をマージする
Babel Obfuscator では、参照アセンブリをターゲットアセンブリにマージできます。マージされた型をすべて internal 化すると、リネームされるシンボルの数が増え、攻撃者によるコードの解読が難しくなります。
定数データを隠す
文字列暗号化に HASH アルゴリズムを使用する
HASH 文字列暗号化アルゴリズムを使用すると、強力な保護が得られるうえ、難読化されたアセンブリのディスク上のサイズも小さくなります。この機能は、次のコマンドで有効にできます。
Babel CLI
babel.exe myapp.exe --stringencryption hashMSBuild の Babel タスク
<PropertyGroup>
<StringEncryption>hash</StringEncryption>
</PropertyGroup>インラインの値と配列を暗号化する
数値定数やインラインの配列には、しきい値、キー、マジックナンバー、ルックアップテーブルといった機密性の高いデータが含まれていることがよくあります。値と配列の暗号化は、それらのリテラルを IL から取り除いて実行時に復号するため、デコンパイラーには実際の数値や配列の内容が見えなくなります。
Babel CLI
babel.exe myapp.exe --values int32=on --values int64=on --values single=on --values double=on --values array=onマネージドリソースを暗号化する
リソース暗号化は、アセンブリ内の埋め込みリソースを圧縮しながら隠し、保護レイヤーをもう 1 つ追加します。この機能を有効にする前に、パフォーマンスへの影響を慎重に検討してください。
プログラムのロジックをわかりにくくする
制御フロー難読化を有効にする
制御フロー難読化は、メソッドの動作を変えずに、if 文を複雑にし、無関係な分岐を挿入し、複数の switch 命令を追加します。goto、if、switch、case、call といった特定の制御フローアルゴリズムを有効にすると、コードが最も効果的にスクランブルされます。次の推奨設定を使うと、効果的な制御フロー難読化を実現できます。
Babel CLI
babel.exe myapp.exe --controlflow goto=on --controlflow if=on --controlflow switch=on --controlflow case=on --controlflow call=onMSBuild の Babel タスク
<PropertyGroup>
<ControlFlowObfuscation>goto=true;if=true;switch=true;case=true;call=true;true</ControlFlowObfuscation>
<ControlFlowIterations>3</ControlFlowIterations>
</PropertyGroup>上記の制御フロー難読化の設定を有効にすると、コードが堅牢かつ効果的に難読化されます。それぞれのアルゴリズム(goto、if、switch、case、call)によって制御フローがより複雑で解析しにくくなり、アプリケーションのセキュリティが高まります。
外部メソッド呼び出しに動的プロキシの生成を使用する
動的プロキシは、外部メソッドと内部メソッドの呼び出しを効果的に隠します。多くの場合、外部呼び出しに対して動的プロキシの生成を有効にするだけで、高いレベルの難読化が得られます。
babel.exe myapp.exe --proxy externalコードを暗号化する
コード暗号化は、ライセンスキーの検証など、機密性の高いデータを扱うメソッドを堅牢に保護します。ただし、コード暗号化はアプリケーションの速度に影響することがあるため、パフォーマンスへの影響に注意し、必要なメソッドだけに適用してください。
Babel Licensing Service と連携すると、アクティベーションライセンスやフローティングライセンスを使って、機能ベースのライセンスを柔軟に実現できます。つまり、ライセンスサービスを通じて暗号化キーを安全に配布し、許可されたユーザーに対してアプリケーションの特定の機能のロックを解除できます。
Babel Licensing Service とコード暗号化を組み合わせると、暗号化されたコード部分を利用できるのは、ライセンスを持つユーザーだけになります。暗号化キーはライセンスサービスを通じて安全に配布されるため、不正使用を防ぎ、アプリケーションの機能の整合性を確保できます。
実行時の攻撃を検出して抑止する
改ざん防止を有効にする
改ざん防止は、アセンブリに対する不正な変更をすべて検出します。攻撃者を抑止するために、実行の停止やユーザーへのエラー報告など、適切なアクションを選択してください。気づかれにくいアクションを選ぶと、改ざん防止の仕組みを回避することがより難しくなります。
babel.exe myapp.exe --tamperingdetectionデバッグ保護を有効にする
デバッグ保護は、アプリケーションがデバッガーやプロファイラーの下で実行されていることを検出します。これらは、実行時に復号される機密情報を攻撃者が観察するために使うツールです。上記の暗号化レイヤーと組み合わせることで、静的解析への対策だけでは残る隙をふさぎます。
babel.exe myapp.exe --antidebugging逆アセンブルツールとリフレクションツールをブロックする
2 つの軽量なスイッチで、手軽な調査を難しくできます。--ildasm は、MSIL 逆アセンブラー(ildasm.exe)がアセンブリを開けないようにアセンブリをマークします。--reflection は、リフレクションベースのツールがアセンブリのメタデータを一覧表示できないようにします。
babel.exe myapp.exe --ildasm --reflection配布に不要なものを取り除く
残したメタデータは、どれも手がかりになります。実行中のアプリケーションに必要のないものは除去してください。
- 不要な属性の除去:
--cleanattrsは、コンパイラーやツールのためだけに使われていたカスタム属性を除去します。 - デッドコード除去:
--deadcodeは、到達できないメソッドとメンバーを取り除きます。 - 列挙型の除去:
--enumremovalは、列挙型をその基になる定数に置き換えます。 - internal クラスのシール:
--sealは、継承されることのない internal 型をシールします。 - デバッグシンボルを配布しない:
.pdbファイルを付けずにデプロイし(--debugは使用しません)、行番号やローカル変数の名前を攻撃者が利用できないようにします。
推奨されるベースライン
次のコマンドは、上記のレイヤーを組み合わせた、強力で汎用的な構成です。機密性が高く、暗号化に値する特定のメソッドには、--msilencryption を追加してください。
babel.exe myapp.dll `
--overloaded --flatns --virtualmembers `
--stringencryption hash `
--values int32=on --values int64=on --values single=on --values double=on --values array=on `
--controlflow if=on --controlflow switch=on --controlflow case=on --controlflow chain=on `
--proxy external `
--resourceencryption `
--tamperingdetection --antidebugging --ildasm `
--cleanattrs --seal同等の MSBuild タスクのプロパティは次のとおりです。
<PropertyGroup>
<OverloadedRenaming>true</OverloadedRenaming>
<FlattenNamespaces>true</FlattenNamespaces>
<VirtualFunctions>true</VirtualFunctions>
<StringEncryption>hash</StringEncryption>
<ValueEncryption>int32=true;int64=true;single=true;double=true;array=true;true</ValueEncryption>
<ControlFlowObfuscation>if=true;switch=true;case=true;chain=true;true</ControlFlowObfuscation>
<DynamicProxy>external=true;true</DynamicProxy>
<ResourceEncryption>true</ResourceEncryption>
<TamperingDetection>true</TamperingDetection>
<DebuggingProtection>true</DebuggingProtection>
<SuppressIldasm>true</SuppressIldasm>
<CleanAttributes>true</CleanAttributes>
<SealClasses>true</SealClasses>
</PropertyGroup>サポートのためにマップファイルを保管する
強力な難読化を行うと、本番環境のスタックトレースは読めなくなります。ビルドごとに生成される難読化のマップファイルを保管しておけば、顧客の問題を調査するときに、配布したアセンブリの保護を弱めることなく、スタックトレースをデコードして元の名前に戻せます。
難読化されたアプリケーションをテストする:難読化の過程で生じた問題を見つけて対処するために、難読化されたアプリケーションを十分にテストすることが重要です。テストによって、コードの安定性と正常な動作を確認できます。FIPS が有効なホストにデプロイする場合は、FIPS 準拠も確認してください。いくつかの暗号化レイヤーは、実行時にプラットフォームの暗号化プロバイダーを通じて復号するためです。
これらの難読化のヒントに従うことで、Babel Obfuscator を効果的に活用し、コードのセキュリティを高められます。より詳しい情報と包括的な説明については、Babel Obfuscator のユーザーガイドを参照することをお勧めします。