techniki zaciemniania wrażliwych ciągów w C++

Muszę przechowywać poufne informacje (klucz szyfrowania symetrycznego, który chcę zachować w tajemnicy) w mojej aplikacji C++. Proste podejście polega na tym, że:

std::string myKey = "mysupersupersecretpasswordthatyouwillneverguess";

Jednak uruchomienie aplikacji przez proces strings (lub jakikolwiek inny, który wyodrębnia łańcuchy z aplikacji binarnej)ujawni powyższy łańcuch.

Jakich technik należy użyć, aby zasłonić takie wrażliwe dane?

Edit:

OK, więc prawie wszyscy powiedzieliście "Twój plik wykonywalny może zostać zmodyfikowany wstecznie" - oczywiście! To jest mój pet peeve, więc będę tu trochę rantować:

Dlaczego 99% (OK, więc może trochę przesadzam) na wszystkie pytania związane z bezpieczeństwem na tej stronie odpowiada torrentem "nie ma możliwego sposobu na stworzenie doskonale bezpiecznego programu" - to nie jest pomocna odpowiedź! Bezpieczeństwo to przesuwająca się skala między doskonałą użytecznością i brakiem bezpieczeństwa na jednym końcu, a doskonałym bezpieczeństwem, ale bez użyteczności na drugi.

Chodzi o to, że wybierasz swoją pozycję na tej przesuwnej skali w zależności od tego, co próbujesz zrobić i środowiska, w którym Twoje oprogramowanie będzie działać. Nie piszę aplikacji dla instalacji wojskowej, piszę aplikację dla domowego PC. Muszę zaszyfrować dane w niezaufanej sieci za pomocą wcześniej znanego klucza szyfrującego. W takich przypadkach "bezpieczeństwo przez ciemność" jest prawdopodobnie wystarczająco dobre! Oczywiście, ktoś z wystarczającą ilością czasu, energii i umiejętności może Inżynieria wsteczna pliku binarnego i znaleźć hasło, ale zgadnij co? Nie obchodzi mnie to:

Czas potrzebny mi na wdrożenie najwyższej klasy bezpiecznego systemu jest droższy niż utrata sprzedaży z powodu pękniętych wersji (nie, że faktycznie sprzedaję to, ale rozumiesz o co mi chodzi). Ten niebieski-sky" lets do it the absolute best way possible " trend w programowaniu wśród nowych programistów jest co najmniej głupi.

Dziękuję za poświęcenie czasu, aby odpowiedzieć na to pytanie - byli najbardziej pomocne. Niestety mogę zaakceptować tylko jedną odpowiedź, ale zagłosowałem na wszystkie przydatne odpowiedzi.

Author: csl, 2009-10-30

13 answers

Zasadniczo każdy, kto ma dostęp do twojego programu i debugera Możei znaleźć klucz w aplikacji, jeśli chce.

Ale jeśli chcesz się tylko upewnić, że klucz nie pojawi się podczas uruchamiania strings na Twoim pliku binarnym, możesz na przykład upewnić się, że klucz nie znajduje się w zakresie druku.

Zasłanianie klucza za pomocą XOR

Na przykład, można użyć XOR, aby podzielić klucz na dwie tablice bajtowe:

key = key1 XOR key2

Jeśli Utwórz key1 o tej samej długości bajtów co key możesz użyć (całkowicie) losowych wartości bajtów, a następnie obliczyć key2:

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

Można to zrobić w środowisku budowania, a następnie przechowywać tylko key1i key2 w aplikacji.

Ochrona danych binarnych

Innym podejściem jest użycie narzędzia do ochrony danych binarnych. Na przykład istnieje kilka narzędzi zabezpieczających, które mogą upewnić się, że Twój plik binarny jest zaciemniony i uruchamia Maszynę wirtualną, na której działa. To utrudnia debugowanie, a także jest konwencyjnym sposobem ochrony wielu komercyjnych bezpiecznych aplikacji (również, niestety, złośliwego oprogramowania).

Jednym z najlepszych narzędzi jestThemida , która świetnie chroni Twoje pliki binarne. Jest często używany przez dobrze znane programy, takie jak Spotify, w celu ochrony przed inżynierią odwrotną. Posiada funkcje zapobiegające debugowaniu w programach takich jak OllyDbg i Ida Pro.

Istnieje również większa lista, być może nieco przestarzała, z narzędzi do ochrony Twojego pliku binarnego.
Niektóre z nich są darmowe.

Password matching

Ktoś tutaj omówił hashowanie hasła + sól.

Jeśli potrzebujesz zapisać klucz, aby dopasować go do jakiegoś hasła użytkownika, powinieneś użyć jednokierunkowej funkcji mieszania, preferując połączenie nazwy użytkownika, hasła i soli. Problem z tym polega jednak na tym, że Twoja aplikacja musi znać sól, aby móc zrobić jednokierunkową i porównać wynikowe hasze. Dlatego nadal musisz przechowywać sól gdzieś w aplikacji. Ale, jak zaznacza @Edward w komentarzach poniżej, będzie to skutecznie chronić przed atakiem słownikowym za pomocą np. tęczowych tabel.

