18

This question follows a suggestion made by @sharptooth in this related question.

Can std::string be tweaked so that it becomes password-safe ?

If not, what would be the guidelines to write a password-handling class (thus a class that takes big care about what it writes to memory and clears it before destruction) ?

Community
  • 1
  • 1
ereOn
  • 53,676
  • 39
  • 161
  • 238
  • 1
    1) use std:wstring, 2) push 0x2022 in instead of password symbols :o) Seriously, if you fear that other process sniffs something from your memory, add/remove some mask to it (xor?) – alxx Sep 24 '10 at 09:04
  • @alxx: How would using `std::wstring` instead of `std::string` be safer ?! Am I missing something ? – ereOn Sep 24 '10 at 09:06
  • 1
    0x2022 is bullet symbol. Get it? :) – alxx Sep 24 '10 at 09:10
  • @alxx: Ok, I finally get the joke ;) Ciphering the strings in memory doesn't help much: if I cipher my strings, both my cipher key and cipher algorithm will be in memory as well. I'm really looking for guidelines to ensure that memory is not copied around too much and automatically reset before destruction. – ereOn Sep 24 '10 at 09:16
  • 2
    @alxx: using wstring is unnecessary, you can push `"\xe2\x80\xa2"` into `std::string`. – Yakov Galka Jan 07 '16 at 16:51

1 Answers1

24

Yes, first define a custom allocator:

template <class T> class SecureAllocator : public std::allocator<T>
{
public:
    template<class U> struct rebind { typedef SecureAllocator<U> other; };

    SecureAllocator() throw() {}
    SecureAllocator(const SecureAllocator&) throw() {}
    template <class U> SecureAllocator(const SecureAllocator<U>&) throw() {}

    void deallocate(pointer p, size_type n)
    {
        std::fill_n((volatile char*)p, n*sizeof(T), 0);
        std::allocator<T>::deallocate(p, n);
    }
};

This allocator zeros the memory before deallocating. Now you typedef:

typedef std::basic_string<char, std::char_traits<char>, SecureAllocator<char>> SecureString;

However there is a small problem, std::string may use small string optimization and store some data inside itself, without dynamic allocation. So you must explicitly clear it on destruction or allocate on the heap with our custom allocator:

int main(int, char**)
{
    using boost::shared_ptr;
    using boost::allocate_shared;
    shared_ptr<SecureString> str = allocate_shared<SecureString>(SecureAllocator<SecureString>(), "aaa");

}

This guarantees that all the data is zeroed before deallocation, including the size of the string, for example.

Yakov Galka
  • 70,775
  • 16
  • 139
  • 220
  • 5
    Don't use `std::fill_n`, use something like `SecureZeroMemory()` (http://msdn.microsoft.com/en-us/library/aa366877(VS.85).aspx) - with `std::fill_n` the compiler might optimize the erasing away. – sharptooth Sep 24 '10 at 09:21
  • Also is there a tweak to force `std::string` erase the space when the calling code causes the string to be shortened - for example removes first N elements and so the tail is moved "leftwards" and leaves characters "on the right"? – sharptooth Sep 24 '10 at 09:22
  • 3
    @sharptooth SecureZeroMemory is not standard, and volatile prevents optimization. Yes, if there is no system call some data may remain in memory before the CPU flushes the cache. – Yakov Galka Sep 24 '10 at 09:23
  • 4
    @ybungalobill: Not sure, but looks like a neat way of using `volatile` for good. – sharptooth Sep 24 '10 at 09:24
  • @sharptooth in C++0x there is shrink_to_fit, however it's not binding request. To force shrinking you can do a copy and swap. (Well, this also won't work with reference counted string... so I guess that you can't do it with string. You **can** do it with vector, since it destructs the elements, so you need to zero the memory in SecureAllocator::destroy too). – Yakov Galka Sep 24 '10 at 09:26
  • @ybungalobill: Thanks for this complete answer. Would you mind adding a non-boost version of the last code sample ? Using boost where I work is *sadly* not an option... I can use `tr1::shared_ptr` thought. – ereOn Sep 24 '10 at 09:34
  • @ereOn you can use tr1::shared_ptr with custom deleter. std::allocate_shared is going to be included in C++0x. – Yakov Galka Sep 24 '10 at 09:42
  • @ybungalobill: congratz on your first K :-) – Matthieu M. Sep 24 '10 at 09:43
  • Well, if one takes a peak into the respective library-implementation, one could replace the destructor for that template-instantiation, with a version first doing what a non-modified version would do (free with the secure allocator), and then overwriting the string-object itself. Thereafter no need for the smart-pointer... – Deduplicator Oct 18 '15 at 02:40
  • 4
    The allocator should disable swapping of the page it allocates memory in by calling [`mlock`](http://man7.org/linux/man-pages/man2/mlock.2.html). Otherwise the data may be written to disk. –  Jan 07 '16 at 15:46