Skip to content

MS_ConfigEncryption

nishi_74322014 edited this page Aug 21, 2026 · 1 revision

*.configの暗号化

概要

  • .NET の config

  • DevPartner の解析結果で気がついたのだけど、
    *.config の RSA 暗号化は connectionStringSection 以外も可能らしい。

補足(本ページの対象は .NET Framework): 本ページが扱う
**保護された構成(Protected Configuration)**は、
System.Configuration の仕組みであり、
.NET Core 以降には存在しない

【.NET Framework】
   Web.config / App.config
     → aspnet_regiis で【セクション単位に暗号化】できる ★ 本ページ

【.NET(Core 系)】
   appsettings.json
     → 【暗号化の仕組みはない】
     → 代わりに【秘密情報を config に置かない】設計にする
        ・環境変数
        ・User Secrets(開発時)
        ・【Key Vault / Secrets Manager】(本番)★
        ・マネージド ID(そもそも秘密情報を持たない)

設計思想が「隠す」から「持たない」へ移った——
というのが最も重要な変化である。
詳細は FaaS config にも記した。

本ページは、既存の .NET Framework 資産を保守する際に必要な知識
として読むのが妥当である。

対象

ファイル

app.config のビルド出力が、*.exe.config になる。

ASP.NET の Web アプリケーション毎の設定

マシンの設定として、暗号化・復号化、署名・検証のキーを含む。

その他、任意の*.configファイル

appSettingsSection を切り出した *.config など。

Section

通常、暗号化の必要な機密情報を収めた Section を aspnet_regiis.exe で暗号化。

appSettings

カスタム アプリケーションの設定

connectionStrings

DBMS などへの、接続文字列

identity

偽装資格情報

sessionState

セッション状態プロバイダ用の接続文字列

暗号化できないSection

  • 以下の Section は通常、暗号化が出来ない。

    • processModel
    • runtime
    • mscorlib
    • startup
    • system.runtime.remoting
    • configProtectedData
    • satelliteassemblies
    • cryptographySettings
    • cryptoNameMapping
    • cryptoClasses
  • ただし、aspnet_setreg.exe ツールを使用すれば暗号化が可能。

補足(なぜ暗号化できないセクションがあるのか): 一覧の顔ぶれには
理由がある

【暗号化できない理由】

 ① 【暗号化の仕組みより先に読まれる】
     configProtectedData … 復号プロバイダの定義そのもの
     cryptographySettings / cryptoClasses … 暗号アルゴリズムの定義
       → これを暗号化すると【復号できなくなる】(鶏と卵)★

 ② 【CLR の起動前に、ネイティブ コードが読む】
     startup / runtime / mscorlib
       → マネージド コードの復号処理が動く前に必要

 ③ 【IIS のワーカー プロセス起動時に読まれる】
     processModel … プロセスの実行 ID を含む
       → w3wp.exe が起動する前に必要

aspnet_setreg.exe が別途用意された理由も、ここにある。

aspnet_setreg は【レジストリに DPAPI で暗号化して格納】し、
config からは参照だけを書く

  <processModel userName="registry:HKLM\SOFTWARE\MyApp\ASPNET_SETREG,userName"
                password="registry:HKLM\SOFTWARE\MyApp\ASPNET_SETREG,password" />

 → config 側は【平文の参照】。実際の値はレジストリにある
 → セクションの暗号化とは【別の仕組み】である ★

identity セクションは、
ASP.NETとSSRSの連携と認証 で触れた
<identity impersonate="true" userName="..." password="..." />
平文パスワード問題に直結する。
現在は gMSA / マネージド ID を使い、
そもそもパスワードを config に書かない
のが正しい。

方法

以下の方法で暗号化できる。

プロバイダ

CNG のプロバイダを使用している模様。

キーコンテナ(鍵コンテナ)

  • CNG キーコンテナ(鍵コンテナ)を使用する。

    • このため、キーコンテナ(鍵コンテナ)のアクセス権云々でハマることがある模様。

    • また、開発サーバから運用サーバへのデプロイのシナリオ、
      Web ファームでの RSA 暗号化シナリオでは、
      カスタムの RSA 暗号鍵をエクスポート&インポートすることで対応する。

  • 参考

補足(「アクセス権云々でハマる」の中身): 原文が記録している
このハマりどころは、実務で最も頻繁に起きる問題なので具体化しておく。

