Question

Je dois stocker des informations sensibles (une clé de chiffrement symétrique que je souhaite garder confidentielles) dans mon application C ++. L’approche simple consiste à faire ceci:

std::string myKey = "mysupersupersecretpasswordthatyouwillneverguess";

Cependant, l'exécution de l'application via le processus strings (ou tout autre processus permettant d'extraire des chaînes d'une application binaire) révélera la chaîne ci-dessus.

Quelles techniques faut-il utiliser pour masquer de telles données sensibles?

Modifier:

OK, vous avez tous dit à peu près & "que votre exécutable peut être désossé &"; - bien sûr! Ceci est ma bête noire, alors je vais en parler un peu:

Pourquoi est-il que 99% (OK, alors j'exagère peut-être un peu) de toutes les questions relatives à la sécurité sur ce site reçoivent une réponse torrentielle de & "; il est impossible de créer un programme parfaitement sécurisé " - ce n'est pas une réponse utile! La sécurité est une échelle mobile entre une convivialité parfaite et aucune sécurité d'un côté, et une sécurité parfaite mais aucune utilisation de l'autre.

Le fait est que vous choisissez votre position sur cette échelle mobile en fonction de ce que vous essayez de faire et de l'environnement dans lequel votre logiciel sera exécuté. Je ne suis pas en train d’écrire une application pour une installation militaire, je suis en train d’écrire une application pour un PC personnel . J'ai besoin de chiffrer des données sur un réseau non approuvé avec une clé de chiffrement connue. Dans ces cas, la & Quot; sécurité par l'obscurité & Quot; est probablement assez bon! Bien sûr, une personne disposant de suffisamment de temps, d’énergie et de compétences pourrait procéder à l’ingénierie inverse du binaire et trouver le mot de passe, mais devinez quoi? Je m'en fiche:

Le temps requis pour mettre en place un système sécurisé haut de gamme coûte plus cher que la perte de ventes due aux versions fissurées (ce n'est pas que je vends ce produit, mais vous comprenez mon point de vue). Ce ciel bleu! & Permet de le faire de la meilleure façon possible & "; tendance de la programmation parmi les nouveaux programmeurs est stupide pour le moins.

Merci d'avoir pris le temps de répondre à cette question. Ils ont été très utiles. Malheureusement, je ne peux accepter qu'une seule réponse, mais j'ai voté favorablement pour toutes les réponses utiles.

Était-ce utile?

La solution

En principe, toute personne ayant accès à votre programme et à un débogueur peut et trouver la clé dans l'application si elle le souhaite.

Toutefois, si vous souhaitez simplement vous assurer que la clé n'apparaît pas lorsque vous exécutez strings sur votre binaire, vous pouvez par exemple vous assurer que la clé ne se trouve pas dans la plage imprimable.

Touche cachée avec XOR

Par exemple, vous pouvez utiliser XOR pour scinder la clé en tableaux à deux octets:

key = key1 XOR key2

Si vous créez key1 avec la même longueur d'octet que key, vous pouvez utiliser des valeurs d'octets aléatoires (complètes), puis calculer key2:

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

Vous pouvez le faire dans votre environnement de construction, puis stocker uniquement key1 et <=> dans votre application.

Protéger votre binaire

Une autre approche consiste à utiliser un outil pour protéger votre fichier binaire. Par exemple, il existe plusieurs outils de sécurité qui peuvent s’assurer que votre fichier binaire est obscurci et démarre une machine virtuelle sur laquelle il est exécuté. Cela rend le débogage plus difficile et constitue également la façon conventionnelle de protéger de nombreuses applications sécurisées de qualité commerciale (également, les logiciels malveillants).

L'un des principaux outils est Themida , qui protège les fichiers binaires de façon fantastique. Il est souvent utilisé par des programmes bien connus, tels que Spotify, pour se protéger contre l'ingénierie inverse. Il dispose de fonctionnalités pour empêcher le débogage dans des programmes tels que OllyDbg et Ida Pro.

Il existe également une liste plus longue, peut-être un peu dépassée, de outils de protection. votre binaire .
Certains sont gratuits.

Correspondance du mot de passe

Quelqu'un ici a parlé de hashing password + salt.

Si vous devez stocker la clé pour la faire correspondre à un type de mot de passe soumis par l'utilisateur, vous devez utiliser une fonction de hachage à sens unique, de préférence en combinant un nom d'utilisateur, un mot de passe et un sel. Le problème avec ceci, cependant, est que votre application doit connaître le sel pour pouvoir effectuer le traitement unidirectionnel et comparer les hachages résultants. Vous devez donc toujours stocker le sel quelque part dans votre application. Mais, comme @Edward le souligne dans les commentaires ci-dessous, cela protégera efficacement contre les attaques par dictionnaire utilisant, par exemple, des tableaux arc-en-ciel.

Enfin, vous pouvez utiliser une combinaison de toutes les techniques ci-dessus.

Autres conseils

Tout d’abord, sachez que vous ne pouvez rien faire qui puisse arrêter un pirate informatique suffisamment déterminé, et il y en a beaucoup autour. La protection de tous les jeux et de toutes les consoles est éventuellement brisée. Il s’agit donc d’une solution temporaire.

Il y a 4 choses que vous pouvez faire qui augmenteront vos chances de rester caché pendant un moment.

1) Cachez les éléments de la chaîne d’une manière ou d’une autre - quelque chose d’évident, comme xoring (l’opérateur ^), une chaîne avec une autre chaîne suffira à rendre la chaîne impossible à rechercher.

2) Divisez la chaîne en morceaux - divisez votre chaîne et en extrayez des morceaux en méthodes étrangement nommées dans des modules étranges. Ne facilite pas la recherche et trouve la méthode contenant la chaîne. Bien sûr, une méthode devra appeler tous ces bits, mais cela le rend un peu plus difficile.

