uipath secure new net generate convert c# security

c# - net - new secure string



¿SecureString es práctico en una aplicación de C#? (7)

Siéntase libre de corregirme si mis suposiciones están equivocadas aquí, pero permítame explicarle por qué lo pregunto.

Tomado de MSDN, un SecureString :

Representa texto que debe mantenerse confidencial. El texto se cifra para su privacidad cuando se usa y se elimina de la memoria de la computadora cuando ya no se necesita.

Entiendo esto, tiene mucho sentido almacenar una contraseña u otra información privada en un SecureString sobre un System.String , porque puedes controlar cómo y cuándo se almacena realmente en la memoria, porque un System.String :

es inmutable y, cuando ya no se necesita, no puede programarse para la recolección de basura mediante programación; es decir, la instancia es de solo lectura después de su creación y no es posible predecir cuándo se eliminará la instancia de la memoria de la computadora. En consecuencia, si un objeto String contiene información confidencial, como una contraseña, un número de tarjeta de crédito o datos personales, existe el riesgo de que la información se revele después de que se use porque su aplicación no puede eliminar los datos de la memoria de la computadora.

Sin embargo, en el caso de una aplicación GUI (por ejemplo, un cliente ssh), SecureString se debe construir desde un System.String . Todos los controles de texto utilizan una cadena como su tipo de datos subyacente .

Entonces, esto significa que cada vez que el usuario presiona una tecla, la cadena antigua que estaba allí se descarta, y se construye una nueva cadena para representar cuál es el valor dentro del cuadro de texto, incluso si se usa una máscara de contraseña. Y no podemos controlar cuándo o si alguno de esos valores se descarta de la memoria .

Ahora es el momento de iniciar sesión en el servidor. ¿Adivina qué? Debe pasar una cadena a través de la conexión para la autenticación . Así que vamos a convertir nuestro SecureString en un System.String ... y ahora tenemos una cadena en el montón sin forma de forzarlo a pasar por la recolección de basura (o escribir 0 en su búfer).

Mi punto es : no importa lo que haga, en algún punto de la línea, SecureString se convertirá en un System.String , lo que significa que al menos existirá en el montón en algún momento (sin ninguna garantía de recolección de basura).

Mi punto no es : si hay formas de evitar el envío de una cadena a una conexión ssh, o de evitar que un control almacene una cadena (haga un control personalizado). Para esta pregunta, puede reemplazar "conexión ssh" con "formulario de inicio de sesión", "formulario de registro", "formulario de pago", "formulario de alimentos que alimentaría a su cachorro pero no a sus hijos", etc.

  • Entonces, ¿en qué momento se vuelve práctico usar SecureString ?
  • ¿Alguna vez vale la pena el tiempo de desarrollo adicional para erradicar por completo el uso de un objeto System.String ?
  • ¿El objetivo principal de SecureString es simplemente reducir la cantidad de tiempo que un System.String está en el montón (reduciendo su riesgo de pasar a un archivo de intercambio físico)?
  • Si un atacante ya tiene los medios para una inspección del montón, entonces es muy probable que (A) ya tenga los medios para leer las pulsaciones de teclas, o (B) ya tenga la máquina físicamente ... Así que el uso de un SecureString le evitaría Los datos de todos modos?
  • ¿Es esto simplemente "seguridad en la oscuridad"?

Perdón si estoy poniendo las preguntas demasiado gruesas, la curiosidad acaba de vencerme. Siéntase libre de responder cualquiera o todas mis preguntas (o dígame que mis suposiciones son completamente erróneas). :)


Como ha identificado correctamente, SecureString ofrece una ventaja específica sobre la string : el borrado determinista. Hay dos problemas con este hecho:

  1. Como han mencionado otros y como se han dado cuenta, esto no es suficiente por sí mismo. Debe asegurarse de que cada paso del proceso (incluida la recuperación de la entrada, la construcción de la cadena, el uso, la eliminación, el transporte, etc.) se SecureString el propósito de utilizar SecureString . Esto significa que debe tener cuidado de nunca crear una string inmutable administrada por GC o cualquier otro búfer que almacene la información confidencial (o tendrá que realizar un seguimiento de eso también). En la práctica, esto no siempre es fácil de lograr, ya que muchas API solo ofrecen una forma de trabajar con string , no SecureString . E incluso si logras todo bien ...
  2. SecureString protege contra tipos de ataque muy específicos (y para algunos de ellos, ni siquiera es tan confiable). Por ejemplo, SecureString le permite reducir la ventana de tiempo en la que un atacante puede volcar la memoria de su proceso y extraer con éxito la información confidencial (de nuevo, como lo señaló correctamente), pero con la esperanza de que la ventana sea demasiado pequeña para el atacante. tomar una instantánea de su memoria no se considera seguridad en absoluto.