【暗号化と復号は別の主体が行う】

  暗号化: 開発者 / 配置担当者(管理者権限)
             aspnet_regiis -pe ... を実行
  復号 : 【IIS のアプリケーション プール ID】(実行時)
             → w3wp.exe が鍵コンテナを読む

  → 【復号する側に鍵コンテナへのアクセス権がないと落ちる】★
# 鍵コンテナへのアクセス権を付与する(これを忘れて落ちる)
aspnet_regiis -pa "NetFrameworkConfigurationKey" "IIS APPPOOL\MyAppPool"

典型的なエラー:

「構成セクションを復号化できませんでした。
  RSA キー コンテナーを開くことができませんでした。」
   → -pa でアクセス権を付与していない
   → またはアプリケーション プール ID を変更した後に付け直していない ★

Web ファーム(複数台)での運用:

【既定の鍵はマシン固有】
   → A サーバで暗号化した config は、B サーバでは復号できない

【対応】
   ① カスタムの鍵コンテナを作る
        aspnet_regiis -pc "MyKeys" -exp
   ② 【エクスポートする】(-exp を付けないとエクスポートできない)★
        aspnet_regiis -px "MyKeys" keys.xml -pri
   ③ 全サーバにインポート
        aspnet_regiis -pi "MyKeys" keys.xml
   ④ 各サーバでアクセス権を付与
        aspnet_regiis -pa "MyKeys" "IIS APPPOOL\MyAppPool"
【運用上の注意】
   ・エクスポートした keys.xml は【秘密鍵そのもの】
      → 安全に扱い、作業後は確実に破棄する ★
   ・鍵を失うと復号できない= config を作り直すことになる
   ・この煩雑さが、現在 Key Vault 等の外部保管に移った理由でもある

移行メモ: 原文は「CNG のプロバイダを使用している模様」と
推測形で書いているが、
RsaProtectedConfigurationProvider が使うのは
従来の CryptoAPI(CAPI)の RSA キー コンテナ
である
(CNG ではない)。
格納場所は以下になる。

%ALLUSERSPROFILE%\Microsoft\Crypto\RSA\MachineKeys\   (マシン レベル)
%APPDATA%\Microsoft\Crypto\RSA\                        (ユーザー レベル)

RSAProtectedConfigurationProvider

  • 既定のプロバイダ
  • RSA 公開鍵暗号化を使用して、データの暗号化と暗号化解除

DPAPIProtectedConfigurationProvider

  • Windows データ保護 API (DPAPI) を使用して、データの暗号化と暗号化解除

補足(2 つのプロバイダの使い分け):

RSAProtectedConfigurationProvider DPAPIProtectedConfigurationProvider
方式 RSA(鍵コンテナ) DPAPI(マシン鍵 / ユーザー鍵)
鍵の持ち出し できる(エクスポート/インポート) できない
Web ファーム (鍵を配る) 不可(1 台ごとに暗号化が必要)
設定の手間 鍵コンテナの作成・権限付与 少ない(すぐ使える)
既定 こちら
【使い分け】
   1 台構成・簡便さ優先        → DPAPI
   Web ファーム・配置の自動化   → RSA ★

DPAPI の useMachineProtection に注意する。

<configProtectedData>
  <providers>
    <add name="MyDPAPI"
         type="System.Configuration.DpapiProtectedConfigurationProvider, ..."
         useMachineProtection="true" />   <!-- ← マシン単位。既定は true -->
  </providers>
</configProtectedData>
useMachineProtection="true"  … 【同じマシンなら誰でも復号できる】
                                 → 他のアプリからも読める(防御が弱い)
useMachineProtection="false" … ユーザー単位
                                 → サービス アカウントのプロファイルが要る

ツール、API

aspnet_regiisコマンドライン・ツール

補足(aspnet_regiis の使い方と落とし穴): 原文が記録している
**「Web.config にリネームして暗号化する」**という回避策は、
実務でよく使われる手法である。

# 物理ディレクトリを指定して暗号化する(-pef)
aspnet_regiis -pef "connectionStrings" "C:\MyApp"
#                   ↑ セクション名        ↑ Web.config があるフォルダ

# 仮想パスを指定する場合(-pe)
aspnet_regiis -pe "connectionStrings" -app "/MyApp"

# 復号
aspnet_regiis -pdf "connectionStrings" "C:\MyApp"
【App.config を暗号化する手順(原文の回避策)】
  ① App.config → Web.config にリネーム
  ② aspnet_regiis -pef で暗号化
  ③ Web.config → App.config に戻す
  ④ ビルド出力の MyApp.exe.config として配置

 → デスクトップ アプリの config を暗号化する定石

