Preciso armazenar informações confidenciais (uma chave de criptografia simétrica que desejo manter particular) em meu aplicativo C ++. A abordagem simples é fazer isso:
std::string myKey = "mysupersupersecretpasswordthatyouwillneverguess";
No entanto, executar o aplicativo por meio do strings
processo (ou qualquer outro que extraia strings de um aplicativo binário) revelará a string acima.
Que técnicas devem ser usadas para ocultar esses dados confidenciais?
Editar:
OK, quase todos vocês disseram "seu executável pode ser submetido a engenharia reversa" - é claro! Esta é uma implicância minha, então vou reclamar um pouco aqui:
Por que 99% (OK, talvez eu exagere um pouco) de todas as perguntas relacionadas à segurança neste site são respondidas com um torrent de "não há maneira possível de criar um programa perfeitamente seguro" - isso não é útil responda! Segurança é uma escala móvel entre usabilidade perfeita e nenhuma segurança em uma extremidade e segurança perfeita, mas nenhuma usabilidade na outra.
A questão é que você escolhe sua posição nessa escala móvel dependendo do que está tentando fazer e do ambiente em que o software será executado. Não estou escrevendo um aplicativo para uma instalação militar, estou escrevendo um aplicativo para um PC doméstico . Preciso criptografar dados em uma rede não confiável com uma chave de criptografia pré-conhecida. Nesses casos, "segurança através da obscuridade" provavelmente é suficiente! Claro, alguém com tempo, energia e habilidade suficientes poderia fazer a engenharia reversa do binário e encontrar a senha, mas adivinhe? Eu não me importo:
O tempo que levo para implementar um sistema seguro de primeira linha é mais caro do que a perda de vendas devido às versões quebradas (não que eu esteja realmente vendendo isso, mas você entendeu). Essa tendência "vamos fazer da melhor maneira possível" na programação entre os novos programadores é, no mínimo, tola.
Obrigado por responder a esta pergunta - eles foram muito úteis. Infelizmente, só posso aceitar uma resposta, mas votei positivamente em todas as respostas úteis.
Respostas:
Basicamente, qualquer pessoa com acesso ao seu programa e um depurador pode e vai encontrar a chave no aplicativo se quiser.
Mas, se você apenas deseja ter certeza de que a chave não apareça durante a execução
strings
em seu binário, você pode, por exemplo, certificar-se de que a chave não está dentro do intervalo de impressão.Chave obscurecedora com XOR
Por exemplo, você pode usar XOR para dividir a chave em duas matrizes de bytes:
Se você criar key1 com o mesmo comprimento de byte,
key
poderá usar valores de byte (completamente) aleatórios e, em seguida, calcularkey2
:Você pode fazer isso em seu ambiente de construção e, em seguida, apenas armazenar
key1
ekey2
em seu aplicativo.Protegendo seu binário
Outra abordagem é usar uma ferramenta para proteger seu binário. Por exemplo, existem várias ferramentas de segurança que podem garantir que seu binário seja ofuscado e inicie uma máquina virtual em que seja executado. Isso torna mais difícil (er) depurar e também é a maneira convencional pela qual muitos aplicativos seguros de nível comercial (também, infelizmente, malware) são protegidos.
Uma das principais ferramentas é o Themida , que faz um ótimo trabalho protegendo seus binários. É frequentemente usado por programas bem conhecidos, como o Spotify, para proteção contra engenharia reversa. Possui recursos para evitar a depuração em programas como OllyDbg e Ida Pro.
Há também uma lista maior, talvez um pouco desatualizada, de ferramentas para proteger seu binário .
Alguns deles são gratuitos.
Correspondência de senha
Alguém aqui discutiu o hash de senha + salt.
Se precisar armazenar a chave para compará-la com algum tipo de senha enviada pelo usuário, você deve usar uma função de hash unilateral, de preferência combinando nome de usuário, senha e um salt. O problema com isso, porém, é que seu aplicativo precisa conhecer o sal para poder fazer o unilateral e comparar os hashes resultantes. Portanto, você ainda precisa armazenar o sal em algum lugar de seu aplicativo. Mas, como @Edward aponta nos comentários abaixo, isso protegerá efetivamente contra um ataque de dicionário usando, por exemplo, tabelas de arco-íris.
Finalmente, você pode usar uma combinação de todas as técnicas acima.
fonte
Em primeiro lugar, perceba que não há nada que você possa fazer para impedir um hacker suficientemente determinado, e há muitos deles por perto. A proteção em cada jogo e console é quebrada eventualmente, então esta é apenas uma correção temporária.
Existem 4 coisas que você pode fazer que aumentarão suas chances de ficar escondido por um tempo.
1) Oculte os elementos da string de alguma forma - algo óbvio como xorar (o operador ^) a string com outra string será bom o suficiente para tornar a string impossível de ser pesquisada.
2) Divida a string em pedaços - divida sua string e coloque pedaços dela em métodos com nomes estranhos em módulos estranhos. Não facilite a busca e a localização do método com a string. É claro que algum método terá que chamar todos esses bits, mas ainda torna um pouco mais difícil.
3) Nunca construa a string na memória - a maioria dos hackers usa ferramentas que permitem que eles vejam a string na memória depois de codificada. Se possível, evite isso. Se, por exemplo, você estiver enviando a chave para um servidor, envie-a caractere por caractere, para que a string inteira nunca esteja por perto. Claro, se você o estiver usando a partir de algo como a codificação RSA, isso é mais complicado.
4) Faça um algoritmo ad-hoc - além de tudo isso, adicione uma ou duas variações exclusivas. Talvez apenas adicione 1 a tudo que você produz, ou faça qualquer criptografia duas vezes, ou adicione um açúcar. Isso apenas torna as coisas um pouco mais difíceis para o hacker que já sabe o que procurar quando alguém está usando, por exemplo, hashing md5 vanilla ou criptografia RSA.
Acima de tudo, certifique-se de que não seja muito importante quando (e será quando o aplicativo se tornar popular o suficiente) sua chave for descoberta!
fonte
Uma estratégia que usei no passado é criar uma série de caracteres aparentemente aleatórios. Você inicialmente insere, e então localiza seus caracteres particulares com um processo algébrico onde cada passo de 0 a N produzirá um número <tamanho do array que contém o próximo caractere em sua string ofuscada. (Esta resposta está parecendo ofuscada agora!)
Exemplo:
Dada uma matriz de caracteres (números e travessões são apenas para referência)
E uma equação cujos primeiros seis resultados são: 3, 6, 7, 10, 21, 47
Geraria a palavra "OLÁ!" da matriz acima.
fonte
Eu concordo com @Checkers, seu executável pode sofrer engenharia reversa.
Uma maneira um pouco melhor é criá-lo dinamicamente, por exemplo:
fonte
Obviamente, armazenar dados privados em um software enviado ao usuário é sempre um risco. Qualquer engenheiro suficientemente educado (e dedicado) pode fazer a engenharia reversa dos dados.
Dito isso, muitas vezes você pode tornar as coisas seguras o suficiente, aumentando a barreira que as pessoas precisam superar para revelar seus dados privados. Geralmente é um bom compromisso.
No seu caso, você poderia confundir suas strings com dados não imprimíveis e, em seguida, decodificá-los no tempo de execução usando uma função auxiliar simples, como esta:
fonte
Eu criei uma ferramenta de criptografia simples para strings, ela pode gerar strings criptografadas automaticamente e tem algumas opções extras para fazer isso, alguns exemplos:
String como uma variável global:
Como string Unicode com loop de descriptografia:
String como uma variável global:
fonte
Um pouco dependente do que você está tentando proteger, como aponta Joshperry. Por experiência própria, eu diria que, se faz parte de algum esquema de licenciamento para proteger seu software, não se preocupe. Eles irão, evidentemente, fazer engenharia reversa. Basta usar uma cifra simples como ROT-13 para protegê-lo de ataques simples (linha passando por cima dele). Se for para proteger dados confidenciais de usuários, eu questionaria se proteger esses dados com uma chave privada armazenada localmente é uma medida acertada. Novamente, tudo se resume ao que você está tentando proteger.
EDIT: Se você vai fazer isso, então uma combinação de técnicas que Chris aponta será muito melhor do que podridão13.
fonte
Como foi dito antes, não há como proteger totalmente sua corda. Mas existem maneiras de protegê-lo com uma segurança razoável.
Quando tive que fazer isso, coloquei uma string de aparência inocente no código (um aviso de direitos autorais, por exemplo, ou algum prompt de usuário falso ou qualquer outra coisa que não será alterada por alguém corrigindo um código não relacionado), criptografando-a usando a si mesma como uma chave, fiz um hash (adicionando um pouco de sal) e usei o resultado como uma chave para criptografar o que eu realmente queria criptografar.
Claro que isso pode ser hackeado, mas é preciso um hacker determinado para fazer isso.
fonte
Se você estiver usando a DPAPI do usuário do Windows, http://msdn.microsoft.com/en-us/library/ms995355.aspx
Como um post anterior disse se você estiver no mac use o chaveiro.
Basicamente, todas essas idéias engraçadas sobre como armazenar sua chave privada dentro de seu binário são suficientemente fracas do ponto de vista da segurança que você não deve fazê-las. Qualquer pessoa que receber sua chave privada é um grande negócio, não a mantenha dentro de seu programa. Dependendo de como seu aplicativo é importado, você pode manter suas chaves privadas em um cartão inteligente, em um computador remoto com o qual seu código se comunica ou pode fazer o que a maioria das pessoas faz e mantê-lo em um local muito seguro no computador local (a chave " store "que é uma espécie de registro seguro estranho) que é protegido por permissões e toda a força do seu sistema operacional.
Este é um problema resolvido e a resposta NÃO é manter a chave dentro do seu programa :)
fonte
Experimente isso . O código-fonte explica como criptografar e descriptografar imediatamente todas as strings em um determinado projeto Visual Studio c ++.
fonte
Um método que tentei recentemente é:
part1
part2
part1
epart2
part1
. Ele verificará a integridade dos dados privados.MACRO para preencher dados:
Suponha que os dados privados tenham 4 bytes. Definimos uma macro para ele que salva os dados com instruções de atribuição em alguma ordem aleatória.
Agora use esta macro no código onde você precisa salvar
part1
epart2
, da seguinte maneira:fonte
Em vez de armazenar a chave privada em seu executável, você pode solicitá-la ao usuário e armazená-la por meio de um gerenciador de senhas externo , algo semelhante ao Mac OS X Keychain Access.
fonte
Depende do contexto, mas você pode simplesmente armazenar o hash da chave mais um salt (string constante, fácil de ocultar).
Então, quando (se) o usuário inserir a chave, você adiciona o sal , calcula o hash e compara.
O salt é provavelmente desnecessário neste caso, ele interrompe um ataque de dicionário de força bruta se o hash puder ser isolado (uma pesquisa no Google também funcionou).
Um hacker ainda precisa inserir uma instrução jmp em algum lugar para ignorar o lote inteiro, mas isso é um pouco mais complicado do que uma simples pesquisa de texto.
fonte
Há uma ofuscação de projeto (muito leve) apenas de cabeçalho feita por adamyaxley que funciona perfeitamente. É baseado em funções lambda e macros e criptografa strings literais com uma cifra XOR em tempo de compilação. Se necessário, podemos alterar a semente de cada string.
O código a seguir não armazenará a string "hello world" no binário compilado.
Eu testei com c ++ 17 e visual studio 2019 e verifiquei via IDA e confirmo que a string está oculta. Uma vantagem preciosa em comparação com ADVobfuscator é que ele pode ser convertido em std :: string (embora ainda esteja oculto no binário compilado):
fonte