Pregunta

Necesito almacenar información sensible (una clave de cifrado simétrico que quiero mantener en privado) en mi aplicación de C++.El enfoque simple para hacer esto:

std::string myKey = "mysupersupersecretpasswordthatyouwillneverguess";

Sin embargo, la ejecución de la aplicación a través de la strings proceso (o cualquier otro que los extractos de las cadenas de un binario de la aplicación) revelará la cadena anterior.

¿Qué técnicas deben ser utilizadas para ocultar los datos sensibles?

Editar:

OK, así que casi todos te han dicho "su ejecutable se puede hacer ingeniería inversa" - ¡por supuesto!Este es un animal que pone de mal humor de la mía, así que me voy a despotricar un poco aquí:

¿Por qué es que el 99% (OK, así que tal vez exagero un poco) de todos los relacionados con la seguridad de preguntas en este sitio son respondidas con un torrente de "no hay manera posible para crear un perfecto programa de seguridad" - que no es una respuesta útil!La seguridad es una escala móvil perfecto entre la usabilidad y la seguridad no en un extremo, y la seguridad perfecta, pero no de la usabilidad en el otro.

El punto es que usted escoja su posición en que la escala móvil en función de lo que estamos tratando de hacer y el entorno en el que el software funcionará. No voy a escribir una aplicación para una instalación militar, estoy escribiendo una aplicación para un PC de casa.Necesito para cifrar los datos a través de una red de confianza con un pre-conoce la clave de cifrado.En estos casos, la "seguridad por oscuridad" es probablemente lo suficientemente bueno!Claro, alguien con suficiente tiempo, energía y habilidad podría invertir la ingeniería del binario y encontrar la contraseña, pero ¿adivinen qué?No me importa:

El tiempo que me lleva a implementar un top-notch sistema seguro es más caro que la pérdida de ventas debido a la agrietado versiones (no es que yo realmente estoy vendiendo esto, pero usted consigue mi punto).Este cielo azul "vamos a hacerlo de la mejor manera posible" tendencia en la programación entre los nuevos programadores es tonto decir lo menos.

Gracias por tomarse el tiempo para responder a esta pregunta - que eran más útiles.Por desgracia, sólo puedo aceptar una respuesta, pero he de seguridad de votación de todas las respuestas útiles.

¿Fue útil?

Solución

Básicamente, cualquier persona con acceso a su programa, y un depurador puede y se encontrar la clave en la aplicación si se desea.

Pero, si usted sólo quiere asegurarse de que la clave no se muestra cuando se ejecuta strings en su binario, usted podría, por ejemplo, asegúrese de que la clave no está dentro de la imprimible gama.

Ocultando clave con XOR

Por ejemplo, usted podría usar XOR para dividir la clave en dos matrices de bytes:

key = key1 XOR key2

Si usted cree key1 con el mismo byte de longitud como key usted puede utilizar (completamente aleatoria los valores de byte y, a continuación, calcular key2:

key1[n] = crypto_grade_random_number(0..255)
key2[n] = key[n] XOR key1[n]

Usted puede hacer esto en su entorno, y, a continuación, sólo de la tienda key1y key2 en su solicitud.

La protección de su binario

Otro enfoque es usar una herramienta para proteger su binario.Por ejemplo, hay varias herramientas de seguridad que puede asegurarse de que el binario se confunden y se inicia una máquina virtual que se ejecuta en.Esto hace que sea difícil(er) para depurar, y también los convencionales de manera que muchas de grado comercial de aplicaciones de seguridad (también, por desgracia, malware) está protegido.

Una de las principales herramientas es Protección de themida, que hace un trabajo impresionante de proteger tus archivos binarios.Es a menudo utilizado por los reconocidos programas, tales como Spotify, para proteger contra la ingeniería inversa.Tiene características para evitar la depuración de programas, tales como OllyDbg y el Ida Pro.

También hay una lista más grande, tal vez un poco anticuado, de herramientas para proteger tu binario.
Algunos de ellos son gratuitos.

Contraseña coincidencia

Alguien que discutimos aquí hash de la contraseña+sal.

Si usted necesita para almacenar la clave para coincidir con algún tipo de usuario enviado la contraseña, usted debe utilizar un one-way hash function, preferiblemente mediante la combinación de nombre de usuario, contraseña y una de sal.El problema con esto, sin embargo, es que su aplicación tiene que saber la sal para ser capaz de hacer un camino y comparar el resultado de hashes.Por lo tanto, usted todavía necesita para almacenar la sal en algún lugar de su aplicación.Pero, como @Edward señala en los comentarios de abajo, de esta forma, proteger contra un ataque de diccionario de usar, e.g, tablas de arco iris.

Por último, puede utilizar una combinación de todas las técnicas anteriores.

Otros consejos

Antes que nada, date cuenta de que no hay nada que puedas hacer que detenga a un hacker lo suficientemente decidido, y hay muchos de esos alrededor. La protección en cada juego y consola está agrietada eventualmente, por lo que esto es solo una solución temporal.

Hay 4 cosas que puede hacer que aumentarán sus posibilidades de permanecer oculto por un tiempo.

1) Ocultar los elementos de la cadena de alguna manera: algo obvio como xoring (el operador ^) la cadena con otra cadena será lo suficientemente buena como para que la cadena sea imposible de buscar.