Na koniec możesz użyć kombinacji wszystkich powyższych technik.

 37
Author: csl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-16 08:04:20

Po pierwsze, zdaj sobie sprawę, że nic nie możesz zrobić, aby powstrzymać wystarczająco zdeterminowanego hakera, a jest ich mnóstwo w pobliżu. Ochrona każdej gry i konsoli jest ostatecznie pęknięta, więc jest to tylko tymczasowa poprawka.

Są 4 rzeczy, które możesz zrobić, aby zwiększyć szanse na pozostanie w ukryciu przez jakiś czas.

1) Ukryj elementy łańcucha w jakiś sposób-coś oczywistego jak xoring (operator^) łańcuch z innym łańcuchem będzie wystarczająco dobry, aby ciąg nie mógł być wyszukany.

2) podziel łańcuch na kawałki-podziel łańcuch i pop jego fragmenty na dziwnie nazwane metody w dziwnych modułach. Nie ułatwiaj wyszukiwania i znajdowania metody z ciągiem znaków. Oczywiście jakaś metoda będzie musiała wywołać wszystkie te bity, ale to jeszcze trochę utrudnia.

3) nigdy nie buduj łańcucha w pamięci-większość hakerów używa narzędzi, które pozwalają im zobaczyć łańcuch w pamięci po zakodowałeś to. Jeśli to możliwe, unikaj tego. Jeśli na przykład wysyłasz klucz off do serwera, wyślij go znak po znaku, więc cały łańcuch nigdy nie jest wokół. Oczywiście, jeśli używasz go z czegoś takiego jak kodowanie RSA, to jest to trudniejsze.

4) Wykonaj algorytm ad-hoc - do tego wszystkiego dodaj unikalny zwrot lub dwa. Może po prostu dodaj 1 do wszystkiego, co produkujesz, lub zrób dwa razy szyfrowanie, lub dodaj cukier. To tylko trochę utrudnia hakerowi, który już wie czego szukać, gdy ktoś używa np. waniliowego hashowania md5 lub szyfrowania RSA.

Przede wszystkim upewnij się, że nie jest to zbyt ważne, kiedy (i będzie, gdy aplikacja stanie się wystarczająco popularna) Twój klucz zostanie odkryty!

 7
Author: Chris Jefferson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-30 08:55:23

Strategia, której używałem w przeszłości, polega na stworzeniu szeregu pozornie przypadkowych postaci. Początkowo wstawiasz, a następnie lokalizujesz konkretne znaki za pomocą procesu algebraicznego, w którym każdy krok od 0 do N daje liczbę

Przykład:

Podano tablicę znaków (cyfry i myślniki są tylko w celach informacyjnych)

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

I równanie, którego pierwsze sześć wyniki są: 3, 6, 7, 10, 21, 47

Oddałby słowo "cześć!"z tablicy powyżej.

 5
Author: Paul Sasik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-30 10:36:14

Zgadzam się z @ Checkers, Twój plik wykonywalny może być odwrócony.

Nieco lepszym sposobem jest tworzenie go dynamicznie, na przykład:

std::string myKey = part1() + part2() + ... + partN();
 4
Author: Nick Dandoulakis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-30 08:37:53

Stworzyłem proste narzędzie szyfrujące dla ciągów, może automatycznie generować zaszyfrowane ciągi i ma kilka dodatkowych opcji, aby to zrobić, kilka przykładów:

Łańcuch jako zmienna globalna:

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

Jako ciąg unicode z pętlą deszyfrującą:

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

Łańcuch jako zmienna globalna:

// 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;
 4
Author: Bartosz Wójcik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-16 11:00:06

Oczywiście przechowywanie prywatnych danych w oprogramowaniu, które jest wysyłane do użytkownika, zawsze stanowi ryzyko. Każdy odpowiednio wykształcony (i oddany) inżynier może odtworzyć dane.

Mając to na uwadze, często można wystarczająco zabezpieczyć rzeczy, podnosząc barierę, którą ludzie muszą pokonać, aby ujawnić Twoje prywatne dane. To zazwyczaj dobry kompromis.

W Twoim przypadku możesz zaśmiecić swoje ciągi danymi, które nie są drukowalne, a następnie dekodować je w czasie wykonywania za pomocą prostego funkcja pomocnicza, jak Ta:

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;
    // ...
}
 3
Author: Frerich Raabe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-30 08:47:44

Trochę zależy od tego, co próbujesz chronić, jak wskazuje joshperry. Z doświadczenia chciałbym powiedzieć, że jeśli jest to część jakiegoś systemu licencjonowania w celu ochrony oprogramowania, to nie kłopocz się. Będą je eventially reverse engineering it. Po prostu użyj prostego szyfru, takiego jak ROT-13, aby chronić go przed prostymi atakami(linia biegnąca nad nim). Jeśli chodzi o ochronę poufnych danych użytkowników, kwestionowałbym, czy ochrona tych danych za pomocą klucza prywatnego przechowywanego lokalnie jest mądrym posunięciem. Again wszystko sprowadza się do tego, co próbujesz chronić.

