Pergunta

Eu preciso armazenar informações confidenciais (a chave de criptografia simétrica que eu quero manter privado) no meu aplicativo C ++. A abordagem é simples de fazer isso:

std::string myKey = "mysupersupersecretpasswordthatyouwillneverguess";

No entanto, a executar a aplicação através do processo strings (ou qualquer outro que extractos cordas de um aplicativo de binário) irão revelar a cadeia acima.

O que técnicas devem ser usadas para obscurecer tais dados confidenciais?

Editar:

OK, então praticamente todos vocês disseram "seu executável pode ser a engenharia reversa" - é claro! Esta é uma implicância minha, então eu vou reclamar um pouco aqui:

Por que é que 99% (OK, então talvez eu exagerei um pouco) de todas as questões relacionadas à segurança neste site são respondidas com uma torrente de "não há nenhuma maneira possível para criar um programa perfeitamente seguro" - que é não uma resposta útil! A segurança é uma escala móvel entre perfeita usabilidade e nenhuma segurança em uma extremidade, e uma segurança perfeita, mas nenhuma usabilidade na outra.

O ponto é que você escolhe a sua posição no que escala móvel, dependendo do que você está tentando fazer e o ambiente em que o software será executado. Eu não estou escrevendo um app para uma instalação militar, eu estou escrevendo um aplicativo para um PC em casa . Eu preciso para criptografar dados através de uma rede não confiável com uma chave de criptografia pré-conhecido. Nestes casos, "segurança pela obscuridade" é provavelmente bom o suficiente! Claro, alguém com bastante tempo, energia e habilidade poderia fazer engenharia reversa do binário e encontrar a senha, mas adivinhem? Eu não me importo:

O tempo que me leva para implementar um sistema seguro de alto nível é mais caro do que a perda de vendas devido às versões rachados (não que eu estou realmente vendendo isso, mas você começa o meu ponto). Este azul-céu "permite fazê-lo da melhor maneira absoluta possível" tendência na programação entre os novos programadores é tolo para dizer o mínimo.

Obrigado por tomar o tempo para responder a esta pergunta - eles foram mais útil. Infelizmente, só pode aceitar uma resposta, mas eu tenho up-votado todas as respostas úteis.

Foi útil?

Solução

Basicamente, qualquer pessoa com acesso ao seu programa e um depurador pode e irá encontrar a chave na aplicação, se quiserem.

Mas, se você quiser apenas para se certificar de que a chave não aparecem durante a execução strings em seu binário, você poderia, por exemplo make certeza que a chave não está dentro do intervalo de impressão.

chave Obscurecer com XOR

Por exemplo, você poderia usar XOR para dividir a chave em duas matrizes de bytes:

key = key1 XOR key2

Se você criar key1 com o mesmo byte de comprimento como key você pode usar (totalmente) valores de bytes aleatórios e key2 então compute:

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

Você pode fazer isso em seu ambiente de construção, e apenas armazenar key1 key2and na sua aplicação.

Proteger o seu binário

Outra abordagem é usar uma ferramenta para proteger o seu binário. Por exemplo, existem várias ferramentas de segurança que podem fazer se o seu binário é ofuscado e inicia uma máquina virtual que roda em. Isso torna difícil (er) para depurar, e é também a maneira convential muitas aplicações seguras classe comercial (também, infelizmente, malware) está protegida.

Uma das ferramentas premier é Themida , que faz um trabalho incrível de proteger seus binários. Ele é frequentemente usado por programas conhecidos, como Spotify, para proteger contra engenharia reversa. Ele tem características para impedir a depuração de programas como OllyDbg e Ida Pro.

Há também uma lista maior, talvez um pouco desatualizado, de ferramentas para proteger seu binário.
Alguns deles são gratuitos.

correspondência de senha

Alguém aqui discutido senha hashing + sal.

