ワンクリックで
dotnet-wpf-secure-config
WPF アプリに DPAPI 暗号化された設定管理を追加し、資格情報を安全に保存する。 WPF アプリケーションで暗号化済み設定を用意するとき。
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
WPF アプリに DPAPI 暗号化された設定管理を追加し、資格情報を安全に保存する。 WPF アプリケーションで暗号化済み設定を用意するとき。
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
こんなときに使う: Ubuntu / Linux サーバーに SSH で接続し、sudo、systemd サービス、HTTP 監視を一連で安全に進めたいとき。 接続前に SSH_AUTH_SOCK を含む認証状態を固定し、認証で止まらずに サーバー接続・権限確認・サービス起動・停止・再起動・状態確認を一気に行いたいとき。
調査→修正→検証→ふりかえり/後続 Issue 化までを 1 つの改善ループで回したいときに使う。 実装前の再現確認や、review 指摘・検証結果をもとに次のアクションへつなぐ。
こんなときに使う: 現在の会話内容をもとに、実装・エージェント発注に直結する PRD を作りたいとき。 追加のインタビューはせず、すでに会話に出ている内容だけから構成する。 情報が不足している場合は捏造せず「未確定」として明示する。
こんなときに使う: ユーザーが「インタビューして」「質問して」「設計を詰めたい」などと言ったら使う。 計画や設計の重要な判断軸を洗い出し、具体例・反例・影響範囲まで深掘りして要件整理に落とす。
Copilot の custom skill / agent / repository instructions の作成・改善・構造確認を 1 つの入口にまとめる。複合スキルとして、対象に応じて適切な authoring ルートへ 分けつつ、実行時のモデル呼び出しを抑止してルーティングを優先する。試作から `plugins/*` 配布へ昇格するときの name / description 整備も扱う。skill / agent / repo-wide instructions / path-specific instructions を新規作成したいとき、既存定義を育てたいとき、公開前に責務や導線を確かめたいとき。
新しい custom agent を既存 agent 群と同じ型で立ち上げる。agent の新規追加、役割分離のための専門 agent 作成、既存群の隙間を埋めたいとき。
| name | dotnet-wpf-secure-config |
| description | WPF アプリに DPAPI 暗号化された設定管理を追加し、資格情報を安全に保存する。 WPF アプリケーションで暗号化済み設定を用意するとき。 |
.NET WPFアプリケーションにWindows DPAPI暗号化設定管理を追加するワークフロー:資格情報の暗号化保存、%LOCALAPPDATA%へのJSON永続化、拡張可能なAppConfigModel、DI登録。
ゴール駆動で使うため、最初に達成したいゴール、成功条件、確認手段を短く固定します。
このスキルを使用する場面:
SecureConfigServiceを作成する場合Infrastructure/Configuration/層を新しいWPFプロジェクトに移植する場合appsettings.json資格情報をDPAPI暗号化に置き換える場合このスキルが前提となるスキル:
dotnet — WPF 連携系の入口dotnet-wpf-mvvm-patterns — このセキュリティ基盤を利用する UI 層dotnet — WPF 連携系の入口dotnet-wpf-mvvm-patterns — このセキュリティ基盤を利用する UI 層git-commit — 生成コードを原子的変更で管理config.jsonファイルで全サービスの資格情報を管理(継続は力)設定の暗号化に必要なフォルダ構造とNuGet依存関係を初期化する場合に使用。
Infrastructure/Configuration/フォルダを作成し、必要なパッケージをインストールする。
YourApp/
└── Infrastructure/
└── Configuration/
├── DpapiEncryptor.cs # DPAPI暗号化/復号化ユーティリティ
├── ISecureConfigService.cs # サービスインターフェース
├── SecureConfigService.cs # JSON永続化 + 暗号化
└── AppConfigModel.cs # 拡張可能な設定ルート
# DPAPI(System.Security.Cryptography.ProtectedData)に必要
Install-Package System.Security.Cryptography.ProtectedData
# DI登録に必要
Install-Package Microsoft.Extensions.DependencyInjection
Values: 基礎と型 / 成長の複利
すべての設定モデルが共有するWindows DPAPI暗号化ユーティリティを追加する場合に使用。
Encrypt、Decrypt、MaskSensitiveメソッドを持つ静的暗号化ヘルパーを作成する。
// Infrastructure/Configuration/DpapiEncryptor.cs — シグネチャ概要
public static class DpapiEncryptor
{
private static readonly byte[] Entropy = Encoding.UTF8.GetBytes("YourApp_Config_Salt_2026");
public static string Encrypt(string plainText) // DPAPI Protect → Base64
public static string Decrypt(string encryptedText) // Base64 → DPAPI Unprotect
public static string MaskSensitive(string value) // ログ用マスク "abcd****"
}
完全な実装は references/detailed-patterns.md を参照。
なぜ完全なエラーハンドリングが必要か:CryptographicExceptionはユーザーAで暗号化したデータをユーザーBが復号化しようとした場合(プロファイル移行後など)に発生する。これをキャッチしないと、再入力を促す代わりにアプリが起動時にクラッシュする。
Values: ニュートラル / 基礎と型
拡張可能な設定データ構造を定義する場合に使用。
AppConfigModelをルートとし、ドメイン固有モデルをプロパティとして追加する。各統合スキルが独自のモデルを追加する。
AppConfigModel.cs — 拡張可能なルート:
namespace YourApp.Infrastructure.Configuration
{
/// <summary>
/// アプリケーション設定ルート
/// 新サービス統合時にプロパティを追加する
/// </summary>
public class AppConfigModel
{
// ✅ 新サービス統合時にここにプロパティを追加
// public NewIntegrationConfigModel ServiceA { get; set; } = new(); // 新しい統合が追加
// public NewIntegrationConfigModel ServiceB { get; set; } = new(); // 新しい統合が追加
public string Version { get; set; } = "1.0";
}
}
設定モデルのパターン — 各サービスは以下のテンプレートに従う:
/// <summary>
/// ドメイン固有設定モデルのテンプレート
/// "Service"を統合名に置き換える
/// </summary>
public class ServiceConfigModel
{
public string Endpoint { get; set; } = string.Empty;
public string CredentialEncrypted { get; set; } = string.Empty;
public string GetDecryptedCredential()
=> DpapiEncryptor.Decrypt(CredentialEncrypted);
public void SetCredential(string plainCredential)
=> CredentialEncrypted = DpapiEncryptor.Encrypt(plainCredential);
public bool IsValid()
=> !string.IsNullOrWhiteSpace(Endpoint)
&& !string.IsNullOrWhiteSpace(CredentialEncrypted);
}
なぜ拡張可能なルートにするか:OracleとDifyを同時に使用する場合、AppConfigModelが両方を保持する:
{
"OracleDb": {
"UserId": "SCOTT",
"PasswordEncrypted": "AQAAANCMnd8B..."
},
"DifyApi": {
"BaseUrl": "https://api.dify.ai",
"ApiKeyEncrypted": "AQAAANCMnd8B..."
},
"Version": "1.0"
}
Values: 成長の複利 / 基礎と型
DPAPI暗号化資格情報サポート付きのJSON永続化サービスを実装する場合に使用。
ISecureConfigServiceインターフェースとSecureConfigService実装を作成する。サービスはAppConfigModel全体を管理し、各統合向けの型付きload/saveメソッドを公開する。
ISecureConfigService.cs:
using System.Threading.Tasks;
namespace YourApp.Infrastructure.Configuration
{
public interface ISecureConfigService
{
bool ConfigExists();
Task ResetConfigAsync();
// ✅ 統合ごとに型付きload/saveメソッドを追加
// Task<OracleConfigModel> LoadOracleConfigAsync();
// Task SaveOracleConfigAsync(OracleConfigModel config);
// Task<DifyConfigModel> LoadDifyConfigAsync();
// Task SaveDifyConfigAsync(DifyConfigModel config);
}
}
SecureConfigService.cs:
// Infrastructure/Configuration/SecureConfigService.cs — シグネチャ概要
public class SecureConfigService : ISecureConfigService
{
// ✅ "YourAppName"を変更 — Step 6参照
// 設定を %LOCALAPPDATA%/YourAppName/config/config.json に保存
public bool ConfigExists()
public Task ResetConfigAsync()
// ✅ 統合ごとに型付きload/saveメソッドを追加
protected Task<AppConfigModel> LoadAppConfigAsync()
protected Task SaveAppConfigAsync(AppConfigModel appConfig)
}
完全な実装は references/detailed-patterns.md を参照。
なぜLoad/SaveAppConfigAsyncがprotectedか:統合スキル(Oracle、Dify)が型付きメソッドを追加してこれらの内部ヘルパーを呼び出す。protectedにすることでサブクラス化を可能にしつつ、公開APIをクリーンに保つ。
Values: 基礎と型 / 継続は力
SecureConfigServiceをWPFアプリケーションの依存性注入に接続する場合に使用。
App.xaml.csでISecureConfigServiceをシングルトンとして登録する:
// App.xaml.cs
using Microsoft.Extensions.DependencyInjection;
public partial class App : Application
{
private ServiceProvider? _serviceProvider;
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var services = new ServiceCollection();
// ✅ シングルトン:1つの設定ファイル、1つのサービスインスタンス
services.AddSingleton<ISecureConfigService, SecureConfigService>();
// 統合スキルがここにサービスを追加:
// services.AddSingleton<DifyApiService>(); // Dify統合
// services.AddTransient<ISofRepository, SofDatabaseOracle>(); // Oracle統合
_serviceProvider = services.BuildServiceProvider();
}
protected override void OnExit(ExitEventArgs e)
{
_serviceProvider?.Dispose();
base.OnExit(e);
}
}
なぜシングルトンか:すべての統合が同じconfig.jsonファイルを共有する。複数インスタンスは同時書き込みの競合リスクを生む。
Values: 成長の複利 / 基礎と型
生成コードを本番デプロイ用に準備する場合に使用。
出荷前に以下のプレースホルダーを置き換える:
| 項目 | ファイル | 変更内容 | 未変更時の影響 |
|---|---|---|---|
| アプリ名 | SecureConfigService.cs | "YourAppName" → 実際のアプリ名 | 設定が間違ったフォルダに保存 |
| ソルト | DpapiEncryptor.cs | Entropyのバイト配列値 | 共有ソルトで分離が弱まる |
| 名前空間 | 全.csファイル | YourApp → 実際の名前空間 | ビルドエラー |
カスタマイズ確認:
# プレースホルダーが残っていないか確認
Select-String -Path "Infrastructure/Configuration/*.cs" -Pattern "YourApp" -SimpleMatch
# カスタマイズ後の期待結果:0件
⚠️ 重要:暗号化済みデータがある状態でEntropy値を変更すると、既存の暗号化データは回復不可能になる。初回使用前に一度だけ設定すること。
Values: ニュートラル / 基礎と型
問題:資格情報が保存された後にDpapiEncryptorのソルト値を更新してしまう。
解決策:初回デプロイ前にエントロピー値を一度設定する。変更が必要な場合は、旧ソルトで復号化し新ソルトで再暗号化するマイグレーションを実装する。
問題:DataProtectionScope.LocalMachineは同じPC上の全ユーザーが復号化可能。
解決策:ユーザー単位の資格情報分離には常にDataProtectionScope.CurrentUserを使用する。
// ❌ 間違い — このPC上の全ユーザーが復号化可能
ProtectedData.Protect(data, entropy, DataProtectionScope.LocalMachine);
// ✅ 正しい — 暗号化したユーザーのみ復号化可能
ProtectedData.Protect(data, entropy, DataProtectionScope.CurrentUser);
問題:平文のパスワードやAPIキーをログファイルに書き込む。
解決策:ログ出力には常にDpapiEncryptor.MaskSensitive()を使用する。
// ❌ 間違い — ログに秘密情報が漏洩
logger.LogInformation($"Password: {password}");
// ✅ 正しい — マスク出力
logger.LogInformation($"Password: {DpapiEncryptor.MaskSensitive(password)}");
// 出力: "Password: abcd****"
問題:復号化エラーを無視して空文字列を返す。
解決策:エラーをユーザーに表示し、資格情報の再入力を促す。
何が問題か:平文のパスワードやAPIキーをソース管理対象の設定ファイルに保存すること。
なぜ問題か:リポジトリアクセス権を持つ全員が読める。デプロイ成果物も暗号化されていないことが多い。
正しいアプローチ:DpapiEncryptor + SecureConfigServiceを使い、暗号化値を%LOCALAPPDATA%に保存する。
何が問題か:oracle-config.json、dify-config.jsonなど統合ごとに別ファイルを作成すること。
なぜ問題か:ファイルI/Oロジックが重複し、競合状態が発生し、バックアップ/リセットが困難になる。
正しいアプローチ:サービスごとに型付きプロパティを持つ単一のAppConfigModelを1つのconfig.jsonに永続化する。
何が問題か:接続文字列やAPIキーをC#コードに直接埋め込むこと。
なぜ問題か:削除後もバージョン管理の履歴に資格情報が残る。
正しいアプローチ:実行時に常にISecureConfigServiceから読み取る。
Infrastructure/Configuration/フォルダをコピー(4ファイル)SecureConfigServiceコンストラクタのアプリ名を変更("YourAppName")DpapiEncryptorのエントロピー値を変更(アプリ固有のソルトを設定)AppConfigModelに統合用の設定モデルプロパティを追加ISecureConfigServiceとSecureConfigServiceに型付きload/saveメソッドを追加ISecureConfigServiceを登録DpapiEncryptor.Encrypt()で保存前に暗号化されているMaskSensitive()を使用しているDataProtectionScope.CurrentUserを使用している(LocalMachineではない)%LOCALAPPDATA%に保存されている(Windowsによるユーザー分離)| データ種別 | 暗号化? | 理由 |
|---|---|---|
| パスワード | ✅ 必須 | 認証資格情報 |
| APIキー | ✅ 必須 | サービスアクセストークン |
| トークン/秘密鍵 | ✅ 必須 | ベアラー資格情報 |
| URL/エンドポイント | ❌ 不要 | 公開情報 |
| ユーザーID/名前 | ⚠️ ポリシー依存 | 個人情報の可能性 |
| タイムアウト値 | ❌ 不要 | 非機密設定 |
| 項目 | 値 |
|---|---|
| 暗号化スコープ | ユーザー単位(CurrentUser) |
| 鍵管理 | 自動(Windows管理) |
| ポータブル? | ❌ 別PC・別ユーザーでは復号化不可 |
| クラウド同期? | ❌ 非対応(マシン鍵が異なるため) |
| バックアップ戦略 | リストア後に資格情報を再入力 |