2008-09-29 6 views
7

Una de las solicitudes de funciones que tengo para el programa en el que estoy trabajando es para poder guardar la lista de credenciales que los usuarios ingresan, para que puedan compartirse. El caso de uso específico que inspiró esta solicitud fue el uso de nuestro programa en una gran red corporativa, formada por LAN bastante buenas conectadas por una WAN escamosa. La idea era que, en lugar de tener nuestro programa en contra de la WAN cuando estaba inactivo, enviarían un archivo de 'configuración' que contenía las credenciales de administrador celosamente guardadas, ejecútelo en cada LAN y comprima los resultados y envíelos por correo electrónico. espalda.Guardar un SecureString

Sí.

Mi instinto inicial es burlarme de esta solicitud: ¿guardar contraseñas? realmente? y seguramente la división de redes de la compañía preferiría que probaras y vendieras cualquier producto WAN que tengan, pero resulta que una de las clases para las que utilizo las credenciales puede tomar SecureString, y, bueno, siempre es bueno tener cuidado para formas en que puede ahorrarle a la gente un poco de esfuerzo. Eso me hizo preguntarme:

¿Es posible guardar un SecureString encriptado, para poder guardar los datos confidenciales en un archivo y abrirlo en otro lugar?

¿Cuáles son sus pensamientos, desbordamiento de pila?

+0

¿Qué quiere decir "compartido en caso de fallo de la red"? ¿Puedes explicar eso por favor? –

Respuesta

5

No hay soporte para guardar en SecureString, está pensado como un mecanismo para proteger una cadena administrada en la memoria y solo se usa para interactuar con API no administradas. Si se almacenara una contraseña en una instancia de System.String, la seguridad sería menor debido a la naturaleza de System.String. La existencia de recolección de basura e internamiento mantendría la contraseña en la memoria por más tiempo de lo necesario. Además, debido a la gran cantidad de excelentes herramientas de depuración para .NET, sería significativamente más fácil acceder a la cadena a través de la reflexión u otra API de .NET incluso sin una vida útil más larga.

Si va a guardar una contraseña en el disco, su seguridad está bastante comprometida. Si alguien tiene acceso físico a la máquina, o acceso remoto a nivel de administrador, entonces lo mejor que puede hacer es hacerlo más difícil, pero nunca imposible. Use una API de cifrado, guárdela en una ubicación segura, configure los derechos de acceso.

Aparte de todo, Merus, sugiero que intente mejorar el sistema general porque para un caso de uso como el que está describiendo (suponiendo que lo entiendo) sería mejor que guarde un hash que el contraseña real

+1

Tenía la esperanza de que SecureString hiciera el hash por mí. Sé que no es mejor rodar mi propia función hash. – Merus

+1

¿En PowerShell se puede guardar SecureString, de cualquier forma usando C#? http://stackoverflow.com/questions/11174425/how-does-one-securely-handle-passwords-in-a-custom-written-powershell-cmdlet – Kiquenet

3

Si quiere decir guardar los bytes cifrados de SecureString, esto no funcionará: la clave para SecureString está vinculada al usuario y al proceso. Lea en esos bytes en un proceso diferente o para un usuario diferente, y no hay forma de descifrar la cadena.

1

Es posible que desee echar un vistazo a IsolatedStorageFileStream class. Especifica formas de escribir y almacenar datos de archivos a los que solo puede acceder su ensamblaje.

No creo que puedas usar SecureString en él.

1

El SecureString no es serializable por lo que no se puede simplemente guardarlo con algunos de los serializadores entregados (binario, XML, etc.)

Tampoco se puede acceder simplemente por ejemplo, una propiedad de "Contraseña" de un objeto de seguridad, ya que no existe tal cosa. tienes que usar Marshalling y un poco de plomería para hacer esto. si desea almacenar credenciales de usuario en algún lugar, sugiero encriptarlas por su cuenta, ya que esto facilita el desarrollo posterior. Después de evaluar el enfoque SecureString, decidimos implementar algo por nosotros mismos, pero estos son solo mis 2 centavos.

