how does one securely clear std::string?
Based on the answer given here, I wrote an allocator to securely zero memory.
#include <string>
#include <windows.h>
namespace secure
{
template <class T> class allocator : public std::allocator<T>
{
public:
template<class U> struct rebind { typedef allocator<U> other; };
allocator() throw() {}
allocator(const allocator &) throw() {}
template <class U> allocator(const allocator<U>&) throw() {}
void deallocate(pointer p, size_type num)
{
SecureZeroMemory((void *)p, num);
std::allocator<T>::deallocate(p, num);
}
};
typedef std::basic_string<char, std::char_traits<char>, allocator<char> > string;
}
int main()
{
{
secure::string bar("bar");
secure::string longbar("bHow Does One Securely Clear Std::StringHow Does One Securely Clear Std::StringHow Does One Securely Clear Std::StringHow Does One Securely Clear Std::Stringaaaaar");
}
}
However, it turns out, depending on how std::string
is implemented, it is possible that the allocator isn't even invoked for small values. In my code, for example, the deallocate
doesn't even get called for the string bar
(on Visual Studio).
The answer, then, is that we cannot use std::string to store sensitive data. Of course, we have the option to write a new class that handles the use case, but I was specifically interested in using std::string
as defined.
Thanks everyone for your help!
how does one securely clear std::string?
Based on the answer given here, I wrote an allocator to securely zero memory.
#include <string>
#include <windows.h>
namespace secure
{
template <class T> class allocator : public std::allocator<T>
{
public:
template<class U> struct rebind { typedef allocator<U> other; };
allocator() throw() {}
allocator(const allocator &) throw() {}
template <class U> allocator(const allocator<U>&) throw() {}
void deallocate(pointer p, size_type num)
{
SecureZeroMemory((void *)p, num);
std::allocator<T>::deallocate(p, num);
}
};
typedef std::basic_string<char, std::char_traits<char>, allocator<char> > string;
}
int main()
{
{
secure::string bar("bar");
secure::string longbar("bHow Does One Securely Clear Std::StringHow Does One Securely Clear Std::StringHow Does One Securely Clear Std::StringHow Does One Securely Clear Std::Stringaaaaar");
}
}
However, it turns out, depending on how std::string
is implemented, it is possible that the allocator isn't even invoked for small values. In my code, for example, the deallocate
doesn't even get called for the string bar
(on Visual Studio).
The answer, then, is that we cannot use std::string to store sensitive data. Of course, we have the option to write a new class that handles the use case, but I was specifically interested in using std::string
as defined.
Thanks everyone for your help!
how does one securely clear std::string?
Based on the answer given here, I wrote an allocator to securely zero memory.
#include <string>
#include <windows.h>
namespace secure
{
template <class T> class allocator : public std::allocator<T>
{
public:
template<class U> struct rebind { typedef allocator<U> other; };
allocator() throw() {}
allocator(const allocator &) throw() {}
template <class U> allocator(const allocator<U>&) throw() {}
void deallocate(pointer p, size_type num)
{
SecureZeroMemory((void *)p, num);
std::allocator<T>::deallocate(p, num);
}
};
typedef std::basic_string<char, std::char_traits<char>, allocator<char> > string;
}
int main()
{
{
secure::string bar("bar");
secure::string longbar("bHow Does One Securely Clear Std::StringHow Does One Securely Clear Std::StringHow Does One Securely Clear Std::StringHow Does One Securely Clear Std::Stringaaaaar");
}
}
However, it turns out, depending on how std::string
is implemented, it is possible that the allocator isn't even invoked for small values. In my code, for example, the deallocate
doesn't even get called for the string bar
(on Visual Studio).
The answer, then, is that we cannot use std::string to store sensitive data. Of course, we have the option to write a new class that handles the use case, but I was specifically interested in using std::string
as defined.
Thanks everyone for your help!
Securely resizing and deletion of std::string
It's not guaranteed that c_str()
will point to the original buffer and not make a copy. That's probably the way it works, but there's no way to be sure without looking at your specific implementation of basic_string
.
There are enough potential problems with making std::wstring
secure that I'd avoid it entirely and find a secure string class or write my own.
How secure are string values?
Yes, 1874
would be stored in memory and in the executable.
You could use a cryptography library to salt and hash the correct pin and store the salt and hashed value. In the past I have used crypto++. This would be almost impossible to try and figure out the original value by working with the hashed value, they would have to brute force it. Although the rest of your executable is not secure so they could just hack out the comparison of the response and the correct pin.
How do you clear a stringstream variable?
For all the standard library types the member function empty()
is a query, not a command, i.e. it means "are you empty?" not "please throw away your contents".
The clear()
member function is inherited from ios
and is used to clear the error state of the stream, e.g. if a file stream has the error state set to eofbit
(end-of-file), then calling clear()
will set the error state back to goodbit
(no error).
For clearing the contents of a stringstream
, using:
m.str("");
is correct, although using:
m.str(std::string());
is technically more efficient, because you avoid invoking the std::string
constructor that takes const char*
. But any compiler these days should be able to generate the same code in both cases - so I would just go with whatever is more readable.
How to cleanse (overwrite with random bytes) std::string internal buffer?
It is probably safe. But not guaranteed.
However, since C++11
, a std::string
must be implemented as contiguous data so you can safely access its internal array using the address of its first element &secretString[0]
.
if(!secretString.empty()) // avoid UB
{
char* modifiable = &secretString[0];
OpenSSL_cleanse(modifiable, secretString.size());
}
How do you safely clear an object from memory (with attributes) which was created using the new keyword?
Yes. When you delete b
it deletes also letters_in_box
array.
But, for your b->letters_in_box = "Hello world";
you will get a compile error: "error C3863: array type 'char [80]' is not assignable"
#include <memory> // For 'memcpy_s' (since C11)
class Box
{
public:
double length;
char letters_in_box[80];
};
int main()
{
Box* b = new Box;
b->length = 2.0;
// b->letters_in_box = "Hello world"; ** Compile Error C3863: array type 'char [80]' is not assignable **
memcpy_s(b->letters_in_box, sizeof(b->letters_in_box), "Hello world", sizeof("Hello world"));
// Some code with b
delete b;
}
MODERN C++
A better practice than new
is smart pointers, than for example you don't have to bother with delete in case of exception and at all:
#include <memory> // For 'std::unique_ptr' and for 'memcpy_s'
class Box
{
public:
double length;
char letters_in_box[80];
};
constexpr char my_text[] = "Hello world";
int main()
{
auto b = std::make_unique<Box>(); // No need to delete
b->length = 2.0;
memcpy_s(b->letters_in_box, sizeof(b->letters_in_box), my_text, sizeof(my_text));
// Some code with b
}
Also, (instead of C array) I prefer to use C++ array:
#include <array> // For 'std::array'
#include <memory> // For 'std::unique_ptr' and for 'memcpy_s'
class Box
{
public:
double length;
std::array<char, 80> letters_in_box;
};
constexpr char my_text[] = "Hello world";
int main()
{
auto b = std::make_unique<Box>(); // No need to delete
b->length = 2.0;
memcpy_s(&b->letters_in_box, b->letters_in_box.size(), my_text, sizeof(my_text));
//Some code with b
}
--
One last comment: Without a constraint to use char[]
, I would use std::string
instead:
#include <string> // For 'std::string'
#include <memory> // For 'std::unique_ptr'
class Box
{
public:
double length;
std::string letters_in_box;
};
int main()
{
auto b = std::make_unique<Box>(); // No need to delete
b->length = 2.0;
b->letters_in_box = "Hello world";
//Some code with b
}
Related Topics
C++ Compare Char Array with String
Range Based For-Loop on Array Passed to Non-Main Function
When Is an Object "Out of Scope"
Move-Only Version of Std::Function
Sendinput() Not Equal to Pressing Key Manually on Keyboard in C++
Error Lnk2005: Already Defined - C++
C++ Exception:Throwing Std::String
Performance of Unsigned VS Signed Integers
#Define Nominmax Using Std::Min/Max
Vector of Const Objects Giving Compile Error
How to Specify Preference of Library Path
How to Correctly and Standardly Compare Floats
C++ Compiler Error C2280 "Attempting to Reference a Deleted Function" in Visual Studio 2013 and 2015
Create a Function to Check for Key Press in Unix Using Ncurses