Se você precisa armazenar a chave para combiná-lo contra algum tipo de enviado pelo usuário senha, você deve usar uma função hash unidirecional, de preferência através da combinação de nome de usuário, senha e um sal. O problema com isso, porém, é que a sua aplicação tem de saber o sal para ser capaz de fazer o one-way e comparar os hashes resultantes. Assim, portanto, você ainda precisa para armazenar a algum lugar sal em sua aplicação. Mas, como @Edward aponta nos comentários abaixo, isto irá proteger eficazmente contra um ataque de dicionário usando, por exemplo, as tabelas do arco-íris.

Finalmente, você pode usar uma combinação de todas as técnicas acima.

Outras dicas

Em primeiro lugar, perceber que não há nada que você pode fazer que vai parar suficientemente hacker determinado, e há uma abundância de pessoas à sua volta. A proteção em todos os jogos e consolar redor está rachado, eventualmente, por isso esta é apenas uma correção temporária.

Existem 4 coisas que você pode fazer que irá aumentar as suas chances de permanecer escondido por um tempo.

1) Ocultar os elementos da cadeia de alguma forma -. Algo óbvio como XORing (o ^ operador) a corda com uma outra corda vai ser bom o suficiente para fazer a corda impossível procurar

2) dividir a string em pedaços - split-se a cadeia e pop partes dele em métodos nome estranho em módulos estranhos. Não torná-lo fácil de pesquisar e encontrar o método com a corda nele. É claro que alguns método terá que chamar todos esses bits, mas ainda faz com que seja um pouco mais difícil.

3) Nunca construir a string na memória - a maioria dos hackers usam ferramentas que lhes permitem ver a cadeia na memória depois de ter codificado-lo. Se possível, evite isso. Se por exemplo você está enviando a chave para um servidor, enviá-lo caractere por caractere, por isso toda a corda não está ao redor. Claro, se você estiver usando-lo de algo como codificação RSA, então este é mais complicado.

4) Faça um ad-hoc algoritmo - no topo de tudo isso, adicionar um único toque ou dois. Talvez basta adicionar 1 a tudo o que produzem, ou fazer qualquer criptografia duas vezes, ou adicionar um açúcar. Isso só faz com que seja um pouco mais difícil para o hacker que já sabe o que procurar quando alguém está usando, por exemplo, baunilha hash MD5 ou RSA criptografia.

Acima de tudo, certifique-se que não é muito importante quando (e ele vai ser quando se aplicação torna-se bastante popular) sua chave é descoberto!

Uma estratégia que usei no passado é criar uma matriz de caracteres aparentemente aleatórios. Está inicialmente inserir, e, em seguida, localizar os caracteres particulares com um processo onde cada passo algébrica de 0 a N irá produzir um número

Exemplo:

Dado um conjunto de caracteres (números e traços são apenas para referência)

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

E uma equação cujo primeiro seis resultados são: 3, 6, 7, 10, 21, 47

renderia a palavra "OLÁ!" a partir da matriz acima.

Eu concordo com @Checkers, o executável pode ser engenharia reversa.

Um pouco melhor maneira é criar dinamicamente, por exemplo:

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

Eu criei uma ferramenta de criptografia simples para cordas, que pode gerar automaticamente seqüências criptografadas e tem algumas opções extras para fazer isso, alguns exemplos:

String como uma variável 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 string unicode com loop de decodificação:

// 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;

String como uma variável 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;

Claro, armazenar dados privados em software que é fornecido para o usuário é sempre um risco. Qualquer engenheiro suficientemente educado (e dedicada) poderia reverter a engenharia de dados.

Dito isto, muitas vezes você pode tornar as coisas bastante seguro, elevando a barreira que as pessoas precisam para superar a revelar os seus dados privados. Isso é geralmente um bom compromisso.

No seu caso, você poderia desordenar suas cordas com dados não-imprimíveis, e depois de decodificação que em tempo de execução usando uma função simples ajudante, assim:

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;
    // ...
}

