Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Secure BSTR and Other Data Wrappers

0.00/5 (No votes)
15 Oct 2003 1  
This atricle introduces a secure wrapper framework for any in-memory data class and discusses its application to the Microsoft bstr_t and CComBSTR wrappers for the COM data type BSTR.

Introduction

A recent project where security is important and the use of COM BSTR strings is required was the impulse for developing the secure wrapper framework. Security coding rules mandate that:
  • Sensitive data should be left in plain text in memory for as short a time as possible
  • When a variable goes out of scope its contents should be overwritten with a particular pattern immediately
  • When sensitive data is passed over COM it must be encrypted
As you know, there are two main Microsoft-supplied wrapper classes for the BSTR data type: CComBSTR and _bstr_t. They both have their good and bad points and developers on the project use them both. Rather than write a third wrapper I decided to build on these two well-known, reliable classes, giving them the additional security features we required.

Features Required

I came up with the following list of requirements to satisfy the requirements of the project and to make development simple and as similar to using the regular wrappers as possible:

  • Data should be kept hidden in memory
  • Data should be revealed in memory only when required
  • Data should be monitored for how long it has been revealed
  • Data should be positively wiped when no longer needed
  • Data for COM transmission should be encrypted and decrypted
  • New wrappers should be able to interact with existing wrappers
  • Performance should not be noticeably affected
  • The new features should be as automatic as possible

Hiding the Data

Secure data in memory is obfuscated so that hackers using a memory scan or memory dump to file cannot easily see it. The rule that is followed is that the data is normally hidden, being revealed only when needed and then for as short a time as possible. This rule is implemented in the methods of the BSTR wrappers by making sure that data is hidden unless the method itself calls for exposure � such as copying out to a BSTR.

I chose to hide the data using an efficient obfuscation technique based on a rolling exclusive OR. This is functionality is held in an Obfuscator class which can be altered independently of other code if a more robust method is required. Note that this feature is different from the encryption required for communication.

Revealing the Data

Data should be revealed, that is available in clear text in memory, for as short a time as possible. You reveal the data by using one of the extractor or casting functions, for instance:
Secure::_bstr_t 
strData(aBSTR,true);  // hidden

const char *szData;
.
.
.
szData = (const char *)strData; // revealed

.
.
.
strData.HideData();  // hidden � so is szData

This reveals the data since szData is a pointer straight into an underlying member variable and so must be in clear text. Hiding the data will have the effect of also hiding the data pointed to by szData. Clearly, care must be taken not to hide the data too early in situations like this.

Exposure Monitoring

Data is revealed or exposed when it is available in clear text in memory, that is when it is not obfuscated. The role of exposure monitoring is to detect instances of secure data having been exposed for longer than a programmable time and to do something about them.

There are some obvious limitations on exposure monitoring. For instance if you take a copy of the data to an insecure area when it is exposed then hide the original data quickly, exposure monitoring will not detect this yet there is still an exposed copy of the data in memory. You should deal with this as described in the next section.

To monitor overexposed data I chose to implement a separate thread and a simple map of currently exposed data. The methods that reveal and hide secure data keep the map updated. You can start and stop declaratively or programmatically by wrapper type. The abstract base classes, described later, implement this thread.

When data has been exposed for too long, you can get the thread to call your callback routine or simply to re-hide the data.

Your callback routine should have the following signature:

typedef void (*pfnExposedDataCallback)(const DataHolder * pData);

Wiping Data

When data goes out of scope or is deleted it is positively overwritten with a defined pattern. A single non-class Wipe function writes the pattern FE EE FE EE� to memory. Destructors of data holding classes call this function which is also available to be used on insecure copies of secure data immediately after the data is no longer needed.

Encrypting for COM Transmission

I chose the strong AES symmetric encryption using 128 bit keys and a randomly generated initialization vector. The encrypted data includes the length of the original data and a SHA1 hash of the data. The data is encrypted first with a key available to all software components then encrypted again with a Diffie-Hellman ephemeral key negotiated between the two COM partners. I use base64 encoding of the entire blob to make the encrypted data easily transmissible via COM as a string.

This may be the subject of a separate KB. Because of issues of performance, security and key management these features are kept separate from the wrapper classes and are not presented here.

Existing Wrappers

I took a hard look at the implementation of the existing wrapper classes to determine what changes to make.

Of the two wrappers _bstr_t is more complicated, having a contained class, Data_t. Data_t is reference-counted and can hold the data for several _bstr_t objects having the same value.

For instance consider the following code fragment:

