c++ - smart - programar contratos inteligentes
¿Cómo se borra de forma segura std:: string? (4)
¿Cómo se almacenan los datos confidenciales (por ejemplo, contraseñas) en std::string
?
Tengo una aplicación que le pide al usuario una contraseña y la pasa a un servidor secundario durante la configuración de la conexión. Quiero borrar de forma segura el valor de la contraseña después de que se haya establecido la conexión.
Si SecureZeroMemory la contraseña como una matriz char *
, puedo usar API como SecureZeroMemory para eliminar los datos confidenciales de la memoria de proceso. Sin embargo, quiero evitar matrices de caracteres en mi código y estoy buscando algo similar para std::string
?
Basado en la respuesta dada here , escribí un asignador para cero memoria de forma segura.
#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");
}
}
Sin embargo, resulta que, dependiendo de cómo se implementa std::string
, es posible que el asignador ni siquiera se invoque para valores pequeños. En mi código, por ejemplo, no se llama al deallocate para la bar
cadenas (en Visual Studio).
La respuesta, entonces, es que no podemos usar std :: string para almacenar datos confidenciales. Por supuesto, tenemos la opción de escribir una nueva clase que maneje el caso de uso, pero estaba específicamente interesado en usar std::string
como se define.
¡Gracias a todos por su ayuda!
Para la posteridad, una vez decidí ignorar este consejo y usar std :: string de todos modos, y escribí un método cero () usando c_str () (y desechando la constancia) y volátil. Si tuve cuidado y no causé una reasignación / movimiento de los contenidos, y llamé manualmente a cero () donde lo necesitaba limpio, todo parecía funcionar correctamente. Lamentablemente, descubrí otra falla seria de la manera más difícil: std :: string también puede ser un objeto contado referenciado ... explotar la memoria en c_str () (o la memoria a la que apunta el objeto al que se hace referencia) destruirá el otro objeto sin saberlo .
std :: string se basa en un char *. En algún lugar detrás de toda la magia dinámica como un personaje. Entonces, cuando dices que no quieres usar char * ''s en tu código, aún estás usando un char *, está en el fondo con un montón de basura acumulada encima de él.
No tengo demasiada experiencia con la memoria de proceso, pero siempre podría recorrer cada carácter (después de haber cifrado y almacenado la contraseña en una base de datos?) Y establecerla en un valor diferente.
También hay un std :: basic_string, pero no estoy seguro de qué ayuda haría eso por ti.
std::string mystring;
...
std::fill(mystring.begin(), mystring.end(), 0);
O incluso mejor escribe tu propia función:
void clear(std::string &v)
{
std::fill(v.begin(), v.end(), 0);
}