Question

Est-il possible de « effacer » des chaînes dans Delphi ?Laisse-moi expliquer:

J'écris une application qui inclura une DLL pour autoriser les utilisateurs.Il lira un fichier crypté dans un DOM XML, utilisera les informations qui s'y trouvent, puis libérera le DOM.

Il est évident que le XML non chiffré se trouve toujours dans la mémoire de la DLL et est donc vulnérable à un examen.Maintenant, je ne vais pas aller trop loin en protégeant cela - l'utilisateur pourrait créer une autre DLL - mais j'aimerais prendre une mesure fondamentale pour empêcher les noms d'utilisateur de rester en mémoire pendant des siècles.Cependant, je ne pense pas pouvoir facilement effacer la mémoire de toute façon à cause des références.Si je traverse mon DOM (qui est une classe TNativeXML) et que je trouve chaque instance de chaîne, puis que j'en fais quelque chose comme "aaaaa", alors cela n'attribuera-t-il pas réellement le nouveau pointeur de chaîne à la référence DOM, puis laisserai l'ancienne chaîne en place là en mémoire en attente de réallocation ?Existe-t-il un moyen d'être sûr que je tue la seule et originale copie ?

Ou existe-t-il dans D2007 un moyen de lui dire d'effacer toute la mémoire inutilisée du tas ?Je pourrais donc libérer le DOM, puis lui dire de l'effacer.

Ou devrais-je simplement passer à ma prochaine tâche et oublier cela, car cela n'en vaut vraiment pas la peine.

Était-ce utile?

La solution

Je ne pense pas que ce soit la peine de continuer, parce que si un utilisateur peut lire la mémoire du processus en utilisant la DLL, le même utilisateur peut également mettre un terme à l'exécution à un moment donné dans le temps. Enrayer l'exécution avant que la mémoire est effacée sera toujours donner à l'utilisateur un accès complet aux données non chiffrées.

OMI tout utilisateur suffisamment intéressés et capables de faire ce que vous décrivez ne sera pas sérieusement incommodés par votre DLL effacer la mémoire.

Autres conseils

Deux points généraux à ce sujet:

Tout d'abord, c'est l'un de ces domaines où « si vous devez demander, vous ne devriez probablement pas faire cela. » Et s'il vous plaît ne prenez pas le mauvais sens; Je ne veux pas manquer de respect à vos compétences en programmation. Il est juste que l'écriture de logiciels sécurisés, cryptographiquement forte est quelque chose qui soit vous êtes un expert ou vous n'êtes pas. Très bien de la même manière que la connaissance « un peu de karaté » est beaucoup plus dangereux que de savoir le karaté pas du tout. Il y a un certain nombre d'outils tiers pour l'écriture de logiciels sécurisés en Delphi qui ont le soutien d'experts disponibles; J'encourage vivement tout le monde sans une connaissance approfondie des services de chiffrement dans Windows, les bases mathématiques de la cryptographie et de l'expérience pour vaincre les attaques de canal latéral pour les utiliser au lieu de tenter de « rouler leur propre. »

Pour répondre à votre question: L'API Windows a un certain nombre de fonctions qui sont utiles, telles que CryptProtectMemory . Cependant, cela apportera un faux sentiment de sécurité si vous chiffrer votre mémoire, mais un trou ailleurs dans le système, ou d'exposer un canal latéral. Il peut être comme mettre un verrou sur la porte, mais en laissant la fenêtre ouverte.