落とし穴:

① 【32bit / 64bit で aspnet_regiis の場所が違う】
     %WINDIR%\Microsoft.NET\Framework\v4.0.30319\aspnet_regiis.exe
     %WINDIR%\Microsoft.NET\Framework64\v4.0.30319\aspnet_regiis.exe
     → 鍵コンテナは共通だが、実行する側は環境に合わせる

② 【暗号化した config はソース管理に入れない】
     → 環境ごとに鍵が違うため、他環境で復号できない
     → 平文をテンプレートとして持ち、【配置時に暗号化】する ★

③ 【暗号化しても「隠している」だけ】
     → サーバに管理者権限で入れる人は復号できる
     → アプリ自身が復号できる= アプリを乗っ取れば読める
     → 【防御の深さの 1 層】であって、万能ではない

暗号化後の Web.config は以下のようになる

<connectionStrings configProtectionProvider="RsaProtectedConfigurationProvider">
  <EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element" ...>
    <CipherData><CipherValue>rXbT8h...</CipherValue></CipherData>
  </EncryptedData>
</connectionStrings>

アプリ側のコードは変更不要である。

// 暗号化されていても、読み方は同じ(実行時に自動で復号される)★
var cs = ConfigurationManager.ConnectionStrings["MyDb"].ConnectionString;

この**「アプリのコードを変えずに済む」**点が、
保護された構成の最大の利点である。

API(自作プログラム)

SectionInformation.ProtectSection メソッド、Configuration.Save メソッドを使用する。

補足(API で行う場合の実装): 原文が挙げる 2 つのメソッドで、
インストーラや初回起動時に自動暗号化できる。

static void ProtectSection(string sectionName)
{
    var config = ConfigurationManager.OpenExeConfiguration(
        ConfigurationUserLevel.None);
    var section = config.GetSection(sectionName);

    if (section != null && !section.SectionInformation.IsProtected)
    {
        section.SectionInformation.ProtectSection(
            "RsaProtectedConfigurationProvider");
        section.SectionInformation.ForceSave = true;   // ← これが要る ★
        config.Save(ConfigurationSaveMode.Modified);
    }
}
【注意点】
 ・`ForceSave = true` を付けないと、
   変更がないと判断されて【保存されない】ことがある
 ・config ファイルへの【書き込み権限】が必要
    → Program Files 配下は管理者権限が要る
 ・IIS 配下で実行時に暗号化すると、
   【config の変更でアプリケーション プールが再起動する】★
    → 初回アクセスが再帰的に走らないよう注意

ProtectSection の対称として UnprotectSection がある
(復号)。運用手順書に復号方法も残しておくこと。

補足(現在の推奨:そもそも config に秘密情報を置かない): 本ページの
技術は .NET Framework では有効だが、
現在の設計指針を明記しておく

① 開発時 — User Secrets

dotnet user-secrets init
dotnet user-secrets set "ConnectionStrings:MyDb" "Server=...;"
・プロジェクト外(ユーザー プロファイル配下)に保存される
・【ソース管理に絶対に入らない】★
・IConfiguration から通常どおり読める

② 本番 — Key Vault / Secrets Manager

builder.Configuration.AddAzureKeyVault(
    new Uri("https://myvault.vault.azure.net/"),
    new DefaultAzureCredential());
// → 以降、Configuration["MyDb"] で読める(値は Key Vault から)

③ 最善 — 秘密情報そのものをなくす

// 接続文字列にパスワードを含めない(Entra ID 認証)
"Server=tcp:myserver.database.windows.net;Database=MyDb;
 Authentication=Active Directory Default;"
【マネージド ID の利点】
   ・パスワードが【どこにも存在しない】
   ・ローテーション不要
   ・漏洩のしようがない ★
   → 暗号化して隠すより、根本的に強い

④ 漏洩を検知する仕組みを持つ

・GitHub の Secret Scanning / Push Protection
・git-secrets / gitleaks を CI に組み込む
・【一度リポジトリに入った秘密情報は、履歴から消えない】
  → 漏れたら【必ずローテーションする】(履歴の書き換えより確実)★

参考

Microsoft Learn

その他

Microsoft Learn(現在の方式)


Tags: 移行, .NET開発, セキュリティ, 暗号化

NetDevInfraWiki

マイクロソフト系技術情報 Wiki
Open 棟梁 Wiki

(未着手)

開発基盤部会 Wiki

移行管理: DONETODO

Clone this wiki locally