2) Divida la cadena en pedazos: divida su cadena y haga estallar fragmentos de ella en métodos con nombres extraños en módulos extraños. No facilite buscar y encontrar el método con la cadena. Por supuesto, algún método tendrá que llamar a todos estos bits, pero aún lo hace un poco más difícil.

3) Nunca construyas la cadena en la memoria: la mayoría de los hackers usan herramientas que les permiten ver la cadena en la memoria después de haberla codificado. Si es posible, evite esto. Si, por ejemplo, está enviando la clave a un servidor, envíela carácter por carácter, de modo que la cadena completa nunca esté alrededor. Por supuesto, si lo está usando desde algo como la codificación RSA, entonces esto es más complicado.

4) Haga un algoritmo ad-hoc: además de todo esto, agregue un giro único o dos. Tal vez solo agregue 1 a todo lo que produce, o haga cualquier cifrado dos veces, o agregue un azúcar. Esto hace que sea un poco más difícil para el hacker que ya sabe qué buscar cuando alguien está usando, por ejemplo, hasilla md5 vanilla o cifrado RSA.

¡Sobre todo, asegúrese de que no sea demasiado importante cuándo (y lo será si su aplicación se vuelve lo suficientemente popular) cuando descubra su clave!

Una estrategia que he usado en el pasado es crear una serie de personajes aparentemente aleatorios. Inicialmente inserta y luego localiza sus caracteres particulares con un proceso algebraico donde cada paso de 0 a N producirá un número & Lt; tamaño de la matriz que contiene el siguiente carácter en su cadena ofuscada. (¡Esta respuesta se siente ofuscada ahora!)

Ejemplo:

Dado un conjunto de caracteres (los números y guiones son solo de referencia)

0123456789
----------
ALFHNFELKD
LKFKFLEHGT
FLKRKLFRFK
FJFJJFJ!JL

Y una ecuación cuyos primeros seis resultados son: 3, 6, 7, 10, 21, 47

Daría la palabra " ¡HOLA! " del conjunto de arriba.

Estoy de acuerdo con @Checkers, tu ejecutable puede ser modificado por ingeniería inversa.

Una forma un poco mejor es crearlo dinámicamente, por ejemplo:

std::string myKey = part1() + part2() + ... + partN();

He creado una herramienta de cifrado simple para cadenas, puede generar cadenas cifradas automáticamente y tiene algunas opciones adicionales para hacerlo, algunos ejemplos:

Cadena como variable global:

// myKey = "mysupersupersecretpasswordthatyouwillneverguess";
unsigned char myKey[48] = { 0xCF, 0x34, 0xF8, 0x5F, 0x5C, 0x3D, 0x22, 0x13, 0xB4, 0xF3, 0x63, 0x7E, 0x6B, 0x34, 0x01, 0xB7, 0xDB, 0x89, 0x9A, 0xB5, 0x1B, 0x22, 0xD4, 0x29, 0xE6, 0x7C, 0x43, 0x0B, 0x27, 0x00, 0x91, 0x5F, 0x14, 0x39, 0xED, 0x74, 0x7D, 0x4B, 0x22, 0x04, 0x48, 0x49, 0xF1, 0x88, 0xBE, 0x29, 0x1F, 0x27 };

