How can I increase memory security in Delphi?

前端 未结 11 1669
醉酒成梦
醉酒成梦 2020-12-11 09:50

Is it possible to \"wipe\" strings in Delphi? Let me explain:

I am writing an application that will include a DLL to authorise users. It will read an encrypted file

11条回答
  •  陌清茗
    陌清茗 (楼主)
    2020-12-11 10:21

    Be careful of functions that try to treat a string as a pointer, and try to use FillChar or ZeroMemory to wipe the string contents.

    • this is both wrong (strings are shared; you're screwing someone else who's currently using the string)
    • and can cause an access violation (if the string happens to have been a constant, it is sitting on a read-only data page in the process address space; and trying to write to it is an access violation)

     

    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;
    

    Once you have the UnicodeString version, you can create the AnsiString and WideString versions:

    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;
    

提交回复
热议问题