EDIT: jeśli masz zamiar to zrobić, to kombinacja technik, na które wskazuje Chris, będzie o wiele lepsza niż rot13.

 2
Author: Nic Strong,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-30 09:01:35

Jak powiedziano wcześniej, nie ma sposobu, aby całkowicie chronić swój sznurek. Ale są sposoby, aby go chronić rozsądnie.

Kiedy musiałem to zrobić, umieściłem w kodzie jakiś niewinnie wyglądający ciąg znaków (np. informacja o prawach autorskich lub jakiś fałszywy monit użytkownika lub cokolwiek innego, co nie zostanie zmienione przez kogoś naprawiającego niepowiązany kod), zaszyfrowałem to używając samego siebie jako klucza, zaszyfrowałem (dodając trochę soli) i użyłem wyniku jako klucza do zaszyfrowania tego, co chciałem zaszyfrować.

Oczywiście można to zhakować, ale potrzeba do tego zdecydowanego hakera.

 2
Author: sbi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-30 10:05:33

Zamiast przechowywać klucz prywatny w pliku wykonywalnym, możesz zażądać go od użytkownika i zapisać go za pomocą zewnętrznego menedżera haseł , czegoś podobnego do Mac OS X Keychain Access.

 1
Author: mouviciel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-30 08:40:10

Spróbuj tego . Kod źródłowy wyjaśnia, jak zaszyfrować i odszyfrować w locie wszystkie ciągi znaków w danym projekcie Visual Studio c++.

 1
Author: Michael Haephrati,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-19 19:56:44

Jedna metoda, którą ostatnio wypróbowałem to:

  1. Weź hash (SHA256) prywatnych danych i zapełnij je w kodzie jako part1
  2. Weź XOR prywatnych danych i ich hash i zapełnij je w kodzie jako part2
  3. wypełniaj dane: nie przechowuj ich jako znak str [], ale wypełniaj w trybie runtime za pomocą instrukcji przypisania (jak pokazano w makrze poniżej)
  4. teraz Wygeneruj prywatne dane o czasie wykonywania, pobierając XOR z part1 i part2
  5. dodatkowy krok : Oblicz hash wygenerowanych danych i porównać je z part1. Zweryfikuje integralność danych prywatnych.

Makro do wypełnienia danych:

Załóżmy, że dane prywatne mają 4 bajty. Definiujemy dla niego makro, które zapisuje dane z instrukcjami przypisania w pewnej losowej kolejności.

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

Teraz użyj tego makra w kodzie, gdzie musisz zapisać part1 i part2, w następujący sposób:

char part1[4] = {0};
char part2[4] = {0};
POPULATE_DATA(part1, 1, 2, 3, 4); 
POPULATE_DATA(part2, 5, 6, 7, 8);
 1
Author: Zeeshan Mahmood,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-24 09:32:36

Zależny od kontekstu, ale możesz po prostu zapisać hash klucza plus salt (stały łańcuch, łatwy do przesłania).

Następnie, gdy (if) użytkownik wprowadzi klucz, dodajesz salt , obliczasz hash i porównujesz.

The salt jest prawdopodobnie niepotrzebny w tym przypadku, zatrzymuje atak słownikowy brute-force, jeśli hash może być izolowany(wyszukiwarka Google również wie, że działa).

Haker musi jeszcze wstawić jmp Instrukcja gdzieś, aby ominąć całość, ale to raczej bardziej skomplikowane niż proste wyszukiwanie tekstu.

 0
Author: hplbsh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-30 09:30:10

Jeśli korzystasz z DPAPI systemu windows, http://msdn.microsoft.com/en-us/library/ms995355.aspx

Jak w poprzednim poście powiedział, Jeśli jesteś na mac użyj pęku kluczy.

Zasadniczo wszystkie te urocze pomysły na to, jak przechowywać swój klucz prywatny w Twoim pliku binarnym są na tyle słabe z punktu widzenia bezpieczeństwa, że nie powinieneś ich robić. Jeśli ktoś dostanie Twój klucz prywatny to wielka sprawa, nie przechowuj go w swoim programie. W zależności od tego, jak zaimportowana jest aplikacja, możesz zachować klucze prywatne na karcie inteligentnej, na zdalnym komputerze Twój kod rozmawia lub możesz zrobić to, co robi większość ludzi i przechowywać go w bardzo bezpiecznym miejscu na komputerze lokalnym ("przechowalnia kluczy", który jest rodzajem dziwnego bezpiecznego rejestru), który jest chroniony przez uprawnienia i całą siłę Twojego systemu operacyjnego.

To jest rozwiązany problem i odpowiedzią nie jest trzymanie klucza w programie:)

 0
Author: Collin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-02 20:00:36