myKey[30] -= 0x18;
myKey[39] -= 0x8E;
myKey[3] += 0x16;
myKey[1] += 0x45;
myKey[0] ^= 0xA2;
myKey[24] += 0x8C;
myKey[44] ^= 0xDB;
myKey[15] ^= 0xC5;
myKey[7] += 0x60;
myKey[27] ^= 0x63;
myKey[37] += 0x23;
myKey[2] ^= 0x8B;
myKey[25] ^= 0x18;
myKey[12] ^= 0x18;
myKey[14] ^= 0x62;
myKey[11] ^= 0x0C;
myKey[13] += 0x31;
myKey[6] -= 0xB0;
myKey[22] ^= 0xA3;
myKey[43] += 0xED;
myKey[29] -= 0x8C;
myKey[38] ^= 0x47;
myKey[19] -= 0x54;
myKey[33] -= 0xC2;
myKey[40] += 0x1D;
myKey[20] -= 0xA8;
myKey[34] ^= 0x84;
myKey[8] += 0xC1;
myKey[28] -= 0xC6;
myKey[18] -= 0x2A;
myKey[17] -= 0x15;
myKey[4] ^= 0x2C;
myKey[9] -= 0x83;
myKey[26] += 0x31;
myKey[10] ^= 0x06;
myKey[16] += 0x8A;
myKey[42] += 0x76;
myKey[5] ^= 0x58;
myKey[23] ^= 0x46;
myKey[32] += 0x61;
myKey[41] ^= 0x3B;
myKey[31] ^= 0x30;
myKey[46] ^= 0x6C;
myKey[35] -= 0x08;
myKey[36] ^= 0x11;
myKey[45] -= 0xB6;
myKey[21] += 0x51;
myKey[47] += 0xD9;

Como cadena unicode con ciclo de descifrado:

// myKey = "mysupersupersecretpasswordthatyouwillneverguess";
wchar_t myKey[48];

myKey[21] = 0x00A6;
myKey[10] = 0x00B0;
myKey[29] = 0x00A1;
myKey[22] = 0x00A2;
myKey[19] = 0x00B4;
myKey[33] = 0x00A2;
myKey[0] = 0x00B8;
myKey[32] = 0x00A0;
myKey[16] = 0x00B0;
myKey[40] = 0x00B0;
myKey[4] = 0x00A5;
myKey[26] = 0x00A1;
myKey[18] = 0x00A5;
myKey[17] = 0x00A1;
myKey[8] = 0x00A0;
myKey[36] = 0x00B9;
myKey[34] = 0x00BC;
myKey[44] = 0x00B0;
myKey[30] = 0x00AC;
myKey[23] = 0x00BA;
myKey[35] = 0x00B9;
myKey[25] = 0x00B1;
myKey[6] = 0x00A7;
myKey[27] = 0x00BD;
myKey[45] = 0x00A6;
myKey[3] = 0x00A0;
myKey[28] = 0x00B4;
myKey[14] = 0x00B6;
myKey[7] = 0x00A6;
myKey[11] = 0x00A7;
myKey[13] = 0x00B0;
myKey[39] = 0x00A3;
myKey[9] = 0x00A5;
myKey[2] = 0x00A6;
myKey[24] = 0x00A7;
myKey[46] = 0x00A6;
myKey[43] = 0x00A0;
myKey[37] = 0x00BB;
myKey[41] = 0x00A7;
myKey[15] = 0x00A7;
myKey[31] = 0x00BA;
myKey[1] = 0x00AC;
myKey[47] = 0x00D5;
myKey[20] = 0x00A6;
myKey[5] = 0x00B0;
myKey[38] = 0x00B0;
myKey[42] = 0x00B2;
myKey[12] = 0x00A6;

for (unsigned int fngdouk = 0; fngdouk < 48; fngdouk++) myKey[fngdouk] ^= 0x00D5;

Cadena como variable global:

// myKey = "mysupersupersecretpasswordthatyouwillneverguess";
unsigned char myKey[48] = { 0xAF, 0xBB, 0xB5, 0xB7, 0xB2, 0xA7, 0xB4, 0xB5, 0xB7, 0xB2, 0xA7, 0xB4, 0xB5, 0xA7, 0xA5, 0xB4, 0xA7, 0xB6, 0xB2, 0xA3, 0xB5, 0xB5, 0xB9, 0xB1, 0xB4, 0xA6, 0xB6, 0xAA, 0xA3, 0xB6, 0xBB, 0xB1, 0xB7, 0xB9, 0xAB, 0xAE, 0xAE, 0xB0, 0xA7, 0xB8, 0xA7, 0xB4, 0xA9, 0xB7, 0xA7, 0xB5, 0xB5, 0x42 };

for (unsigned int dzxykdo = 0; dzxykdo < 48; dzxykdo++) myKey[dzxykdo] -= 0x42;

Por supuesto, almacenar datos privados en un software que se envía al usuario siempre es un riesgo. Cualquier ingeniero suficientemente educado (y dedicado) podría realizar ingeniería inversa de los datos.

Dicho esto, a menudo puede hacer que las cosas sean lo suficientemente seguras al levantar la barrera que las personas deben superar para revelar sus datos privados. Eso suele ser un buen compromiso.

En su caso, podría saturar sus cadenas con datos no imprimibles y luego decodificarlos en tiempo de ejecución utilizando una función auxiliar simple, como esta:

void unscramble( char *s )
{
    for ( char *str = s + 1; *str != 0; str += 2 ) {
        *s++ = *str;
    }
    *s = '\0';
}