_bstr_t strA(TEXT(�secret�);
_bstr_t strB(strA);
_bstr_t strC;

strC = strB;
The above code results in three_bstr_t objects but only one Data_t object, which is shared between the three _bstr_ts. I decided to keep the contained class but to de-implement Data_t sharing because of problems that would arise with clashes of data being revealed in one _bstr_t object but hidden in another when they shared a common Data_t object. The CComBSTR wrapper is a much more straightforward, thinner class, holding the data itself.

Since the new wrappers classes mimic the original builtin classes I decided to keep the same names but to place them, along with the rest of the implementation, in the Secure namespace. Interoperability is built in to the new wrappers. For instance a Secure _bstr_t and an original one can be constructed from each other, be compared to each other and be set to each other.

Abstract Base Classes

To implement the required features I created two abstract base classes which contain new shared functionality and mandate a few extra pure virtual methods on the new wrapper classes. These abstract base classes are DataWrapper and DataHolder. DataWrapper is the externally used wrapper class (like _bstr_t) and DataHolder holds the data (like Data_t).

DataWrapper

This class is inherited by the new versions of _bstr_t and CComBSTR since they are the wrappers. It implements additional required functionality associated with controlling exposure monitoring and mandates a method to return the DataWrapper�s DataHolder object.

DataHolder

This class is inherited by the new versions of Data_t and CCOMBSTR since that is where the data is held. It implements the bulk of the functionality associated with data hiding and revelation as well as exposure monitoring. It mandates methods to determine which internal data is to be hidden and revealed.

Overall Class Structure

So in outline we have the following class structure:

Namespace Secure
{
    inline void Wipe(void * pData, size_t size);

    class Obfuscator { };

    template<class T>class Checker{};

    class DataHolder{};

    class DataWrapper{};

    class _bstr_t : public DataWrapper
    {
        class Data_t : public DataHolder{};
    };

    class CComBSTR : public DataWrapper, public DataHolder{};
};
Let�s look at each of the classes in a bit more detail.

Checker

Checker is a convenience class used to declaratively start and stop exposure monitoring.
template<class T
class Checker
{
public:
    inline Checker<T>(bool bStart = true,
                            pfnExposedDataCallback callback = NULL);
    inline ~Checker<T>();
};
You can control exposure monitoring simply by declaring an object of Checker:
Secure::Checker<CComBSTR> BSTRMonitor(true, MyCallBack);
When the object goes out of scope, the exposure monitoring thread stops.

DataHolder

DataHolder is the largest class containing methods to handle data hiding and revelation as well as exposure monitoring.

The structure EXPOSEDATA helps the exposure monitoring thread, its controlling methods and the Hide and Reveal methods. The contained ExposeMap is a standard template library map holding details of currently revealed data. The Reveal method adds to the map and the Hide method removes entries from the map.

class DataHolder
{
public:
    typedef std::map<const DataHolder *,_timeb>ExposeMap;
    typedef struct EXPOSEDATA
    {
        ExposeMap Map;
        CRITICAL_SECTION Crit
        HANDLE hThread;
        HANDLE hWake;
        bool bThreadActive;
        unsigned long lWaitTime;
        double dblMaxExposeTime;
        pfnExposedDataCallback callback;
    };

    DataHolder()
    inline void Hide() const;
    inline void Reveal() const;
    bool IsHidden() const;
    inline static void StartExposeThread(pfnExposedDataCallback callback,
                       double maxExposeTime, unsigned long waitTime);
    inline static void CloseExposeThread();
    inline void Obfuscate(void * pData, size_t size, unsigned char seed);
    inline void Deobfuscate(void * pData, size_t size, unsigned char seed);

protected:
    virtual void HideMe() const = 0;
    virtual void RevealMe() const = 0;
    void SeedAndHide() const;

    mutable bool m_hidden;
    mutable unsigned char m_seed;
    Obfuscator *m_obfuscator;

private:
    inline static EXPOSEDATA * EMap();
    inline static DWORD WINAPI ExposeThread(LPVOID param);
};
Inheriting classes must implement the HideMe and RevealMe methods because only they know where the data is.

The implementation for Data_t is very small, m_wstr is the unicode string and m_str the ASCII string.

inline void _bstr_t::Data_t::HideMe() const
{
    Obfuscate(m_wstr, ::SysStringByteLen(m_wstr),m_seed);
    Obfuscate(m_str, ::SysStringLen(m_wstr), m_seed);
}

inline void _bstr_t::Data_t::RevealMe() const
{
    Deobfuscate(m_wstr, ::SysStringByteLen(m_wstr), m_seed);
    Deobfuscate(m_str, ::SysStringLen(m_wstr), m_seed);
}
and for CComBSTR there is only an optional unicode string.
inline void CComBSTR::HideMe() const
{
    if (m_str != NULL)
    {
        Obfuscate(m_str, ::SysStringByteLen(m_str),m_seed);
    }
}

inline void CComBSTR::RevealMe() const
{
    if (m_str != NULL)
    {
        Deobfuscate(m_str, ::SysStringByteLen(m_str),m_seed);
    }
}

DataWrapper

DataWrapper implements control methods for exposure monitoring and mandates inheriting classes with a single pure virtual method, Data, concerning the location of the DataHolder data object.
class DataWrapper
{
public:
    virtual const DataHolder * Data() const=0;
    void HideData() const;
    void RevealData() const;
    virtual bool IsDataHidden() const;
    inline static StartExposeChecking(pfnExposedDataCallback callback,
                       double maxExposeTime = MAXEXPOSETIME,
                       unsigned long waitTime = EXPOSEHEARTBEATMILLISECONDS);
    inline static void StopExposeChecking();
};
We require the Data method to be implemented by the derived class because the base class does not know how the derived class will reference the DataHolder class.

Again, the implementation of the method is quite small.

For _bstr_t the DataHolder class is held in class variable m_Data:

inline const DataHolder * _bstr_t::Data() const
{
    return m_Data;
}
and for CComBSTR, which also implements DataHolder itself:
inline const DataHolder *CComBSTR::Data() const
{
    return this;
}

Obfuscator

Obfuscator contains the code to hide and reveal in-memory data. I currently use a rolling, seeded exclusive OR technique that is very fast. It is not a very strong encryption but acceptable for protecting in-memory, transient data. A stronger method may be included later.
class Obfuscator
{
public:
    void Obfuscate (void * pData, size_t size, unsigned char seed) const;
    void Deobfuscate (void * pData, size_t size, unsigned char seed) const;

private:
    void Xor(void * pData, size_t size, unsigned char seed) const
};

Changes to Existing Wrappers

Many of methods need changing in small but similar ways. Almost all of the changes are associated with data hiding. I followed these rules:
  • All constructors and assignment methods hide the data on receipt.
  • The data remains hidden, apart for brief internal revelation/hiding changes, until it is required externally
  • When a method operates on another wrapper object it leaves that object in the same hidden state as it found it.
For instance the comparison operator !:
inline bool _bstr_t::operator!() const throw()
{
    return (m_Data != NULL) ? !m_Data->GetWString() : true;
}
becomes
inline bool _bstr_t::operator!() const throw()
{
    bool Rtn;
    bool bHidden = IsDataHidden();

    RevealData();
    Rtn = (m_Data != NULL) ? !m_Data->GetWString() : true;

    if (bHidden)
        HideData();
}
The changes here are to determine if the data is currently hidden, then reveal it. After the main work of determining the return value the data is hidden if it was hidden at the start of the method.

Another example in the contained Data_t class of a constructor:

inline _bstr_t::Data_t::Data_t(const char* s) throw(_com_error)
            : m_str(NULL), m_RefCount(1)
{
    m_wstr = _com_util::ConvertStringToBSTR(s);

    if (m_wstr == NULL && s != NULL)
    {
        _com_issue_error(E_OUTOFMEMORY);
    }
}
becomes
inline _bstr_t::Data_t::Data_t(const char* s) throw(_com_error)
            : m_str(NULL), m_RefCount(1)
{
    m_wstr = _com_util::ConvertStringToBSTR(s);

    if (m_wstr == NULL && s != NULL)
    {
        _com_issue_error(E_OUTOFMEMORY);
    }

    SeedAndHide();
}
At the end of the method, the DataHolder�s SeedAndHide method is called to calculate a seed value for obfuscation and to hide the data.

Secure Wrappers in Use

After extracting the data you can copy the data elsewhere then call the HideData method or let the exposure monitoring thread deal with it. You may also choose to use the Secure::Wipe method to wipe other areas of memory containing sensitive data that is no longer required:
STDMETHODIMP CLASS:Method(BSTR bstrInput,�)
{
    // Acquire the input parameter into a secure string

    //

    Secure::_bstr_t strSensitiveData(bstrInput);

    // Extract as a plain const char *

    //

    const char * pExtract = (const char *)strSensitiveData;

    // Copy to another char *

    //

    size_t uiSize = strlen(pExtract) + 1;
    char * pCopy = new char[uiSize];
    strcpy(pCopy, pExtract);

    // Hide the secure string

    //

    strSensitiveData.HideData();
    .
    .
    .
    // Wipe the insecure copy

    //

    Secure::Wipe(pCopy, uiSize);
    delete[] pCopy;
    .
    .
    .
} 
For situations where a whole C++ file or project requires mainly secure wrappers you can default to secure wrappers but to continue using the original wrappers using the _ibstr_t original wrapper. This example also shows the use of the exposure monitoring callback function.
#define SECURE_BSTR_T
#include �SecureBstrT.h�

void MyCallback(const DataHolder * pData)
{
    const char * szData = (const char *)(*pData);

    // Record the insecure exposure somewhere

    .
    .
    .
    // Hide the data

    pData->Hide();
}

Secure::Checker<_bstr_t> BstrCheck(true, MyCallback);


HRESULT CDemo::Method(BSTR bstrInput, BSTR * pbstrOutput)
{
    _bstr_t strSensitiveData(bstrInput);  // Secure

    _ibstrt strNormalData;                // Insecure

    .
    .
    .
    strNormalData = strSensitiveData;     // Exposed

    .
    .
    .
}

Summary

An effective, simple to use, extensible security-enhanced class framework has been developed.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here