Es un hecho bien conocido que C # string
es bastante inseguro, no está anclado en la RAM, el recolector de basura puede moverlo, copiarlo, dejar múltiples huellas en la RAM y la RAM se puede intercambiar y estar disponible como un archivo para leer, sin mencionar otros hechos conocidos.
Para mitigar esto, a Microsoft se le ocurrió SecureString
. La cuestión es: ¿cuál es la forma correcta de usarlo?
Pido esto porque tarde o temprano tendrás que extraer la cadena interna. Y sí, SecureString
nos permite escribir un char
a la vez y nos da otros usos menores mientras escondemos su secreto, pero a veces necesitamos toda la cadena a la vez o algo así. Hasta ahora, todas las implementaciones que veo usan un string
regular al final, con el contenido extraído y descifrado del SecureString
que creo (o espero) que es evitable en algunos casos, aunque puede implicar un código complicado para moverse un .NET string
.
En mi caso, uso una biblioteca para el hashing de contraseñas denominado BCrypt . Utiliza un string
regular para calcular los hashes, que creo que está lejos de ser ideal. No encontré ninguna biblioteca que aceptara un SecureString
directamente, así que no tengo mucha opción, pero la uso.
Actualmente ejecuto código como este:
SecureString password; // This usually comes from a PasswordBox Property
IntPtr passwordBSTR = Marshal.SecureStringToBSTR(password);
string insecurePassword = Marshal.PtrToStringBSTR(passwordBSTR);
Marshal.ZeroFreeBSTR(passwordBSTR);
passwordBSTR = default(IntPtr);
password.Clear();
// Use the insecurePassword....usually as:
bool result = BCrypt.Net.BCrypt.Verify(insecurePassword, user.Password); // This hashes the provided password and compares it with another BCrypt hash.
insecureString = string.Empty; // hoping for the GC to act now, fingers crossed.
// use result and etc...
De esta manera, la contraseña raw string
se copiará varias veces para el método BCrypt
y probablemente incluso más veces dentro de él.
Esto hace que surjan algunas preguntas en mi mente:
1 - ¿Son BSTR
cadenas como C
cadenas en el sentido de administración de memoria? ¿Están fijadas en la RAM y puedo manipularlas y borrarlas según lo crea conveniente? es más seguro usarlos en C # o interoperarlo con un código C ++ para poder eliminar muchas de mis manipulaciones .NET string
(como verificar si la cadena de la contraseña es un espacio en blanco nulo o blanco o tiene la longitud correcta o tiene la contraseña deseada) fuerza)?
2 - Si la respuesta # 1 de la pregunta es "sí", entonces ¿debería invertir algo de tiempo para pasar la cadena BSTR a un código de interoperabilidad C ++ para calcular el hash y verificar?
3 - ¿Mi código posterior es correcto o me falta algo en la manipulación SecureString
?
Solo para ser claro: Mi principal intención con esta pregunta es evaluar si debo pasar todo mi código de manejo de contraseña a C ++ desde mi C # (probablemente usando C ++ / CLI o interoperabilidad) para lograr un Código más seguro que lo que C # tiene para ofrecer. Este escenario realmente me preocupa para el hashing de cadenas de contraseñas donde se crearán múltiples cadenas y se copiarán con la información secreta del usuario dispersada por toda la RAM.
Como ejemplo: podría hacer un método como:
public string SecureHash(SecureString password)
Y este método extraerá el valor SecureString
y lo pasará como una cadena BSTR
(u otra opción de extracción, honestamente no conozco los beneficios entre ellos) a un método de hashing C ++, por lo que podemos controlar todos los lugares donde se encuentra nuestra contraseña en la memoria.
Le pido a esto que no haga un gran trabajo refactorizando mi código y al final descubro que todo fue en vano porque estaba usando SecureStrings
incorrectamente todo el tiempo y ya hay soluciones para esto, o una falsa Sentido de seguridad, como sucede a veces, ya que no sé cómo se implementan BSTR
cadenas en la memoria o el otro SecureStrings
opciones de extracción (Unicode, ANSI y etc.)