void f()
{
    char privateStr[] = "\001H\002e\003l\004l\005o";
    unscramble( privateStr ); // privateStr is 'Hello' now.

    string s = privateStr;
    // ...
}

Depende un poco de lo que intente proteger como señala joshperry. Por experiencia, diría que si es parte de algún esquema de licencia para proteger su software, no se moleste. Eventualmente lo realizarán ingeniería inversa. Simplemente use un cifrado simple como ROT-13 para protegerlo de ataques simples (líneas que se ejecutan sobre él). Si se trata de proteger los datos confidenciales de los usuarios, me preguntaría si proteger esos datos con una clave privada almacenada localmente es un movimiento inteligente. Nuevamente, todo se reduce a lo que está tratando de proteger.

EDITAR: si lo vas a hacer, entonces una combinación de técnicas que Chris señala será mucho mejor que rot13.

Como se dijo antes, no hay forma de proteger totalmente su cadena. Pero hay formas de protegerlo con una seguridad razonable.

Cuando tuve que hacer esto, puse una cadena de aspecto inocente en el código (un aviso de derechos de autor, por ejemplo, o algún aviso de usuario falso o cualquier otra cosa que no sea cambiada por alguien que arregle un código no relacionado), encriptado que se usó a sí mismo como una clave, mezcló eso (agregando un poco de sal) y usó el resultado como una clave para cifrar lo que realmente quería cifrar.

Por supuesto, esto podría ser pirateado, pero se necesita un hacker determinado para hacerlo.

En lugar de almacenar la clave privada en su ejecutable, es posible que desee solicitarla al usuario y almacenarla mediante un administrador de contraseñas , algo similar a Mac OS X Keychain Access.

Si está en el usuario DPAPI de Windows, http://msdn.microsoft .com / es-es / biblioteca / ms995355.aspx

Como dijo una publicación anterior si estás en Mac usa el llavero.

Básicamente, todas estas ideas lindas sobre cómo almacenar su clave privada dentro de su binario son lo suficientemente pobres desde una perspectiva de seguridad que no debería hacerlas. Cualquier persona que obtenga su clave privada es un gran problema, no la guarde dentro de su programa. Dependiendo de qué tan importante sea su aplicación, puede guardar sus claves privadas en una tarjeta inteligente, en una computadora remota con la que habla su código o puede hacer lo que la mayoría de la gente hace y guardarla en un lugar muy seguro en la computadora local (¡ quot; key store " que es algo así como un registro seguro extraño) que está protegido por permisos y toda la fuerza de su sistema operativo.

Este es un problema resuelto y la respuesta NO es mantener la clave dentro de su programa :)

Pruebe esto . El código fuente explica cómo cifrar y descifrar sobre la marcha todas las cadenas en un proyecto dado de Visual Studio c ++.

Un método recientemente he probado es:

  1. Tomar hash (SHA256) de los datos privados y rellenarla en el código como part1
  2. Tomar XOR de los datos privados y su hash y rellenar en el código como part2
  3. Llenar los datos:No almacene como char str[], sino poblar en tiempo de ejecución mediante la asignación de instrucciones (como se muestra en la macro a continuación)
  4. Ahora, generar los datos privados en el tiempo de ejecución de tomar el XOR de part1 y part2
  5. Paso adicional:Calcular el hash de los datos generados y compararlo con part1.Verificar la integridad de los datos privados.

MACRO para llenar los datos:

Supongamos que, privada de datos es de 4 bytes.Podemos definir una macro para que el que guarda los datos con la asignación de instrucciones en algún orden aleatorio.

#define POPULATE_DATA(str, i0, i1, i2, i3)\
{\
    char *p = str;\
    p[3] = i3;\
    p[2] = i2;\
    p[0] = i0;\
    p[1] = i1;\
}

Ahora uso esta macro en el código donde usted necesita para guardar part1 y part2, como sigue:

char part1[4] = {0};
char part2[4] = {0};
POPULATE_DATA(part1, 1, 2, 3, 4); 
POPULATE_DATA(part2, 5, 6, 7, 8);

Depende del contexto, pero puede almacenar el hash de la clave más una sal (cadena constante, fácil de ocultar).

Luego, cuando (si) el usuario ingresa la clave, agrega la sal , calcula el hash y compara.

La sal es probablemente innecesaria en este caso, detiene un ataque de diccionario de fuerza bruta si se puede aislar el hash (también se sabe que una búsqueda en Google funciona).

Un pirata informático solo tiene que insertar una instrucción jmp en algún lugar para omitir todo el lote, pero eso es bastante más complicado que una simple búsqueda de texto.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top