3

Esto vencería el punto de un SecureString, que está garantizado que reside en la memoria. Entonces, si lo está guardando en un archivo, también podría guardarlo como una cadena normal, ya que ya no es "seguro".

4

Es posible que desee comparar el hash de contraseñas.
Tendría una sal hecha de nombre de usuario y probablemente alguna otra constante, seguida de la cadena. Luego, pasaría eso a un algoritmo hash, como SHA1 *.
Por ejemplo,

using System.Security.Cryptography; 

public byte[] GetPasswordHash(string username, string password, string salt) 
{ 
    // get salted byte[] buffer, containing username, password and some (constant) salt 
    byte[] buffer; 
    using (MemoryStream stream = new MemoryStream()) 
    using (StreamWriter writer = new StreamWriter(stream)) 
    { 
     writer.Write(salt); 
     writer.Write(username); 
     writer.Write(password); 
     writer.Flush(); 

     buffer = stream.ToArray(); 
    } 

    // create a hash 
    SHA1 sha1 = SHA1.Create(); 
    return sha1.ComputeHash(buffer); 
} 

Entonces, usted comparar el resultado de GetPasswordHash(username, expectedPassword, salt)-GetPasswordHash(username, givenPassword, salt).
Si implementa su propia lista de usuarios con nombres de usuario y contraseñas, puede considerar solo guardar el hash (GetPasswordHash(username, givenPassword, salt)) y comparar con el hash guardado.

+0

a veces comparar la contraseña dada no es la tarea, pero el programa en sí quiere usar contraseñas para obtener autorización en otro sistema. Al igual que un cliente de correo electrónico, use la contraseña para obtener autorización en el servidor SMTP. ¿Qué podemos hacer en esa situación? –

+0

@Sankaran: cada caso tiene su propia solución. La mayoría de las API modernas aceptan hashes de contraseña; algunos tienen claves API, por lo que ni siquiera necesita saber el hash de la contraseña. Para utilizar API antiguas que solo admiten el uso directo de la contraseña, no tendría más remedio que almacenar la contraseña. En ese caso, utilizaría algún tipo de cifrado, aunque es bastante inútil. – configurator

+0

MD5 es totalmente inapropiado como una opción de algoritmo hash. Necesita bcrypt o scrypt, o al _least_ sha1 (e incluso eso no es realmente recomendable). –

0

Como se indica aquí, SecureString no es la mejor manera de utilizar en este escenario.

Si tuviera la necesidad de cuota de las credenciales de los usuarios que probablemente compartir el nombre de usuario y la hash + contraseña salada, por lo que sería seguro y sólo comparten la representación de una contraseña, no su contenido.

La forma más simple de esto es, por ejemplo, utilizar el BCryt Biblioteca (where there's a .NET representation of it) y simplemente sede de la representación nombre de usuario y contraseña en una mesa listo para ser compartido

Ésta es la forma en que lo utilizaría:

StringBuilder sb = new StringBuilder(); 

// run ADO.NET/Entity Framework/etc and query your DB with something like: 
"SELECT user_id, username, password FROM [TblUsers];" 

// loop through the results 

sb.AppendFormat(
     "INSERT INTO [TblSharedUsers] SELECT '{0}','{1}','{2}'",    
      dr["id"], dr["username"], 
      BCrypt.HashPassword(dr["password"], BCrypt.GenerateSalt(12)); 
     ); 

// then run sb.ToSTring() agains your db 

en el cliente final, puede crear un nuevo AuthenticationProvider (por lo que será fácil cambiar cuando la red no está disponible utilizando IoC y DI) que lee de la TblSharedUsers y comprobar la contraseña de usuario como

string userPassword = Request["password"]; 

"SELECT id, password FROM [TblSharedUsers] WHERE username = @usr;" 

if(BCrypt.CheckPassword(userPassword, dr["password"])) 
    // User can log in 
else 
    // Credentials are invalid 

Espero que esto ayude a alguien con el mismo problema.

Cuestiones relacionadas