Saya perlu menyimpan informasi sensitif (kunci enkripsi simetris yang ingin saya rahasiakan) di aplikasi C ++ saya. Pendekatan sederhananya adalah melakukan ini:
std::string myKey = "mysupersupersecretpasswordthatyouwillneverguess";
Namun, menjalankan aplikasi melalui strings
proses (atau yang lainnya yang mengekstrak string dari aplikasi biner) akan menampilkan string di atas.
Teknik apa yang harus digunakan untuk mengaburkan data sensitif seperti itu?
Edit:
Oke, hampir semua dari Anda telah mengatakan "eksekusi Anda dapat direkayasa balik" - tentu saja! Ini adalah hewan kesayangan saya, jadi saya akan sedikit mengoceh di sini:
Mengapa 99% (Oke, jadi mungkin saya sedikit melebih-lebihkan) semua pertanyaan terkait keamanan di situs ini dijawab dengan torrent "tidak ada cara yang mungkin untuk membuat program yang sangat aman" - itu tidak membantu menjawab! Keamanan adalah skala geser antara kegunaan sempurna dan tidak ada keamanan di satu sisi, dan keamanan sempurna tetapi tidak ada kegunaan di sisi lain.
Intinya adalah Anda memilih posisi Anda pada skala geser itu tergantung pada apa yang Anda coba lakukan dan lingkungan tempat perangkat lunak Anda akan berjalan. Saya tidak menulis aplikasi untuk instalasi militer, saya sedang menulis aplikasi untuk PC rumahan . Saya perlu mengenkripsi data di jaringan yang tidak tepercaya dengan kunci enkripsi yang telah dikenal sebelumnya. Dalam kasus ini, "keamanan melalui ketidakjelasan" mungkin cukup baik! Tentu, seseorang dengan cukup waktu, energi, dan keterampilan dapat merekayasa balik biner dan menemukan kata sandinya, tetapi coba tebak? Saya tidak peduli:
Waktu yang saya perlukan untuk menerapkan sistem keamanan terbaik lebih mahal daripada kehilangan penjualan karena versi yang sudah di-crack (bukan karena saya benar-benar menjual ini, tetapi Anda mengerti maksud saya). Tren biru-langit "mari lakukan dengan cara terbaik yang mutlak" dalam pemrograman di antara programmer baru adalah bodoh untuk sedikitnya.
Terima kasih telah meluangkan waktu untuk menjawab pertanyaan ini - mereka sangat membantu. Sayangnya saya hanya dapat menerima satu jawaban, tetapi saya telah memilih semua jawaban yang berguna.
Jawaban:
Pada dasarnya, siapa pun yang memiliki akses ke program Anda dan debugger dapat dan akan menemukan kunci dalam aplikasi jika mereka mau.
Tetapi, jika Anda hanya ingin memastikan bahwa kunci tidak muncul saat dijalankan
strings
pada biner Anda, Anda dapat misalnya memastikan bahwa kunci tersebut tidak berada dalam kisaran yang dapat dicetak.Mengaburkan kunci dengan XOR
Misalnya, Anda dapat menggunakan XOR untuk membagi kunci menjadi dua array byte:
Jika Anda membuat key1 dengan panjang byte yang sama seperti
key
Anda dapat menggunakan (sepenuhnya) nilai byte acak lalu menghitungkey2
:key1[n] = crypto_grade_random_number(0..255) key2[n] = key[n] XOR key1[n]
Anda dapat melakukan ini di lingkungan build Anda, lalu hanya menyimpan
key1
dankey2
di aplikasi Anda.Melindungi biner Anda
Pendekatan lain adalah dengan menggunakan alat untuk melindungi biner Anda. Misalnya, ada beberapa alat keamanan yang dapat memastikan biner Anda dikaburkan dan memulai mesin virtual yang menjalankannya. Hal ini menyulitkan (er) untuk men-debug, dan juga merupakan cara konvential banyak aplikasi keamanan tingkat komersial (juga, sayangnya, malware) dilindungi.
Salah satu alat utama adalah Themida , yang melakukan pekerjaan luar biasa dalam melindungi binari Anda. Ini sering digunakan oleh program terkenal, seperti Spotify, untuk melindungi dari rekayasa balik. Ia memiliki fitur untuk mencegah debugging dalam program seperti OllyDbg dan Ida Pro.
Ada juga daftar yang lebih besar, mungkin agak ketinggalan jaman, alat untuk melindungi biner Anda .
Beberapa di antaranya gratis.
Pencocokan kata sandi
Seseorang di sini membahas hashing password + salt.
Jika Anda perlu menyimpan kunci untuk mencocokkannya dengan beberapa jenis kata sandi yang dikirimkan pengguna, Anda harus menggunakan fungsi hashing satu arah, lebih disukai dengan menggabungkan nama pengguna, kata sandi, dan salt. Masalahnya, aplikasi Anda harus mengetahui salt untuk dapat melakukan satu arah dan membandingkan hash yang dihasilkan. Jadi oleh karena itu Anda masih perlu menyimpan garam di suatu tempat di aplikasi Anda. Tapi, seperti yang @Edward tunjukkan pada komentar di bawah, ini akan secara efektif melindungi dari serangan kamus menggunakan, misalnya, tabel pelangi.
Terakhir, Anda dapat menggunakan kombinasi dari semua teknik di atas.
sumber
Pertama-tama, sadari bahwa tidak ada yang dapat Anda lakukan yang akan menghentikan peretas yang cukup gigih, dan ada banyak hal di sekitarnya. Perlindungan pada setiap game dan konsol di sekitar pada akhirnya akan retak, jadi ini hanya perbaikan sementara.
Ada 4 hal yang dapat Anda lakukan yang akan meningkatkan peluang Anda untuk tetap bersembunyi untuk sementara waktu.
1) Sembunyikan elemen string dengan cara tertentu - sesuatu yang jelas seperti xoring (operator ^) string dengan string lain akan cukup baik untuk membuat string tidak mungkin dicari.
2) Bagi string menjadi beberapa bagian - pisahkan string Anda dan masukkan bit ke dalam metode bernama aneh dalam modul aneh. Jangan membuatnya mudah untuk mencari dan menemukan metode dengan string di dalamnya. Tentu saja beberapa metode harus memanggil semua bit ini, tetapi tetap membuatnya sedikit lebih sulit.
3) Jangan pernah membangun string di memori - sebagian besar peretas menggunakan alat yang memungkinkan mereka melihat string di memori setelah Anda menyandikannya. Jika memungkinkan, hindari ini. Jika misalnya Anda mengirim kunci ke server, kirimkan karakter demi karakter, sehingga seluruh string tidak akan pernah ada. Tentu saja, jika Anda menggunakannya dari sesuatu seperti pengkodean RSA, maka ini lebih rumit.
4) Lakukan algoritma ad-hoc - di atas semua ini, tambahkan satu atau dua twist unik. Mungkin cukup tambahkan 1 untuk semua yang Anda hasilkan, atau lakukan enkripsi dua kali, atau tambahkan gula. Ini hanya mempersulit peretas yang sudah tahu apa yang harus dicari ketika seseorang menggunakan, misalnya, hashing vanilla md5 atau enkripsi RSA.
Di atas segalanya, pastikan tidak terlalu penting kapan (dan itu akan terjadi ketika aplikasi Anda menjadi cukup populer) kunci Anda ditemukan!
sumber
Strategi yang pernah saya gunakan di masa lalu adalah membuat serangkaian karakter yang tampaknya acak. Anda awalnya menyisipkan, lalu mencari karakter khusus Anda dengan proses aljabar di mana setiap langkah dari 0 hingga N akan menghasilkan angka <ukuran larik yang berisi karakter berikutnya dalam string yang dikaburkan. (Jawaban ini terasa dikaburkan sekarang!)
Contoh:
Diberikan sebuah array karakter (angka dan tanda hubung hanya untuk referensi)
0123456789 ---------- ALFHNFELKD LKFKFLEHGT FLKRKLFRFK FJFJJFJ!JL
Dan persamaan yang enam hasil pertamanya adalah: 3, 6, 7, 10, 21, 47
Akan menghasilkan kata "HALO!" dari larik di atas.
sumber
Saya setuju dengan @Checkers, file eksekusi Anda dapat direkayasa balik.
Cara yang sedikit lebih baik adalah membuatnya secara dinamis, misalnya:
std::string myKey = part1() + part2() + ... + partN();
sumber
Tentu saja, menyimpan data pribadi dalam perangkat lunak yang dikirimkan ke pengguna selalu berisiko. Insinyur yang cukup terdidik (dan berdedikasi) dapat merekayasa balik data.
Karena itu, Anda sering kali dapat membuat segalanya cukup aman dengan meningkatkan penghalang yang perlu diatasi orang untuk mengungkap data pribadi Anda. Itu biasanya kompromi yang bagus.
Dalam kasus Anda, Anda dapat mengacaukan string dengan data yang tidak dapat dicetak, lalu mendekodekannya pada waktu proses menggunakan fungsi pembantu sederhana, seperti ini:
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; // ... }
sumber
Saya telah membuat alat enkripsi sederhana untuk string, itu dapat secara otomatis menghasilkan string terenkripsi dan memiliki beberapa opsi tambahan untuk melakukannya, beberapa contoh:
String sebagai variabel 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;
Sebagai string unicode dengan loop dekripsi:
// 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 sebagai variabel 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;
sumber
Agak tergantung pada apa yang Anda coba lindungi seperti yang ditunjukkan oleh joshperry. Dari pengalaman, saya akan mengatakan bahwa jika itu adalah bagian dari skema lisensi untuk melindungi perangkat lunak Anda maka jangan repot-repot. Mereka akhirnya akan merekayasa baliknya. Cukup gunakan sandi sederhana seperti ROT-13 untuk melindunginya dari serangan sederhana (baris menjalankan string di atasnya). Jika itu untuk mengamankan data sensitif pengguna, saya akan mempertanyakan apakah melindungi data itu dengan kunci pribadi yang disimpan secara lokal adalah langkah yang bijaksana. Sekali lagi itu tergantung pada apa yang Anda coba lindungi.
EDIT: Jika Anda akan melakukannya maka kombinasi teknik yang ditunjukkan Chris akan jauh lebih baik daripada rot13.
sumber
Seperti yang dikatakan sebelumnya, tidak ada cara untuk melindungi senar Anda sepenuhnya. Tetapi ada cara untuk melindunginya dengan keamanan yang masuk akal.
Ketika saya harus melakukan ini, saya memang memasukkan beberapa string yang tampak tidak bersalah ke dalam kode (pemberitahuan hak cipta, misalnya, atau beberapa permintaan pengguna palsu atau apa pun yang tidak akan diubah oleh seseorang yang memperbaiki kode yang tidak terkait), dienkripsi menggunakan itu sendiri sebagai kunci, hash itu (menambahkan garam), dan menggunakan hasilnya sebagai kunci untuk mengenkripsi apa yang sebenarnya ingin saya enkripsi.
Tentu saja ini bisa diretas, tetapi dibutuhkan peretas yang gigih untuk melakukannya.
sumber
Jika Anda menggunakan DPAPI pengguna windows, http://msdn.microsoft.com/en-us/library/ms995355.aspx
Seperti yang dikatakan posting sebelumnya jika Anda menggunakan mac, gunakan rantai kunci.
Pada dasarnya semua ide lucu tentang cara menyimpan kunci pribadi Anda di dalam biner Anda cukup buruk dari perspektif keamanan sehingga Anda tidak boleh melakukannya. Siapa pun yang mendapatkan kunci pribadi Anda adalah masalah besar, jangan menyimpannya di dalam program Anda. Bergantung pada cara mengimpor aplikasi Anda, Anda dapat menyimpan kunci pribadi Anda di kartu pintar, di komputer jarak jauh kode Anda berbicara atau Anda dapat melakukan apa yang kebanyakan orang lakukan dan menyimpannya di tempat yang sangat aman di komputer lokal ("kunci store "yang seperti registri aman yang aneh) yang dilindungi oleh izin dan semua kekuatan OS Anda.
Ini adalah masalah terpecahkan dan jawabannya adalah BUKAN untuk menyimpan kunci di dalam program Anda :)
sumber
Coba ini . Kode sumber menjelaskan cara mengenkripsi dan mendekripsi dengan cepat semua string dalam proyek Visual Studio c ++ tertentu.
sumber
Salah satu metode yang baru-baru ini saya coba adalah:
part1
part2
part1
danpart2
part1
. Ini akan memverifikasi integritas data pribadi.MAKRO untuk mengisi data:
Misalkan, data pribadi berukuran 4 byte. Kami mendefinisikan makro untuk itu yang menyimpan data dengan instruksi penugasan dalam beberapa urutan acak.
#define POPULATE_DATA(str, i0, i1, i2, i3)\ {\ char *p = str;\ p[3] = i3;\ p[2] = i2;\ p[0] = i0;\ p[1] = i1;\ }
Sekarang gunakan makro ini dalam kode di mana Anda perlu menyimpan
part1
danpart2
, sebagai berikut:char part1[4] = {0}; char part2[4] = {0}; POPULATE_DATA(part1, 1, 2, 3, 4); POPULATE_DATA(part2, 5, 6, 7, 8);
sumber
Ada proyek header-only (sangat ringan) yang tidak jelas dibuat oleh adamyaxley yang bekerja sempurna. Ini didasarkan pada fungsi lambda dan makro dan mengenkripsi string litteral dengan sandi XOR pada waktu kompilasi. Jika perlu, kita bisa mengganti seed untuk setiap string.
Kode berikut tidak akan menyimpan string "hello world" dalam biner yang telah dikompilasi.
#include "obfuscate.h" int main() { std::cout << AY_OBFUSCATE("Hello World") << std::endl; return 0; }
Saya telah menguji dengan c ++ 17 dan visual studio 2019, dan memeriksa melalui IDA dan saya mengonfirmasi bahwa stringnya tersembunyi. Satu keuntungan berharga dibandingkan dengan ADVobfuscator adalah bahwa ia dapat diubah menjadi std :: string (sementara masih tersembunyi dalam biner yang dikompilasi):
std::string var = AY_OBFUSCATE("string");
sumber
Alih-alih menyimpan kunci pribadi dalam file yang dapat dieksekusi, Anda mungkin ingin memintanya dari pengguna dan menyimpannya melalui pengelola kata sandi eksternal , sesuatu yang mirip dengan Akses Keychain Mac OS X.
sumber
Bergantung pada konteks tetapi Anda bisa menyimpan hash kunci plus garam (string konstan, mudah dikaburkan).
Kemudian ketika (jika) pengguna memasukkan kunci, Anda menambahkan garam , menghitung hash dan membandingkan.
The garam mungkin tidak perlu dalam hal ini, berhenti serangan kamus brute-force jika hash dapat diisolasi (pencarian Google juga telah tahu untuk bekerja).
Seorang hacker masih hanya perlu memasukkan instruksi jmp di suatu tempat untuk melewati semuanya, tapi itu lebih rumit daripada pencarian teks sederhana.
sumber