Entonces, ¿cuándo deberías usarlo? Solo cuando está trabajando con algo que puede permitirle trabajar con SecureString para todas sus necesidades e incluso entonces debe tener en cuenta que esto es seguro solo en circunstancias específicas.


En realidad, hay usos muy prácticos de SecureString .

¿Sabes cuantas veces he visto tales escenarios? (La respuesta es: ¡muchos!):

  • Una contraseña aparece en un archivo de registro accidentalmente.
  • Se muestra una contraseña en algún lugar: una vez que una GUI mostraba una línea de comandos de la aplicación que se estaba ejecutando, y la línea de comandos consistía en una contraseña. Ups .
  • Utilizando el generador de perfiles de memoria para perfilar el software con su colega. Colega ve tu contraseña en la memoria. ¿Suena irreal? De ningún modo.
  • Una vez RedGate software RedGate que podía capturar el "valor" de las variables locales en caso de excepciones, increíblemente útil. Sin embargo, puedo imaginar que registrará "contraseñas de cadena" accidentalmente.
  • Un volcado de memoria que incluye la contraseña de cadena.

¿Sabes cómo evitar todos estos problemas? SecureString . En general, se asegura de que no cometa errores tontos como tales. ¿Cómo lo evita? Al asegurarse de que la contraseña esté encriptada en la memoria no administrada y que solo se pueda acceder al valor real cuando esté seguro al 90% de lo que está haciendo.

En el sentido, SecureString funciona con bastante facilidad:

1) Todo está encriptado.

2) El usuario llama AppendChar

3) Descifra todo en MEMORIA NO DESTACADA y agrega el carácter

4) Cifrar todo de nuevo en MEMORIA NO DESTACADA.

¿Qué pasa si el usuario tiene acceso a su computadora? ¿Un virus podría tener acceso a todos los SecureStrings ? Sí. Todo lo que necesita hacer es conectarse a RtlEncryptMemory cuando la memoria se está descifrando, obtendrá la ubicación de la dirección de la memoria sin cifrar y la leerá. Voila! De hecho, podría crear un virus que explorará constantemente el uso de SecureString y registrará todas las actividades con él. No estoy diciendo que sea una tarea fácil, pero se puede hacer. Como puede ver, la "potencia" de SecureString desaparece por completo una vez que hay un usuario / virus en su sistema.

Tienes algunos puntos en tu post. Claro, si utiliza algunos de los controles de UI que contienen una "contraseña de cadena" internamente, usar SecureString real no es tan útil. Aunque, todavía, puede proteger contra alguna estupidez que he enumerado anteriormente.

Además, como otros han señalado, WPF admite PasswordBox, que utiliza SecureString internamente a través de su propiedad SecurePassword .

La conclusión es; Si tiene datos confidenciales (contraseñas, tarjetas de crédito, ...), utilice SecureString . Esto es lo que C # Framework está siguiendo. Por ejemplo, la clase NetworkCredential almacena la contraseña como SecureString . Si observa this , puede ver más de ~ 80 usos diferentes en el marco .NET de SecureString .

Hay muchos casos en los que tiene que convertir SecureString a cadena, porque algunas API lo esperan.

El problema habitual es:

  1. La API es GENERICA. No se sabe que hay un dato sensible.
  2. La API sabe que se trata de datos confidenciales y utiliza "cadena", que es un mal diseño.

Ha planteado un buen punto: ¿qué sucede cuando SecureString se convierte en string ? Esto solo puede suceder debido al primer punto. Por ejemplo, la API no sabe que son datos confidenciales. Personalmente no he visto que eso suceda. Sacar una cadena de SecureString no es tan simple.

No es simple por una simple razón ; nunca tuvo la intención de permitir que el usuario convierta SecureString en una cadena, como usted dijo: GC se activará. Si se ve a sí mismo haciendo eso, debe retroceder y preguntarse: ¿Por qué hago esto o realmente lo necesito? ¿esto por que?

Hay un caso interesante que vi. A saber, la función WinApi LogonUser toma LPTSTR como una contraseña, lo que significa que debe llamar a SecureStringToGlobalAllocUnicode . Eso básicamente le da una contraseña sin cifrar que vive en la memoria no administrada. Tienes que deshacerte de eso tan pronto como hayas terminado:

// Marshal the SecureString to unmanaged memory. IntPtr rawPassword = Marshal.SecureStringToGlobalAllocUnicode(password); try { //...snip... } finally { // Zero-out and free the unmanaged string reference. Marshal.ZeroFreeGlobalAllocUnicode(rawPassword); }

