security delphi memory-management ram-scraping

security - ¿Cómo puedo aumentar la seguridad de la memoria en Delphi?



memory-management ram-scraping (11)

¿Es posible "borrar" cadenas en Delphi? Dejame explicar:

Estoy escribiendo una aplicación que incluirá una DLL para autorizar a los usuarios. Leerá un archivo encriptado en un XML DOM, usará la información allí y luego lanzará el DOM.

Es obvio que el XML no encriptado todavía está en la memoria de la DLL y, por lo tanto, es vulnerable al examen. Ahora, no voy a excederme en la protección de esto, el usuario podría crear otra DLL, pero me gustaría dar un paso básico para evitar que los nombres de los usuarios permanezcan en la memoria durante mucho tiempo. Sin embargo, no creo que pueda borrar la memoria fácilmente debido a las referencias. Si atravieso mi DOM (que es una clase TNativeXML) y encuentro cada instancia de cadena y luego lo hago en algo como "aaaaa", entonces en realidad no asignará el nuevo puntero de cadena a la referencia de DOM, y luego dejaré la cadena anterior ¿Hay memoria en espera de reasignación? ¿Hay alguna manera de estar seguro de que estoy matando la única copia original?

¿O hay en D2007 un medio para decirle que limpie toda la memoria no utilizada del montón? Así que podría liberar el DOM, y luego decirle que limpie.

¿O debería seguir con mi próxima tarea y olvidarme de esto porque realmente no vale la pena molestarse?


¿Qué hay de descifrar el archivo a una secuencia, utilizando un procesador SAX en lugar de un XML DOM para hacer su verificación y luego sobrescribir la secuencia descifrada antes de liberarla?


¿Qué hay de mantener la contraseña como un valor hash en el XML y verificar mediante la comparación del hash de la contraseña de entrada con la contraseña hash en su XML.

Editar: puede mantener cifrados y descifrados todos los datos confidenciales solo en el último momento posible.


¿Sería posible cargar el XML descifrado en una matriz de caracteres o bytes en lugar de una cadena? ¿Entonces no habría manejo de copiado en escritura, por lo que sería capaz de rellenar la memoria con # 0 antes de liberar?

Tenga cuidado si asigna una matriz de caracteres a cadenas, ya que Delphi tiene aquí un manejo inteligente para la compatibilidad con la matriz empaquetada tradicional [1..x] de caracteres.

Además, ¿podrías usar ShortString?


Desordenado, pero podrías anotar el tamaño del montón que has usado mientras tienes el montón lleno de datos confidenciales y cuando se libera haz un GetMem para asignarte un gran fragmento que abarca (digamos) el 200% de eso. llene ese trozo y asuma que cualquier fragmentación no es muy útil para un examinador. Bri


Dos puntos generales sobre esto:

Primero, esta es una de esas áreas donde "si tienes que preguntar, probablemente no deberías estar haciendo esto". Y por favor no lo tomes por el camino equivocado; Me refiero sin falta de respeto a tus habilidades de programación. Es solo que escribir software seguro, criptográficamente fuerte es algo en lo que usted es un experto o no lo es. De la misma manera que saber "un poco de karate" es mucho más peligroso que no saber nada de karate. Hay una serie de herramientas de terceros para escribir software seguro en Delphi que tienen soporte experto disponible; Recomendaría encarecidamente a cualquier persona que no tenga un conocimiento profundo de los servicios criptográficos en Windows, los fundamentos matemáticos de la criptografía y la experiencia en derrotar a los ataques de canal lateral que los use en lugar de intentar "hacer lo propio".

Para responder a su pregunta específica: la API de Windows tiene una serie de funciones que son útiles, como CryptProtectMemory . Sin embargo, esto generará una falsa sensación de seguridad si cifras tu memoria, pero tienes un agujero en otro lugar del sistema o si expone un canal lateral. Puede ser como poner un candado en la puerta pero dejando la ventana abierta.


Las DLL no poseen memoria asignada, los procesos sí. La memoria asignada por su proceso específico se descartará una vez que finalice el proceso, ya sea que la DLL se cuelgue (porque está siendo utilizada por otro proceso) o no.


No creo que valga la pena molestarse, porque si un usuario puede leer la memoria del proceso utilizando el DLL, el mismo usuario también puede detener la ejecución en cualquier momento dado. Detener la ejecución antes de que se borre la memoria aún le dará al usuario acceso total a los datos no encriptados.

IMO Cualquier usuario suficientemente interesado y capaz de hacer lo que usted describe no se verá seriamente incomodado por su DLL borrando la memoria.


Si usa el administrador de memoria FastMM en el modo de depuración completa, puede forzarlo a sobrescribir la memoria cuando se libera.

Normalmente, ese comportamiento se usa para detectar punteros salvajes, pero también se puede usar para lo que desee.

Por otro lado, asegúrese de comprender lo que escribe Craig Stuntz: no escriba esta autenticación y autorización, use el sistema operativo subyacente siempre que sea posible.

