Search code examples
c++builder-6ansistring

Proper way to zero a AnsiString object


How do we zero a AnsiString object? For example:

void TesteClass::test()
{
    AnsiString cardNumber = "1234567890123456";
    ..
}

The AnsiString object is destroyed automatically, but its internal data is not cleaned, so a memory acquisition tool can read the information. We need to zero this data to avoid sensitive information be captured in memory.

The AnsiString class has the method c_str() to access the internal data directly, but doing some like this is not recommended:

memset(cardNumber.c_str(), 0, cardNumber.Length());

What is the correct way to zero the AnsiString internal data before the object is destroyed?


Solution

  • There is nothing wrong with using memset() like you have shown. Using c_str() in this manner is correct (as long as you do not exceed the Length(), since c_str() returns a pointer to const memory if the AnsiString is blank):

    void TesteClass::test()
    {
        AnsiString cardNumber = "1234567890123456";
        // ...
        memset(cardNumber.c_str(), 0, cardNumber.Length());
    }
    

    Since you are worried about information leakage, consider using SecureZeroMemory() instead of memset() (see What’s the point of SecureZeroMemory?).

    To automate the zeroing (so you don't have to remember to do it), consider wrapping the AnsiString inside an RAII-style class/struct (you cannot derive from AnsiString directly, the RTL does not allow it), whose destructor performs the zeroing, and then use that class/struct where needed instead of using AnsiString directly.

    Just be careful, since AnsiString uses reference-counted data, so don't zero the data unless your AnsiString is the sole instance referencing the data:

    class SaferAnsiString
    {
    private:
        AnsiString m_Str;
    
        void CheckZeroNeeded()
        {
            // AnsiString gained a public RefCount() method in C++Builder 2009.
            // In earlier versions, access the reference count manually...
            #ifdef _DELPHI_STRING_UNICODE
            if (m_Str.RefCount() == 1)
            #else
            const void *data = m_Str.data();
            if ((data) && (static_cast<const int*>(data)[-2] == 1))
            #endif
                SecureZeroMemory(m_Str.c_str(), m_Str.Length());
        }
    
    public:
        SaferAnsiString(const AnsiString &src = AnsiString())
            : m_Str(src)
        {
        }
    
        ~SaferAnsiString()
        {
            CheckZeroNeeded();
        }
    
        SaferAnsiString& operator=(const AnsiString &rhs)
        {
            CheckZeroNeeded();
            m_Str = rhs;
            return *this;
        }
    
        operator AnsiString () const { return m_Str; }
    
        // other methods as needed...
    };
    

    void TesteClass::test()
    {
        SaferAnsiString cardNumber = "1234567890123456";
        // ...
    }