Siempre puede extender la clase SecureString con un método de extensión, como ToEncryptedString(__SERVER__PUBLIC_KEY) , que le brinda una instancia de string de SecureString que se cifra mediante la clave pública del servidor. Sólo el servidor puede descifrarlo. Problema resuelto: la recolección de basura nunca verá la cadena "original", ya que nunca la expones en la memoria administrada. Esto es exactamente lo que se está haciendo en PSRemotingCryptoHelper ( EncryptSecureStringCore(SecureString secureString) ).

Y como algo muy casi relacionado: Mono SecureString no se encripta en absoluto . La implementación se ha comentado porque ... esperen a que ... "De alguna manera causa una ruptura de prueba de nunit" , lo que lleva a mi último punto:

SecureString no es compatible en todas partes. Si la plataforma / arquitectura no admite SecureString , obtendrá una excepción. Hay una lista de plataformas que se admiten en la documentación.


Hace algún tiempo tuve que crear una interfaz ac # contra una pasarela de pago con tarjeta de crédito Java y uno necesitaba un cifrado de clave de comunicaciones seguro compatible. Como la implementación de Java fue bastante específica y tuve que trabajar con los datos protegidos de una manera determinada.

Encontré que este diseño es bastante fácil de usar y más fácil que trabajar con SecureString ... para aquellos a quienes les gusta usar ... no dude, sin restricciones legales :-). Tenga en cuenta que estas clases son internas, es posible que tenga que hacerlas públicas.

namespace Cardinity.Infrastructure { using System.Security.Cryptography; using System; enum EncryptionMethods { None=0, HMACSHA1, HMACSHA256, HMACSHA384, HMACSHA512, HMACMD5 } internal class Protected { private Byte[] salt = Guid.NewGuid().ToByteArray(); protected byte[] Protect(byte[] data) { try { return ProtectedData.Protect(data, salt, DataProtectionScope.CurrentUser); } catch (CryptographicException)//no reason for hackers to know it failed { #if DEBUG throw; #else return null; #endif } } protected byte[] Unprotect(byte[] data) { try { return ProtectedData.Unprotect(data, salt, DataProtectionScope.CurrentUser); } catch (CryptographicException)//no reason for hackers to know it failed { #if DEBUG throw; #else return null; #endif } } } internal class SecretKeySpec:Protected,IDisposable { readonly EncryptionMethods _method; private byte[] _secretKey; public SecretKeySpec(byte[] secretKey, EncryptionMethods encryptionMethod) { _secretKey = Protect(secretKey); _method = encryptionMethod; } public EncryptionMethods Method => _method; public byte[] SecretKey => Unprotect( _secretKey); public void Dispose() { if (_secretKey == null) return; //overwrite array memory for (int i = 0; i < _secretKey.Length; i++) { _secretKey[i] = 0; } //set-null _secretKey = null; } ~SecretKeySpec() { Dispose(); } } internal class Mac : Protected,IDisposable { byte[] rawHmac; HMAC mac; public Mac(SecretKeySpec key, string data) { switch (key.Method) { case EncryptionMethods.HMACMD5: mac = new HMACMD5(key.SecretKey); break; case EncryptionMethods.HMACSHA512: mac = new HMACSHA512(key.SecretKey); break; case EncryptionMethods.HMACSHA384: mac = new HMACSHA384(key.SecretKey); break; case EncryptionMethods.HMACSHA256: mac = new HMACSHA256(key.SecretKey); break; case EncryptionMethods.HMACSHA1: mac = new HMACSHA1(key.SecretKey); break; default: throw new NotSupportedException("not supported HMAC"); } rawHmac = Protect( mac.ComputeHash(Cardinity.ENCODING.GetBytes(data))); } public string AsBase64() { return System.Convert.ToBase64String(Unprotect(rawHmac)); } public void Dispose() { if (rawHmac != null) { //overwrite memory address for (int i = 0; i < rawHmac.Length; i++) { rawHmac[i] = 0; } //release memory now rawHmac = null; } mac?.Dispose(); mac = null; } ~Mac() { Dispose(); } } }


Hay pocos temas en sus suposiciones.

En primer lugar, la clase SecureString no tiene un constructor String. Para crear uno, asigna un objeto y luego agrega los caracteres.

En el caso de una GUI o una consola, puede pasar muy fácilmente cada tecla presionada a una cadena segura.

La clase está diseñada de una manera que no puede, por error, acceder al valor almacenado. Esto significa que no puede obtener la string como una contraseña directamente de ella.

Por lo tanto, para usarlo, por ejemplo, para autenticarse a través de la web, tendrá que usar clases adecuadas que también sean seguras.

En el marco .NET tienes algunas clases que pueden usar SecureString