Que diriez-vous quelque chose comme ça?

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 ne possèdent pas la mémoire allouée, de tels processus. La mémoire allouée par votre processus spécifique sera mis au rebut une fois que le processus se termine, que la DLL se bloque autour (parce qu'il est utilisé par un autre processus) ou non.

Que diriez-vous déchiffrer le fichier à un flux, en utilisant un processeur SAX au lieu d'un DOM XML pour faire votre vérification, puis écraser le flux déchiffré avant le libérer?

Si vous utilisez le gestionnaire de mémoire FastMM en mode Débogage complet, vous pouvez le forcer à écraser la mémoire lorsqu'elle est libérée.

Normalement, ce comportement est utilisé pour détecter les pointeurs sauvages, mais il peut également être utilisé pour ce que vous voulez.

D’un autre côté, assurez-vous de bien comprendre ce qu’écrit Craig Stuntz :n'écrivez pas vous-même ces éléments d'authentification et d'autorisation, utilisez le système d'exploitation sous-jacent autant que possible.

D'AILLEURS:Hallvard Vassbotn a écrit un joli blog sur FastMM :http://hallvards.blogspot.com/2007/05/use-full-fastmm-consider-donating.html

Salutations,

Jéroen Pluimers

Messy mais vous pouvez faire une note de la taille du tas que vous avez utilisé pendant que vous avez le tas rempli de données sensibles alors lorsque cela est libéré un GetMem ne vous allouer une grande partie couvrant (disons) 200% de ça. faire un remplissage sur ce morceau et faire l'hypothèse que toute fragmentation est unlinkely être d'une grande utilité à un examinateur. Bri

Comment faire pour garder le mot de passe en tant que valeur de hachage dans le fichier XML et de vérifier en comparant le hachage du mot de passe d'entrée avec le mot de passe haché dans votre XML.

Modifier. Vous pouvez conserver toutes les données sensibles cryptées et décrypter seulement au dernier moment

Serait-il possible de charger le XML déchiffré dans un tableau de char ou octets plutôt qu'une chaîne? Ensuite, il n'y aurait pas de traitement de copie en écriture, alors vous seriez en mesure de remblayer la mémoire avec # 0 avant de libérer?

Soyez prudent si l'attribution tableau de char à chaîne, comme Delphi a une certaine manipulation intelligente ici pour la compatibilité avec gamme tassée traditionnelle [1..x] de charbon.

En outre, vous pouvez utiliser ShortString?

Si votre utilisation de XML, même crypté, pour stocker les mots de passe de vos mettre vos utilisateurs à risque. Une meilleure approche serait de stocker les valeurs de hachage des mots de passe à la place, puis comparer le hachage contre le mot de passe saisi. L'avantage de cette approche est que, même en connaissant la valeur de hachage, vous ne saurez pas le mot de passe qui rend le hachage. Ajout d'un identifiant de force brute (nombre de mots de passe non valide, et verrouiller le compte après un certain nombre) augmentera encore la sécurité.

Il existe plusieurs méthodes que vous pouvez utiliser pour créer un hachage d'une chaîne. Un bon point de départ serait de regarder la puissance turbo projet open source « LockBox », je crois il a plusieurs exemples de création d'une façon clés de hachage.

EDIT

Mais comment connaître la valeur de hachage si son aide d'une façon? Si votre vraiment paranoïaque, vous pouvez modifier la valeur de hachage par quelque chose prediticable que vous seul savez ... disons, un nombre aléatoire en utilisant une valeur spécifique de semences ainsi que la date. Vous pouvez ensuite stocker seulement assez de hachage dans votre xml afin que vous puissiez l'utiliser comme point de départ pour la comparaison. La bonne chose au sujet des générateurs de nombres aléatoires Psuedo est qu'ils génèrent toujours la même série de nombres « aléatoires » étant donné la même graine.

Méfiez-vous des fonctions qui tentent de traiter une chaîne comme un pointeur, et essayez d'utiliser FillChar ou ZeroMemory pour essuyer le contenu de la chaîne.

  • est à la fois ce mal (les chaînes sont partagées, vous vissant quelqu'un d'autre qui est actuellement en utilisant la chaîne)
  • et peut provoquer une violation d'accès (si la chaîne se trouve avoir été une constante, il est assis sur une page de données en lecture seule dans l'espace d'adressage du processus, et en essayant d'y écrire est une violation d'accès)

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;

Une fois que vous avez la version UnicodeString, vous pouvez créer les versions AnsiString et 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;
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top