3) Ne construisez jamais la chaîne en mémoire - la plupart des pirates informatiques utilisent des outils qui leur permettent de voir la chaîne en mémoire après l'avoir codée. Si possible, évitez cela. Si, par exemple, vous envoyez la clé à un serveur, envoyez-la caractère par caractère afin que la chaîne entière ne soit jamais présente. Bien sûr, si vous l’utilisez à partir de quelque chose comme l’encodage RSA, c’est plus compliqué.

4) Faites un algorithme ad-hoc - en plus de cela, ajoutez une torsion unique ou deux. Peut-être juste ajouter 1 à tout ce que vous produisez, ou faire un cryptage deux fois, ou ajouter un sucre. Cela rend simplement la tâche un peu plus difficile pour le pirate informatique qui sait déjà quoi rechercher lorsque quelqu'un utilise, par exemple, le hachage vanilla md5 ou le cryptage RSA.

Surtout, assurez-vous que ce n’est pas trop important (et ce le sera si, si votre application devient assez populaire), votre clé est découverte!

Une stratégie que j’ai utilisée par le passé consiste à créer un tableau de caractères apparemment aléatoires. Vous commencez par insérer, puis vous localisez vos personnages avec un processus algébrique dans lequel chaque étape de 0 à N donnera un nombre & Lt; taille du tableau qui contient le caractère suivant dans votre chaîne obfusquée. (Cette réponse se sent obscurcie maintenant!)

Exemple:

Étant donné un tableau de caractères (les chiffres et les tirets sont à titre de référence uniquement)

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

Et une équation dont les six premiers résultats sont: 3, 6, 7, 10, 21, 47

Donnerait le mot & "BONJOUR! &"; du tableau ci-dessus.

Je suis d'accord avec @Checkers, votre exécutable peut être désossé.

Un meilleur moyen consiste à le créer dynamiquement, par exemple:

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

J'ai créé un outil de cryptage simple pour les chaînes, il peut générer automatiquement des chaînes cryptées et dispose de quelques options supplémentaires pour le faire, par exemple:

Chaîne en tant que variable globale:

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

En tant que chaîne unicode avec boucle de déchiffrement:

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

Chaîne en tant que variable globale:

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

Bien sûr, le stockage de données privées dans un logiciel livré à l'utilisateur est toujours un risque. Tout ingénieur suffisamment instruit (et dédié) pourrait procéder à l’ingénierie inverse des données.

Cela étant dit, vous pouvez souvent rendre les choses suffisamment sécurisées en soulevant la barrière que les gens doivent surmonter pour révéler vos données personnelles. C’est généralement un bon compromis.

Dans votre cas, vous pouvez encombrer vos chaînes de données avec des données non imprimables, puis les décoder au moment de l'exécution à l'aide d'une fonction d'assistance simple, comme ceci:

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