  • El control PasswordBox de WPF mantiene la contraseña como SecureString internamente.
  • La propiedad Password de System.Diagnostics.ProcessInfo es SecureString.
  • El constructor para X509Certificate2 toma un SecureString para la contraseña.

(more)

Para concluir, la clase SecureString puede ser útil, pero requiere más atención del desarrollador.

Todo esto, con ejemplos, está bien descrito en la documentación de MSDN de SecureString


Me gustaría abordar este punto:

Si un atacante ya tiene los medios para realizar una inspección del montón, lo más probable es que (A) ya tenga los medios para leer las pulsaciones de teclas, o (B) ya tenga la máquina físicamente ... Por lo tanto, el uso de un SecureString evitaría que SecureString Los datos de todos modos?

Un atacante puede no tener acceso completo a la computadora y la aplicación, pero puede tener medios para acceder a algunas partes de la memoria del proceso. Generalmente es causada por errores como sobrecargas de búfer cuando una entrada especialmente construida puede hacer que la aplicación exponga o sobrescriba alguna parte de la memoria.

Memoria que gotea del corazón

Tomemos Heartbleed por ejemplo. Las solicitudes especialmente construidas pueden hacer que el código exponga al atacante partes aleatorias de la memoria del proceso. Un atacante puede extraer certificados SSL de la memoria, pero lo único que necesita es usar una solicitud con formato incorrecto.

En el mundo del código administrado, los desbordamientos de búfer se convierten en un problema mucho menos frecuente. Y en el caso de WinForms, los datos ya están almacenados de manera insegura y usted no puede hacer nada al respecto. Esto hace que la protección con SecureString bastante inútil.

Sin embargo, la GUI puede programarse para utilizar SecureString y, en tal caso, vale la pena reducir la ventana de disponibilidad de la contraseña en la memoria. Por ejemplo, PasswordBox.SecurePassword de WPF es de tipo SecureString .


Un SecureString es útil si:

  • Lo construye carácter por carácter (por ejemplo, a partir de la entrada de la consola) o lo obtiene de una API no administrada

  • Lo usas pasándolo a una API no administrada (SecureStringToBSTR).

Si alguna vez lo conviertes en una cadena administrada, has derrotado su propósito.

ACTUALIZACIÓN en respuesta al comentario.

... o BSTR como usted menciona, que no parece más seguro

Una vez que se ha convertido en un BSTR, el componente no administrado que consume el BSTR puede poner a cero la memoria. La memoria no administrada es más segura en el sentido de que se puede restablecer de esta manera.

Sin embargo, hay muy pocas API en el .NET Framework que admiten SecureString, por lo que tiene razón al decir que tiene un valor muy limitado en la actualidad.

El principal caso de uso que vería es en una aplicación cliente que requiere que el usuario ingrese un código o contraseña altamente confidencial. La entrada del usuario podría usarse carácter por carácter para construir un SecureString, luego se podría pasar a una API no administrada, que pone a cero el BSTR que recibe después de usarlo. Cualquier volcado de memoria posterior no contendrá la cadena sensible.

En una aplicación de servidor es difícil ver dónde sería útil.

ACTUALIZACIÓN 2

Un ejemplo de una API .NET que acepta un SecureString es este constructor para la clase X509Certificate . Si participa con ILSpy o similar, verá que SecureString se convierte internamente en un búfer no administrado ( Marshal.SecureStringToGlobalAllocUnicode ), que luego se Marshal.SecureStringToGlobalAllocUnicode cero cuando termina con ( Marshal.ZeroFreeGlobalAllocUnicode ).


El siguiente texto se copia del analizador de código estático HP Fortify

Resumen: El método PassString () en PassGenerator.cs almacena datos confidenciales de manera insegura (es decir, en una cadena), lo que hace posible extraer los datos mediante la inspección del montón.

Explicación: los datos confidenciales (como contraseñas, números de seguridad social, números de tarjetas de crédito, etc.) almacenados en la memoria pueden filtrarse si se almacenan en un objeto String gestionado. Los objetos de cadena no están anclados, por lo que el recolector de basura puede reubicar estos objetos a voluntad y dejar varias copias en la memoria. Estos objetos no están cifrados de forma predeterminada, por lo que cualquier persona que pueda leer la memoria del proceso podrá ver el contenido. Además, si la memoria del proceso se intercambia en el disco, el contenido no cifrado de la cadena se escribirá en un archivo de intercambio. Por último, como los objetos String son inmutables, el recolector de basura CLR solo puede eliminar el valor de un String de la memoria. No se requiere que el recolector de basura se ejecute a menos que el CLR tenga poca memoria, por lo que no hay garantía de cuándo tendrá lugar la recolección de basura. En el caso de un fallo de la aplicación, un volcado de memoria de la aplicación puede revelar datos confidenciales.

Recomendaciones: en lugar de almacenar datos confidenciales en objetos como cadenas, almacénelos en un objeto SecureString. Cada objeto almacena su contenido en un formato cifrado en la memoria en todo momento.