Neutral Scent

App developments & Gadgets

ストアアプリのPasswordVaultで安全にユーザー名とパスワードを保管する


どうもこんにちは、ようやくLumia 820を購入したもののWP8 SDKに未だ全く触れていないkaorunです。
とはいえ、このエントリは Windows 8 Store apps Advent Calendar の12日目のエントリです。
ここのところ派手な持ちネタもないので、今回はクリスマスとは思えないような堅めの線で…。
WinRTでWebサービスにアクセスするようなMetro改めWindows Storeアプリを開発していると、対象になるWebサービスがOAuthなどWeb上で独立した認証システムに対応していない場合や、認証システムで公開されたAPI以上の機能をスクレイピングで実現しなくてはいけなくなることがままあります。
この時、アプリ側でサービスのユーザー名をパスワードを管理しなくてはならなくなるわけですが、さてこの値をどこに保存したものか、ということになるわけです。
ストアアプリの設定といえば、ApplicationData.Current.LocalSettingsやApplicationData.Current.RoamingSettingsが定番ですが、ここに平文のパスワードを保存してしまっても安全でしょうか?
少なくとも私が手元で試した範囲では、LocalSettings, RoamingSettings共にローカルファイルシステム上のファイルに平文でデータが保存されるようです。

それはちょいといただけないですよね?
このような時のために用意されているのがWindows.Security.Credentials名前空間です。
使い方はわりと簡単で、基本的にはPasswordVaultクラスのインスタンスを作成してPasswordCredentialを追加してやる、というプロセスです。

単純に実装するとこんな感じ。



private static readonly string CREDENTIAL_ACCOUNT = "App13_UserAccount"; // アプリケーション固有の識別文字列
internal string Username = "kaorun";
internal string Password = "passwordtext";

private void SavePassword()
{
PasswordVault vault = new PasswordVault();
PasswordCredential cred = new PasswordCredential(CREDENTIAL_ACCOUNT, Username, Password);
vault.Add(cred);
}

private void LoadPassword()
{
PasswordVault vault = new PasswordVault();
PasswordCredential cred = vault.Retrieve(CREDENTIAL_ACCOUNT, Username);
Password = cred.Password;
}

上の例ではエラー処理もしてませんし、Usernameは読み込んでいませんので、RoamingSettingsあたりに書いてる感じでしょうか。
しかし、PasswordCredentialにはUserNameも保存されていますから、PasswordVaultからUsernameとPasswordをまとめて取得するようにして、もう少しきちんと書くとこんな感じです。


private void SaveUserCredentials()
{
var vault = new PasswordVault();
try
{
if (string.IsNullOrWhiteSpace(Username))
{
var c = vault.FindAllByResource(CREDENTIAL_ACCOUNT).FirstOrDefault();
if (c != null)
vault.Remove(c);
}
else if (string.IsNullOrWhiteSpace(Password))
{
var c = vault.Retrieve(CREDENTIAL_ACCOUNT, Username);
vault.Remove(c);
}
else
{
var c = new PasswordCredential(CREDENTIAL_ACCOUNT, Username, Password);
vault.Add(c);
}
}
catch(Exception)
{
}
}

private void LoadUserCredentials()
{
var vault = new PasswordVault();
try
{
// FindAllByResourceではパスワードは取得されず空のまま
var credexist = vault.FindAllByResource(CREDENTIAL_ACCOUNT).FirstOrDefault();
if (credexist != null)
{
Username = credexist.UserName;
var credpass = vault.Retrieve(CREDENTIAL_ACCOUNT, Username);
Password = credpass.Password;
}
}
catch (Exception)
{
// 初回起動時などユーザー情報が存在しない場合FindAllByResourceで例外が発生
Username = string.Empty;
Password = string.Empty;
}
}

例外処理などはお好みで。
まぁ、RoamingSettingsと比べればひと手間増えますが、こんな感じで実装完了です。
公式にユーザー情報を管理してくれるAPIが用意されていると暗号がハッシュが云々ともやもや悩まずに済んで、アプリの実装に集中できるのでありがたいですね。
それではみなさんよいクリスマスを〜。

基礎から学ぶ Windowsストアアプリ開発

基礎から学ぶ Windowsストアアプリ開発

Windows8ストア アプリ開発入門

Windows8ストア アプリ開発入門