Un peu dépendant de ce que vous essayez de protéger, comme le souligne Joshperry. Par expérience, je dirais que si cela fait partie d'un schéma de licence pour protéger votre logiciel, alors ne vous en faites pas. Ils finiront par l’ingénierie inverse. Utilisez simplement un simple chiffre comme ROT-13 pour le protéger contre les attaques simples (lignes qui passent dessus). S'il s'agit de sécuriser les données sensibles des utilisateurs, je me demanderais s'il est judicieux de protéger ces données avec une clé privée stockée localement. Encore une fois, cela revient à ce que vous essayez de protéger.

EDIT: Si vous voulez le faire, alors une combinaison de techniques qui, selon Chris, sera bien meilleure que pour rot13.

Comme cela a été dit précédemment, il n’ya aucun moyen de protéger totalement votre chaîne. Mais il existe des moyens de le protéger avec une sécurité raisonnable.

Lorsque j'ai eu à faire cela, j'ai inséré dans le code une chaîne d'aspect innocent (une notice de copyright, par exemple, ou une invite d'utilisateur fictif ou tout autre élément qui ne serait pas modifié par une personne qui corrigeait du code indépendant), chiffrée. cela s'est utilisé comme une clé, haché cela (en ajoutant du sel), et a utilisé le résultat comme clé pour chiffrer ce que je voulais en réalité chiffrer.

Bien sûr, cela pourrait être piraté, mais il faut un pirate informatique déterminé pour le faire.

Au lieu de stocker la clé privée dans votre exécutable, vous voudrez peut-être la demander à l'utilisateur et la stocker au moyen d'un fichier externe gestionnaire de mots de passe , similaire à l'accès au trousseau Mac OS X.

Si vous utilisez Windows sous DPAPI, http://msdn.microsoft .com / fr-us / library / ms995355.aspx

Comme indiqué dans un précédent article, si vous êtes sur mac, utilisez le trousseau.

En gros, toutes ces idées intéressantes sur la façon de stocker votre clé privée dans votre binaire sont suffisamment pauvres du point de vue de la sécurité pour que vous ne deviez pas les faire. Toute personne qui obtient votre clé privée est un gros problème, ne la gardez pas dans votre programme. Selon l’importation de votre application, vous pouvez conserver vos clés privées sur une carte à puce. Votre code parle sur un ordinateur distant ou vous pouvez faire ce que la plupart des gens font et le conserver dans un emplacement très sécurisé sur l’ordinateur local (le "; magasin de clés &", qui est un peu comme un registre sécurisé étrange) protégé par des autorisations et toute la puissance de votre système d’exploitation.

C’est un problème résolu et la solution n’est PAS de garder la clé dans votre programme:)

Essayez de this . Le code source explique comment chiffrer et déchiffrer à la volée toutes les chaînes d’un projet Visual Studio c ++ donné.

Une méthode que j'ai récemment essayée est la suivante:

  1. Prenez le hachage (SHA256) des données privées et remplissez-le dans le code comme part1
  2. Prenez XOR des données privées et leur hachage et remplissez-le dans le code comme part2
  3. Remplir des données: ne le stockez pas sous la forme char str [], mais remplissez-le au moment de l'exécution à l'aide d'instructions d'affectation (comme indiqué dans la macro ci-dessous)
  4. Maintenant, générez les données privées au moment de l'exécution en prenant le XOR de <=> et <=>
  5. Étape supplémentaire : calculez le hachage des données générées et comparez-le avec <=>. Il vérifiera l’intégrité des données privées.

MACRO pour renseigner les données:

Supposons que les données privées soient de 4 octets. Nous définissons une macro qui enregistre les données avec les instructions d’affectation dans un ordre aléatoire.

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

Utilisez maintenant cette macro dans le code où vous devez enregistrer <=> et <=>, comme suit:

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

Dépend du contexte mais vous pouvez simplement stocker le hachage de la clé plus un sel (chaîne constante, facile à masquer).

Ensuite, lorsque (si) l'utilisateur entre la clé, vous ajoutez le sel , calculez le hachage et comparez.

Le sel est probablement inutile dans ce cas, il arrête une attaque par dictionnaire avec force brute si le hachage peut être isolé (une recherche sur Google a également été appelée à fonctionner).

Un pirate informatique n'a toujours qu'à insérer une instruction jmp quelque part pour contourner le problème, mais c'est un peu plus compliqué qu'une simple recherche dans le texte.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top