私は、当座預金口座で最近の取引を行ってチェックする自動ツールを構築しようとしています(妻の購入をスパイしています笑)。このツールは、私のユーザー名とパスワードを SSL 経由で投稿し、銀行のページのコンテンツをスクレイピングして取引を行います。もちろん、すべてが SSL 経由で送信されますが、自動化するには、銀行のユーザー名とパスワードをどこかに保存する必要があります。
そこで、.NET 文字列、セキュリティ、および SecureString のうさぎの穴から始めました。ユーザー名とパスワードを SecureString に (1 文字ずつ) 入力するフォームを作成し、System.String を使用しないようにしました。次に、SecureString から char[] を取得し、そこから byte[] を取得し、char[] をゼロにし、DPAPI とコードに組み込まれたソルト文字列を使用して byte[] を暗号化し、暗号化された byte[] を Base64 に変換します。 System.String、byte[] をゼロにし、暗号化文字列をアプリケーション設定に保存します。
問題は、System.String にせずに HttpWebRequest の POST データに戻す方法です。私が思いついたのは、次の関数でした(これも、文字列を作成せずに文字を URL エンコードします)。
/// <summary>
/// Retrieves, (and then zero's,) a character array from the SecureString.
/// Then optionally URL encodes the characters and returns an encoded byte array.
/// Make sure to zero out the byte array when you're done with it.
/// </summary>
/// <param name="secure">The SecureString to be retrieved.</param>
/// <param name="encodingFormat">The encoding format used to retrieve the byte array. The default is UTF-8.</param>
/// <param name="urlEncode">If true will URL encode characters which are invalid for URL's.</param>
/// <returns></returns>
public static byte[] ToByteArray(this SecureString secure, System.Text.Encoding encodingFormat, bool urlEncode)
{
if (secure == null) throw new ArgumentNullException("secure");
if (encodingFormat == null)
encodingFormat = System.Text.Encoding.UTF8;
char[] chars = new char[secure.Length];
byte[] bytesToReturn;
// copy the SecureString data into an unmanaged char array and get a pointer to it
IntPtr ptr = Marshal.SecureStringToCoTaskMemUnicode(secure);
try
{
// copy the unmanaged char array into a managed char array
Marshal.Copy(ptr, chars, 0, secure.Length);
}
finally
{
Marshal.ZeroFreeCoTaskMemUnicode(ptr);
}
if (false == urlEncode)
{
bytesToReturn = encodingFormat.GetBytes(chars);
}
else
{
List<int> unsafeUrlCharIntValues = new List<int>();
unsafeUrlCharIntValues.AddRange(Enumerable.Range(32, 16)); // 32-47 Reserved Characters: ' '!?#$%&'()*+,-./
unsafeUrlCharIntValues.AddRange(Enumerable.Range(58, 7)); // 58-64 Reserved Characters: :;<=>?@
unsafeUrlCharIntValues.AddRange(Enumerable.Range(91, 6)); // 91-96 Reserved Characters: [\]^_`
unsafeUrlCharIntValues.AddRange(Enumerable.Range(123, 4)); // 123-126 Reserved Characters: {|}~
// Get a count of the unsafe URL characters to see if any processing is required and how many extra char's we'll need in the array.
int unsafeCharCount = 0;
for (int i = 0; i < chars.Length; i++)
if (unsafeUrlCharIntValues.Contains((int)chars[i]))
unsafeCharCount++;
if (unsafeCharCount < 1)
{
bytesToReturn = encodingFormat.GetBytes(chars);
}
else
{
char[] encodedChars = EncodeChars(chars);
bytesToReturn = encodingFormat.GetBytes(encodedChars);
// zero out encoded chars for security
for (int i = 0; i < encodedChars.Length; i++)
encodedChars[i] = '\0';
}
}
// zero out chars
for (int i = 0; i < chars.Length; i++)
chars[i] = '\0';
return bytesToReturn;
}
処理が終わったら、char[] をゼロにします。また、返された byte[] を HttpWebRequest ストリームに書き込んだ後、必ずゼロに設定します。これは安全ですか、それとも GC は char[] および byte[] オブジェクトのコピーを作成しますか? これは System.String を使用するよりも優れていますか? 安全でないコードを使用し、すべてをアンマネージ メモリで行うという他の投稿を読みましたが、最終的に HttpWebRequest に byte[] を取得する必要があります。