Pergunta

É possível "limpar" strings em Delphi? Deixe-me explicar:

Eu estou escrevendo um aplicativo que irá incluir uma DLL para usuários Autorizar. Ele irá ler um arquivo criptografado em um DOM XML, use as informações lá, e então liberar o DOM.

É óbvio que o XML sem criptografia ainda está sentado na memória do DLL, e, portanto, vulnerável a exame. Agora, eu não estou indo para ir ao mar em proteger este - o usuário poderia criar uma outra DLL - mas eu gostaria de dar um passo fundamental para a prevenção de nomes de usuário de sentar-se na memória para as idades. No entanto, eu não acho que eu posso facilmente apagar a memória de qualquer maneira por causa das referências. Se eu atravessar meu DOM (que é uma classe TNativeXML) e encontrar todos os casos de cordas e, em seguida, transformá-la em algo como "aaaaa", então não realmente atribuir o novo ponteiro de string com a referência DOM, em seguida, deixar o assento velho corda lá na memória aguardando re-alocação? Existe uma maneira de ter certeza que eu estou matando a única e cópia original?

Ou há no meio D2007 um para contar-o para limpar toda a memória não utilizada da pilha? Assim eu poderia liberar o DOM e, em seguida, dizer-lhe para limpar.

Ou devo apenas continuar com minha próxima tarefa e esquecer isso, porque é realmente não vale a pena incomodar.

Foi útil?

Solução

Eu não acho que vale a pena incomodar-se com, porque se um usuário pode ler a memória do processo usando o DLL, o mesmo usuário também pode suspender a execução em qualquer ponto no tempo. Travar a execução antes da memória é apagado ainda vai dar ao usuário acesso completo aos dados sem criptografia.

IMO qualquer usuário suficientemente interessado e capaz de fazer o que você descreve não será seriamente incomodados por sua DLL limpar a memória.

Outras dicas

Dois pontos gerais sobre este:

Em primeiro lugar, esta é uma daquelas áreas onde "se você tem que perguntar, você provavelmente não deveria estar fazendo isso." E por favor não levar isso da maneira errada; Eu quero dizer nenhum desrespeito a suas habilidades de programação. É que escrever software seguro, criptografia forte é algo que ou você é um especialista no ou você não é. Muito da mesma forma que conhecer "um pouco de karate" é muito mais perigoso do que saber não karate em tudo. Há uma série de ferramentas de terceiros para escrever software seguro em Delphi, que tem o apoio de especialistas disponíveis; Encorajo vivamente ninguém sem um profundo conhecimento de serviços de criptografia no Windows, os fundamentos matemáticos da criptografia e experiência em derrotar os ataques de canal lateral para usá-los em vez de tentar "rolar seus próprios."

Para responder à sua pergunta específica: A API do Windows tem uma série de funções que são úteis, tais como CryptProtectMemory . No entanto, isso vai trazer uma falsa sensação de segurança se você criptografar sua memória, mas tem um buraco em outras partes do sistema ou expor um canal lateral. Pode ser como colocar um bloqueio em sua porta, mas deixando a janela aberta.

Como sobre algo assim?

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;

DLLs não possuem memória alocada, os processos de fazer. A memória alocada pelo seu processo específico será descartado uma vez que os termina de processo, se os trava DLL ao redor (porque ele está em uso por outro processo) ou não.

Como cerca de descriptografar o arquivo para um fluxo, usando um processador SAX em vez de um DOM XML para fazer a sua verificação e, em seguida, substituindo o fluxo descriptografado antes de libertá-lo?

Se você usar o Gerenciador de memória FastMM no modo de depuração completo, então você pode forçá-lo a memória de substituição quando ele está sendo liberado.

Normalmente esse comportamento é usado para detectar ponteiros selvagens, mas também pode ser usado para o que seu querer.

Por outro lado, certifique-se de compreender o que Craig Stuntz escreve:. Não escrevo esta autenticação e autorização coisas mesmo, usar o sistema subjacente operando sempre que possível

BTW: Hallvard Vassbotn escreveu um bom blog sobre FastMM: http://hallvards.blogspot.com/2007/ 05 / uso-full-FastMM-consideram-donating.html

Saudações,

Jeroen Pluimers

confuso, mas você poderia fazer uma nota do tamanho da pilha que você usou quando você tem o heap preenchido com dados sensíveis, em seguida, quando que é liberado que um GetMem para atribuir-lhe um grande pedaço abrangendo (digamos) 200% por essa. fazer um Fill naquele pedaço e fazer a suposição de que qualquer fragmentação é unlinkely ser de muita utilidade para um examinador. Bri

Como sobre manter a senha como um valor hash no XML e verificar através da comparação do hash da senha de entrada com o hash de senha em seu XML.

Edit:. Você pode manter todos os dados sensíveis criptografados e descriptografar apenas no último momento possível

Seria possível carregar o XML decifrada em uma matriz de char ou byte em vez de uma string? Então não haveria manipulação de copy-on-write, para que você seria capaz de aterrar a memória com # 0 do antes de liberar?

Tenha cuidado se atribuir matriz de char de corda, como Delphi tem alguma manipulação inteligente aqui para compatibilidade com variedade embalado tradicional [1..x] de carvão.

Além disso, você poderia usar ShortString?

Se o seu usando XML, mesmo criptografada, para armazenar senhas seu colocando seus usuários em risco. Uma abordagem melhor seria armazenar os valores de hash das senhas em vez disso, e então comparar o hash contra a senha digitada. A vantagem dessa abordagem é que, mesmo em saber o valor de hash, você não vai saber a senha que torna o hash. Adicionando um identificador de força bruta (contagem tentativas de senha inválidos, e bloquear a conta após um determinado número) vai aumentar ainda mais a segurança.

Existem vários métodos que você pode usar para criar um hash de uma string. Um bom ponto de partida seria a de olhar para o projeto open source poder turbo " LockBox ", eu acredito ele tem vários exemplos de criação de chaves de hash de sentido único.

Editar

Mas como saber o valor de hash se o seu one-way ajuda? Se o seu realmente paranóico, você pode modificar o valor de hash por algo prediticable que só você saberia ... digamos, um número aleatório usando um valor específico das sementes mais a data. Você poderia, então, armazenar apenas o suficiente do hash na sua xml para que você possa usá-lo como um ponto de partida para a comparação. A coisa agradável sobre geradores de números pseudo aleatórios é que eles sempre gerar a mesma série de números "aleatórios" dadas a mesma semente.

Tenha cuidado de funções que tentam tratar uma string como um ponteiro, e tentar usar FillChar ou ZeroMemory para limpar o conteúdo da seqüência.

  • isso é tanto errada (cordas são compartilhados, você está enroscando alguém que está atualmente usando a string)
  • e pode causar uma violação de acesso (se a cadeia acontece de ter sido uma constante, ele está sentado em uma página de dados somente leitura no espaço de endereço do processo, e tentar escrever é uma violação de acesso)

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;

Uma vez que você tem a versão UnicodeString, você pode criar as versões 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;
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top