Por cierto: Hallvard Vassbotn escribió un buen blog sobre FastMM: http://hallvards.blogspot.com/2007/05/use-full-fastmm-consider-donating.html

Saludos,

Jeroen Pluimerers


Si usa XML, incluso encriptado, para almacenar contraseñas, puede poner en riesgo a sus usuarios. Un mejor enfoque sería almacenar los valores hash de las contraseñas en su lugar, y luego comparar el hash con la contraseña ingresada. La ventaja de este enfoque es que incluso conociendo el valor hash, no sabrá la contraseña que hace el hash. Agregar un identificador de fuerza bruta (contar intentos de contraseña inválida y bloquear la cuenta después de un cierto número) aumentará aún más la seguridad.

Hay varios métodos que puede usar para crear un hash de una cadena. Un buen punto de partida sería observar el proyecto de fuente abierta turbo " LockBox ", creo que tiene varios ejemplos de creación de claves hash de una manera.

EDITAR

Pero, ¿cómo saber el valor hash si es una ayuda de una manera? Si es realmente paranoico, puede modificar el valor hash por algo predecible que solo usted sabría ... digamos, un número aleatorio que utiliza un valor inicial específico más la fecha. A continuación, puede almacenar solo el hash en su xml para que pueda usarlo como punto de partida para la comparación. Lo bueno de los generadores de números aleatorios psuedo es que siempre generan la misma serie de números "aleatorios" con la misma semilla.


¿Qué tal algo como esto?

procedure WipeString(const str: String); var i:Integer; iSize:Integer; pData:PChar; begin iSize := Length(str); pData := PChar(str); for i := 0 to 7 do begin ZeroMemory(pData, iSize); FillMemory(pData, iSize, $FF); // 1111 1111 FillMemory(pData, iSize, $AA); // 1010 1010 FillMemory(pData, iSize, $55); // 0101 0101 ZeroMemory(pData, iSize); end; end;


Tenga cuidado con las funciones que intentan tratar una cadena como un puntero, e intente utilizar FillChar o ZeroMemory para limpiar el contenido de la cadena.

  • esto es incorrecto (las cuerdas son compartidas, estás jodiendo a otra persona que está usando la cuerda)
  • y puede causar una infracción de acceso (si la cadena resulta ser una constante, está ubicada en una página de datos de solo lectura en el espacio de direcciones del proceso, y tratar de escribir es una infracción de acceso)

procedure BurnString(var s: UnicodeString); begin { If the string is actually constant (reference count of -1), then any attempt to burn it will be an access violation; as the memory is sitting in a read-only data page. But Delphi provides no supported way to get the reference count of a string. It''s also an issue if someone else is currently using the string (i.e. Reference Count > 1). If the string were only referenced by the caller (with a reference count of 1), then our function here, which received the string through a var reference would also have the string with a reference count of one. Either way, we can only burn the string if there''s no other reference. The use of UniqueString, while counter-intuitiave, is the best approach. If you pass an unencrypted password to BurnString as a var parameter, and there were another reference, the string would still contain the password on exit. You can argue that what''s the point of making a *copy* of a string only to burn the copy. Two things: - if you''re debugging it, the string you passed will now be burned (i.e. your local variable will be empty) - most of the time the RefCount will be 1. When RefCount is one, UniqueString does nothing, so we *are* burning the only string } if Length(s) > 0 then begin System.UniqueString(s); //ensure the passed in string has a reference count of one ZeroMemory(Pointer(s), System.Length(s)*SizeOf(WideChar)); { By not calling UniqueString, we only save on a memory allocation and wipe if RefCnt <> 1 It''s an unsafe micro-optimization because we''re using undocumented offsets to reference counts. And i''m really uncomfortable using it because it really is undocumented. It is absolutely a given that it won''t change. And we''d have stopping using Delphi long before it changes. But i just can''t do it. } //if PLongInt(PByte(S) - 8)^ = 1 then //RefCnt=1 // ZeroMemory(Pointer(s), System.Length(s)*SizeOf(WideChar)); s := ''''; //We want the callee to see their passed string come back as empty (even if it was shared with other variables) end; end;

Una vez que tenga la versión UnicodeString , puede crear las versiones AnsiString y WideString :

procedure BurnString(var s: AnsiString); overload; begin if Length(s) > 0 then begin System.UniqueString(s); ZeroMemory(Pointer(s), System.Length(s)*SizeOf(AnsiChar)); //if PLongInt(PByte(S) - 8)^ = 1 then //RefCount=1 // ZeroMemory(Pointer(s), System.Length(s)*SizeOf(AnsiChar)); s := ''''; end; end; procedure BurnString(var s: WideString); begin //WideStrings (i.e. COM BSTRs) are not reference counted, but they are modifiable if Length(s) > 0 then begin ZeroMemory(Pointer(s), System.Length(s)*SizeOf(WideChar)); //if PLongInt(PByte(S) - 8)^ = 1 then //RefCount=1 // ZeroMemory(Pointer(s), System.Length(s)*SizeOf(AnsiChar)); s := ''''; end; end;