Domanda

E 'possibile "pulire" le stringhe in Delphi? Mi spiego:

Sto scrivendo un programma che includerà una DLL per autorizzare gli utenti. Si leggerà un file crittografato in un DOM XML, utilizzare le informazioni lì, e quindi rilasciare il DOM.

E 'ovvio che l'XML in chiaro è ancora seduto nella memoria della DLL, e quindi vulnerabile ad esame. Ora, io non ho intenzione di andare in mare nel proteggere questo - l'utente potrebbe creare un altro DLL - ma mi piacerebbe fare un passo fondamentale per prevenire i nomi utente dalla posizione seduta in memoria per le età. Tuttavia, non credo che posso facilmente cancellare la memoria in ogni caso a causa di riferimenti. Se io percorro la mia DOM (che è una classe TNativeXML) e trovare ogni istanza corda e poi trasformarlo in qualcosa come "aaaaa", allora sarà in realtà non assegnare il nuovo puntatore stringa per il riferimento DOM, e poi lasciare la vecchia stringa di seduta lì in memoria in attesa di ri-assegnazione? C'è un modo per essere sicuro che sto uccidendo l'unica e originale la copia?

O c'è in D2007 un mezzo per dirgli di cancellare tutta la memoria inutilizzata dal mucchio? Così ho potuto rilasciare il DOM, e poi dire per pulire.

O devo solo andare avanti con il mio prossimo compito e dimenticare questo perché non è davvero la pena preoccuparsi.

È stato utile?

Soluzione

Non valga la pena assaggiarlo, perché se un utente può leggere la memoria del processo utilizzando la DLL, lo stesso utente può fermare l'esecuzione in un dato punto nel tempo. Arrestare l'esecuzione prima che la memoria viene cancellato sarà ancora dare all'utente l'accesso completo ai dati in chiaro.

IMO qualsiasi utente sufficientemente interessati e in grado di fare ciò che si descrive non verranno seriamente disturbato da DLL pulendo la memoria.

Altri suggerimenti

Due punti generali su questo:

In primo luogo, questo è uno di quei settori in cui "se si deve chiedere, probabilmente non dovrebbe fare questo." E per favore non prendere che il modo sbagliato; Non intendo mancare di rispetto a vostre abilità di programmazione. E 'solo che la scrittura crittografia forte software sicuro, è qualcosa che o sei un esperto o non siete. Molto molto nello stesso modo in cui conoscere "un po 'di karate" è molto più pericoloso di quanto non conoscendo karate a tutti. Ci sono una serie di strumenti di terze parti per la scrittura di software sicuro in Delphi, che ha il supporto degli esperti; Vorrei incoraggiare fortemente chiunque senza una profonda conoscenza dei servizi di crittografia in Windows, i fondamenti matematici della crittografia, e l'esperienza nello sconfiggere canale laterale di usarli invece di tentare di "roll loro".

Per rispondere alla tua domanda specifica: L'API di Windows ha una serie di funzioni che sono utili, come ad esempio CryptProtectMemory . Tuttavia, questo porterà un falso senso di sicurezza, se si crittografare la memoria, ma hanno un buco altrove nel sistema, o esporre un canale laterale. Può essere come mettere un lucchetto alla porta, ma lasciare la finestra aperta.

Che ne dite di qualcosa di simile?

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 non possiedono memoria allocata, processi fanno. La memoria allocata dal processo specifico sarà scartata una volta che il processo termina, se il DLL si aggira (perché è in uso da un altro processo) oppure no.

Che ne dite di decifrare il file su un flusso, utilizzando un processore SAX invece di un DOM XML per fare la tua verifica e quindi sovrascrivere il flusso decifrati prima di liberare esso?

Se si utilizza il gestore di memoria FastMM in modalità Full Debug, allora si può costringerlo a sovrascrivere la memoria quando viene liberato.

Normalmente che il comportamento è utilizzato per rilevare i puntatori selvatici, ma può essere utilizzato anche per che cosa vostra voglia.

D'altra parte, assicurarsi di aver compreso quanto scrive Craig Stuntz:. Non si scrive questa roba l'autenticazione e l'autorizzazione da soli, utilizzare il sistema operativo sottostante, quando possibile

A proposito: Hallvard Vassbotn ha scritto un bel blog su FastMM: http://hallvards.blogspot.com/2007/ 05 / uso-full-FastMM-considerare-donating.html

Saluti,

Jeroen Pluimers

disordinato, ma si potrebbe fare una nota della dimensione heap che hai utilizzato mentre hai il mucchio riempito con i dati sensibili che poi, quando viene rilasciato fare un GetMem di destinare una fetta consistente spanning (diciamo) 200% di quella. fare un riempimento su quel pezzo e fare l'ipotesi che qualsiasi frammentazione è unlinkely essere di grande utilità per un esaminatore. Bri

Come su come mantenere la password come un valore hash in XML e verificare confrontando l'hash della password di ingresso con l'hash della password in XML.

Modifica:. È possibile mantenere tutti i dati sensibili crittografati e decifrare solo all'ultimo momento possibile

Sarebbe possibile caricare il codice XML decifrato in un array di char o byte, piuttosto che una stringa? Poi non ci sarebbe la gestione copy-on-write, così si sarebbe in grado per riempire la memoria con # 0 di prima di liberare?

Fate attenzione se l'assegnazione di array di char a stringa, come Delphi ha qualche movimentazione intelligente qui per la compatibilità con matrice tradizionale imballato [1..x] del char.

Inoltre, si potrebbe usare ShortString?

Se la vostra utilizzando XML, anche criptato, per memorizzare le password vostro mettere gli utenti a rischio. Un approccio migliore potrebbe essere quella di memorizzare i valori hash delle password, invece, e quindi confrontare l'hash contro la password inserita. Il vantaggio di questo approccio è che anche a conoscere il valore di hash, non si conosce la password, che rende l'hash. L'aggiunta di un identificatore di forza bruta (conteggio tentativi di password non validi, e bloccare l'account dopo un certo numero) aumenterà ulteriormente la sicurezza.

Ci sono diversi metodi che è possibile utilizzare per creare un hash di una stringa. Un buon punto di partenza sarebbe quello di guardare il progetto open source di alimentazione turbo " LockBox ", credo ha diversi esempi di creazione di chiavi di hash a senso unico.

Modifica

Ma come fa conoscere il valore di hash se il suo aiuto in un modo? Se il vostro veramente paranoico, è possibile modificare il valore hash da qualcosa prediticable che solo si sa ... diciamo, un numero casuale utilizzando un valore seme specifica più la data. È quindi possibile immagazzinare abbastanza solo l'hash in XML in modo è possibile utilizzarlo come punto di partenza per il confronto. La cosa bella di pseudo generatori di numeri casuali è che essi generano sempre la stessa serie di numeri "casuali" dato lo stesso seme.

Fare attenzione di funzioni che cercano di trattare una stringa come un puntatore, e tenta di utilizzare FillChar o ZeroMemory per pulire il contenuto della stringa.

  • Questo è sia sbagliato (stringhe vengono condivisi, si sta avvitando qualcun altro che sta attualmente utilizzando la stringa)
  • e può causare una violazione di accesso (se la stringa capita di essere stato una costante, si è seduto su una pagina di dati di sola lettura nello spazio di indirizzi del processo, e cercando di scrivere su di esso è una violazione di accesso)

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 volta che avete la versione UnicodeString, è possibile creare le versioni AnsiString e 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;
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top