Pregunta

¿Es posible "borrar" cadenas en Delphi? Me explico:

Estoy escribiendo una aplicación que incluirá un archivo DLL para autorizar a los usuarios. Se leerá un archivo cifrado en un DOM XML, utilice la información allí, y luego suelte el DOM.

Es obvio que el XML sin cifrar está todavía sentado en la memoria de la DLL, y por lo tanto vulnerables a examen. Ahora, yo no voy a ir por la borda en la protección de este - el usuario podría crear otro archivo DLL - pero me gustaría dar un paso básico para la prevención de los nombres de usuario de estar sentado en la memoria durante mucho tiempo. Sin embargo, no creo que pueda limpiar fácilmente la memoria de todos modos porque de referencias. Si recorro mi DOM (que es una clase TNativeXML) y encontrar todos los casos cadena y luego convertirlo en algo así como "aaaaa", entonces será en realidad no asignar el nuevo puntero de cadena a la referencia DOM, y luego salir de la cadena antigua sentado existe en la memoria a la espera de volver a la asignación? ¿Hay una manera de estar seguro de que estoy matando el único y original copia?

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

O debo seguir con mi siguiente tarea y olvidar esto porque realmente no vale la pena molestarse.

¿Fue útil?

Solución

No creo que vale la pena molestarse, porque si un usuario puede leer la memoria del proceso mediante el DLL, el mismo usuario también puede detener la ejecución en cualquier punto dado en el tiempo. Detener la ejecución antes de que la memoria se limpia seguirá siendo dar al usuario acceso completo a los datos sin cifrar.

OMI cualquier usuario interesado lo suficiente y capaz de hacer lo que usted describe no se verá seriamente molestos por el archivo DLL que limpia la memoria.

Otros consejos

Dos puntos generales acerca de esto:

En primer lugar, esta es una de esas áreas en las que "si tiene que pedir, probablemente no debería estar haciendo esto." Y por favor no tome que el camino equivocado; Me refiero sin faltarle el respeto a sus habilidades de programación. Es sólo que la escritura de software seguro, criptográficamente fuerte es algo que ya sea que eres un experto en, o que no lo son. Mucho de la misma manera que el conocimiento de "un poco de karate" es mucho más peligrosa que sin conocer el karate en absoluto. Hay una serie de herramientas de terceros para escribir software seguro en Delphi, que tiene el apoyo de expertos disponibles; Me animo encarecidamente cualquiera sin un profundo conocimiento 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 para usarlos en lugar de tratar de "rodar su propia cuenta."

Para responder a su pregunta específica: La API de Windows tiene una serie de funciones que son útiles, como CryptProtectMemory . Sin embargo, esto traerá una falsa sensación de seguridad si cifrar su memoria, pero tienen un agujero en otra parte del sistema, evite la exposición de canal lateral. Puede ser como poner una cerradura en la puerta, pero dejando la ventana abierta.

¿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;

DLL no son dueños de memoria asignada, procesos hacen. La memoria asignada por su proceso específico será desechado una vez que el proceso termina, si el DLL se cuelga alrededor (porque está en uso por otro proceso) o no.

¿Qué hay de descifrar el archivo a una corriente, usando un procesador de SAX en lugar de un DOM XML para hacer la verificación y después sobrescribir la corriente descifrada antes de liberarlo?

Si utiliza el administrador de memoria FastMM en el modo de depuración completa, entonces se puede obligarlo a sobrescribir la memoria cuando se está liberando.

Normalmente, ese comportamiento se utiliza para detectar punteros salvajes, pero también se puede utilizar para lo que tu quieres.

Por otro lado, asegúrese de que entiende lo que escribe Craig Stuntz:. No escribe esta autenticación y autorización cosas usted mismo, utilice el sistema operativo subyacente siempre que sea posible

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

Saludos,

Jeroen Pluimers

sucia pero se puede hacer una nota del tamaño de la pila que se ha utilizado, mientras que usted tiene la pila llena de datos sensibles que luego, cuando se libera hacen un GetMem que le asigne una gran parte que abarca (por ejemplo) el 200% de eso. hacer un completar en ese trozo y hacer la suposición de que la fragmentación es unlinkely a ser de mucha utilidad a un examinador. Bri

¿Qué hay de mantener la contraseña como un valor hash en el XML y comprobar comparando el hash de la contraseña de entrada con el hash de la contraseña en el código XML.

Editar:. Usted puede mantener todos los datos sensibles cifrados y descifrar sólo en el último momento posible

¿Sería posible cargar el XML descifrado en una matriz de char o byte en lugar de una cadena? Entonces no habría ninguna manipulación de copia por escritura, por lo que sería capaz de rellenar la memoria con # 0 de antes de liberar?

Tenga cuidado si la asignación de arreglo de char a la cadena, como Delphi tiene un manejo inteligente para garantizar la compatibilidad con el régimen tradicional lleno [1..X] de carbón.

Además, se puede utilizar ShortString?

Si su uso de XML, incluso cifrado, para almacenar las contraseñas de sus usuarios poniendo su riesgo. Un mejor enfoque sería la de almacenar los valores hash de las contraseñas en su lugar, y luego comparar el hash en contra de la contraseña introducida. La ventaja de este enfoque es que incluso en conocer el valor hash, que no se sabe la contraseña que hace que el hash. La adición de un identificador de fuerza bruta (contar los intentos de contraseña no válidos, y bloquear la cuenta después de un cierto número) aumentará aún más la seguridad.

Hay varios métodos que puede utilizar para crear un hash de una cadena. Un buen punto de partida sería mirar el proyecto de código abierto de potencia turbo " LockBox ", creo que tiene varios ejemplos de creación de claves de cifrado unidireccional.

Editar

Pero, ¿cómo saber si el valor hash de una manera su ayuda? Si su realmente paranoico, se puede modificar el valor hash por algo prediticable que solamente usted conozca ... digamos, un número aleatorio utilizando un valor específico de semillas además de la fecha. A continuación, puede almacenar sólo lo suficiente de la almohadilla en su XML de modo que se puede utilizar 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", dada la misma semilla.

Tenga cuidado con las funciones que intentan tratar a una cadena como un puntero, y tratar de utilizar FillChar o ZeroMemory para limpiar los contenidos de la cadena.

  • Esto es tanto equivocadas (cadenas están compartidos; que está atornillado a otra persona que está actualmente utilizando la cadena)
  • y puede causar una violación de acceso (si la cadena resulta haber sido una constante, que está sentado en una página de datos de sólo lectura en el espacio de direcciones del proceso, y tratando de escribir en él es una violació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;
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top