Um pouco dependente do que você está tentando proteger como pontos joshperry fora. Da experiência, eu diria que se é parte de um regime de licenciamento para proteger o seu software, em seguida, não se incomode. Eles vão eventially fazer engenharia reversa. Basta usar uma simples cifra como ROT-13 para protegê-lo de ataques simples (linha traçada cordas sobre ele). Se é para proteger os usuários de dados sensíveis eu estaria questionando se proteger esses dados com uma chave privada armazenada localmente é um movimento sábio. Mais uma vez ele se resume ao que você está tentando proteger.

EDIT:. Se você estiver indo para fazê-lo, em seguida, uma combinação de técnicas que Chris assinala será muito melhor do que rot13

Como foi dito antes, não há nenhuma maneira de proteger totalmente a sua string. Mas existem maneiras de protegê-la wis uma segurança razoável.

Quando eu tinha que fazer isso, eu colocar alguma corda que olha inocente no código (um aviso de direitos autorais, por exemplo, ou algum usuário falsificado alerta ou qualquer outra coisa que não vai ser alterado por alguém fixação código não relacionada), criptografada que usando-se como uma chave, hash que (adicionando um pouco de sal), e usou o resultado como uma chave para criptografar o que eu realmente queria para criptografar.

Claro que isso poderia ser cortado, mas faz exame de um hacker determinado a fazê-lo.

Em vez de armazenar a chave privada no seu executável, você pode querer solicitá-lo a partir do usuário e armazená-lo por meio de um link externo gerenciador de senhas , algo semelhante ao Mac OS X Keychain Access.

Se você estiver em janelas DPAPI usuário, http://msdn.microsoft .com / en-us / library / ms995355.aspx

Como um post anterior disse que se você está no mac usar o chaveiro.

Basicamente todas estas ideias bonitos sobre como armazenar a sua chave privada dentro do seu binário são suficientemente pobre de uma perspectiva de segurança que você não deve fazê-las. Qualquer pessoa obter a sua chave privada é um grande negócio, não mantê-lo dentro de seu programa. Dependendo de como importar seu aplicativo é que você pode manter suas chaves privadas em um cartão inteligente, em um computador remoto suas conversas código ou você pode fazer o que a maioria das pessoas e mantê-lo em um lugar muito seguro no computador local (a "chave loja", que é como uma espécie de registro seguro estranho) que estão protegidos pela permissões e toda a força de seu sistema operacional.

Este é um problema resolvido e a resposta não é manter a chave dentro do seu programa:)

Tente este . O código-fonte explica como criptografar e descriptografar a todas as cadeias voar em um determinado projeto Visual C ++ Estúdio.

Um método que eu tentei recentemente é:

  1. Tome de hash (SHA256) dos dados privados e preenchê-lo em código como part1
  2. Tome XOR de dados privados e seu hash e preenchê-lo em código como part2
  3. dados Preencher: Não armazená-lo como char str [], mas populate em tempo de execução usando instruções de atribuição (como mostrado na macro abaixo)
  4. Agora, gerar os dados privados no tempo de execução, tomando o XOR de part1 e part2
  5. Passo adicional : Calcular Hash de dados gerados e compará-lo com part1. Ele vai verificar a integridade dos dados privados.

MACRO aos dados Preencher:

dados Suponha que, privado é de 4 bytes. Nós definimos uma macro para isso que salva os dados com instruções da atribuição de alguma ordem aleatória.

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

Agora, usar essa macro no código onde você precisa salvar part1 e part2, da seguinte forma:

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

Contexto dependente, mas você pode simplesmente armazenar o de hash da chave, mais um sal (string constante, fácil de obscurecer).

Então, quando (se) o usuário entra a chave, você adicionar o sal , calcular o de hash e comparar.

O sal é provavelmente desnecessária, neste caso, ele pára um ataque de dicionário de força bruta se o hash pode ser isolado (uma pesquisa no Google também tem sido saber para trabalhar).

Um hacker ainda só tem que inserir um lugar instrução JMP para ignorar o lote inteiro, mas isso é um pouco mais complicado do que uma simples busca de texto.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top