.net - La mejor forma de recuperar el valor de un SecureString
post (0)
Intento crear una herramienta automatizada que salga y verifique las transacciones recientes en mi cuenta de cheques (espiar las compras de mi esposa jajaja). La herramienta publica mi nombre de usuario y contraseña a través de SSL y luego raspa los contenidos de la página del banco para las transacciones. Todo se envía a través de SSL, por supuesto, pero para hacerlo automatizado, debo guardar mi nombre de usuario y contraseña bancaria en algún lugar.
Así que empecé a navegar por el agujero de conejo de .NET Strings, seguridad y SecureString. Hice un formulario para ingresar mi nombre de usuario y contraseña en SecureString (carácter por carácter) asegurándome de no utilizar ANY System.String''s. Luego recupero un char [] de SecureString, obtengo un byte [] de él, cero el char [], encripto el byte [] usando el DPAPI y una cadena de sal incorporada en el código, convierto el byte encriptado [] a un Base64 System.String, ponga a cero el byte [] y guarde la cadena de cifrado en una configuración de aplicación.
La pregunta era, ¿cómo puedo obtenerla de vuelta a los datos POST en una HttpWebRequest sin convertirla en System.String? Lo que se me ocurrió fue la siguiente función (que también URL codifica los caracteres sin hacer cadenas).
/// <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;
}
Pongo a cero el carbón [] después de que termine con ellos. También me aseguro de poner a cero el byte devuelto [] después de haberlo escrito en la secuencia HttpWebRequest. ¿Esto es seguro o el GC hará copias de los objetos char [] y byte []? ¿Es esto mejor que usar System.String''s? He leído otras publicaciones que dicen que debo usar un código inseguro y hago todo lo posible en la memoria no administrada, pero aún así necesito obtener un byte [] en mi HttpWebRequest al final.