Как безопасно очистить std::string?
Как хранить конфиденциальные данные (например: пароли) в std::string
?
У меня есть приложение, которое запрашивает у пользователя пароль и передает его нисходящему серверу во время установки соединения. Я хочу безопасно очистить значение пароля после установления соединения.
Если я храню пароль как массив char *
, я могу использовать API, например SecureZeroMemory, чтобы избавиться от конфиденциальных данных из память процесса. Тем не менее, я хочу избежать char массивов в моем коде и ищу что-то похожее для std::string
?
Ответы
Ответ 1
Основываясь на ответе здесь, я написал распределитель для надежной нулевой памяти.
#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("baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaar");
}
}
Однако, оказывается, в зависимости от того, как реализовано std::string
, возможно, что распределитель даже не вызывается для небольших значений. В моем коде, например, deallocate
даже не вызывается для строки bar
(в Visual Studio).
Таким образом, ответ заключается в том, что мы не можем использовать std::string для хранения конфиденциальных данных. Конечно, у нас есть возможность написать новый класс, который обрабатывает прецедент, но я был особо заинтересован в использовании std::string
как определено.
Спасибо всем за вашу помощь!
Ответ 2
std::string основан на char *. Где-то позади все динамическое волшебство как char *. Поэтому, когда вы говорите, что не хотите использовать char * в своем коде, вы по-прежнему используете char *, это просто в фоновом режиме с целым куском другого мусора, сложенного поверх него.
Я не слишком опытен с памятью процесса, но вы всегда можете перебирать каждый символ (после того как вы зашифровали и сохранили пароль в БД?) и установили его в другое значение.
Также есть std:: basic_string, но я не уверен, что поможет вам.
Ответ 3
Для потомков я когда-то решил игнорировать этот совет и использовать std::string в любом случае, и написал метод zero(), используя c_str() (и отбрасывая константу) и volatile. Если бы я был осторожен и не вызывал перераспределения/перемещения содержимого, и я вручную вызывал нуль(), где мне было нужно его очистить, все, казалось, функционировали должным образом. Увы, я обнаружил еще один серьезный недостаток: std::string также может быть объектом с подсчетом счета... взрывная память в c_str() (или память, на которую ссылается объект, на который ссылается объект) бессознательно взрывает другой объект.
Ответ 4
std::string mystring;
...
std::fill(mystring.begin(), mystring.end(), 0);
или даже лучше написать свою собственную функцию:
void clear(std::string &v)
{
std::fill(v.begin(), v.end(), 0);
}