Saya selalu ingin peretas tidak dapat memecahkan sandi orang lain di situs ini.
Misalnya, jika seorang pengguna membual kepada seorang peretas bahwa kata sandinya hanya terdiri dari angka, maka pengguna tersebut akan segera kehilangan akunnya.
Tetapi bagaimana jika pengguna memberikan kata sandi kepada istrinya melalui telepon, dan peretas mendengarnya?
Apa?! Apakah peretas tahu kata sandinya? Semua ini adalah kegagalan. Dapatkah Anda membantu pengguna seperti itu untuk mempersulit pembajakan akunnya? Saya selalu khawatir tentang pertanyaan ini dan saya pikir saya menemukan cara untuk melakukannya. Atau menemukannya kembali, seperti yang sering terjadi. Bagaimanapun, semuanya telah lama ditemukan sebelum kita.
Pengantar
- Pengguna ingin memiliki kata sandi di situs "12345".
- Seorang peretas dapat dengan mudah menebak kata sandi ini.
- Tetapi pengguna harus masuk, dan peretas tidak. Bahkan jika peretas mengetahui login dan kata sandi.
- dan tidak ada SMS dengan kode rahasia dan perantara dalam bentuk layanan tambahan. Hanya pengguna dan situs Anda dengan halaman login.
- dan akan relatif aman untuk mengatakan kepada istri Anda di dalam bus listrik: “Galya, saya mengubah kata sandi menjadi 123456 di situs situs untuk login alice - mereka mengatakan itu lebih populer daripada 12345 kami”. Dan jangan takut akun Anda akan diretas sebentar lagi.
Bagaimana cara kerja metode ini? Semua spesifik berada di bawah potongan.
Apa yang dibutuhkan?
- konsep tersebut hanya menjelaskan metode otentikasi
- implementasinya hanya perlu menyimpan " username ", " password ", " salt1 " dan " salt2 ". Ya, dua garam.
- lakukan tanpa tabel log dan penghitung di redis
- kami tidak akan menyimpan tabel dengan alamat IP
- kami tidak akan menggunakan SMS
- kami tidak akan memblokir upaya login. Seperti yang Anda ketahui dari upaya terakhir saya yang tidak berhasil, tidak ada gunanya memblokir pintu masuk - bahkan jika seorang peretas mencapai batas waktu, dia hanya akan mulai merusak kata sandi dari beberapa pengguna sekaligus. Selain itu, pengguna itu sendiri akan mengalami pembatasan. Jangan panggil dia untuk mendukung login ke situs Anda dengan gambar-gambar keren?
- pengguna dapat mengubah sandi kapan saja dan membuatnya tidak valid di perangkat lain. Ini adalah aturan yang umum, tetapi menurut saya itu layak untuk disebutkan.
- Anda dapat mempersulit proses menebak kata sandi menggunakan kamus bagi peretas (opsional, akan disebutkan di bawah).
Inti metode
Izinkan pengguna untuk memiliki kata sandi "12345", dan memecahkan kata sandi itu akan menjadi lebih sulit. Misalnya, cara menebak kata sandi yang terlihat seperti hash.
Bagaimana?
Bayangkan jika browser selalu memiliki salt unik yang dapat digunakan untuk mengasinkan kata sandi. Garam untuk setiap pengguna. Mengapa ini dibutuhkan? Untuk mengenkripsi. Misalnya, jika Anda mengenkripsi string "12345" dengan garam "saltsalt" menjadi argon2id, Anda akan mendapatkan "$ argon2id $ v = 19 $ m = 16, t = 2, p = 1 $ c2FsdHNhbHQ $ jX94laSi6vo9AhS + bHwbkg". Ganti garam dan hashnya akan berbeda. Satu algoritma akan mengenkripsi kata sandi yang sama secara berbeda dengan menggunakan garam yang berbeda untuk masing-masing. Baik.
Tetapi di mana awalnya mendapatkan garam ini? Ya, di sini dia duduk di depan monitor. Biarkan dia memeras dua atau tiga karakter ekstra dan login secara manusiawi. Apakah ada kucing berlarian? Nah, mari kita punya kucing. Apa itu kucing? Ini adalah kata rahasia kami. Kami akan mengirimkannya ke server saat pendaftaran, dan itu akan menghasilkan garam untuk kata ini. Dan kemudian dia akan mengirimkan garam ini kepada kita. Itu dia - browser memiliki garam. Sekarang kata sandinya. Dan kami juga mengenkripsi kata sandi dan memberinya garam dengan garam yang dikirim server.
Sekarang kami tidak menggunakan helm "12345". Kami mengirim hash, dan karena setiap pengguna memiliki garamnya sendiri, hashnya berbeda.
Tampaknya brute-force akan menjadi sakit sekarang: tidak hanya harus melakukan perhitungan tambahan dan mengulang string panjang hash argon alih-alih angka sederhana, tetapi juga setiap pengguna akan memiliki hash mereka sendiri - sekarang tidak ada gunanya mencoba string yang sama sebagai kata sandi untuk memeriksanya untuk semua orang pengguna. Katakanlah tiga pengguna telah memilih sandi yang sama: 12345. Tetapi hash mereka akan berbeda. Karena setiap orang memiliki garam yang berbeda.
- Browser harus menghitung hash kata sandi menggunakan garam yang dikirim server sebelumnya. Ini harus mengirimkan hash, bukan kata sandi itu sendiri.
- Server mengirim garam menggunakan kata rahasia yang hanya diketahui pengguna. Itu bisa sederhana. Misalnya - "kucing".
- Setiap pengguna harus memiliki garamnya sendiri.
- Dua pengguna yang telah memilih kata rahasia yang sama harus memiliki salt yang berbeda.
- Server tidak harus melaporkan apakah kata rahasia yang benar telah digunakan dan apakah salt itu benar untuk pengguna ini - jika tidak maka akan menjadi dua sandi sederhana secara brute force, bukan satu.
- Jika pengguna mengubah kata rahasia, garam juga berubah.
Artinya, untuk melindungi kata sandinya yang sederhana, pengguna harus menemukan kata lain yang sangat sederhana. Dia memasukkan kata ini di mana pun dia ingin diautentikasi, dan hanya kata sandi yang perlu dimasukkan. Sampai dia membersihkan kuenya.
- pergi ke situs
- memasukkan login dan kata rahasia
- memasukkan kata sandi
- siap
Kata sandi dan kata rahasia bisa sangat sederhana. Satu atau dua karakter. Misalnya, kata sandi adalah 12345 dan kata rahasia 42. Dan jika orang lain menemukan kata rahasia 42, maka itu tidak akan menakutkan.
Bagaimana itu bekerja. Konsep langkah demi langkah
Kami memiliki elemen berikut:
- server web
- database dan tabel pengguna:
- Gabung
- password_hash
- salt_unique_for_each_user
- salt_for_password
- browser pengguna
- browser peretas
- halaman login dan registrasi di situs
- skrip yang mencegat acara kirim untuk formulir login
Selanjutnya, kita memerlukan dua algoritme berbeda yang dapat diterapkan bahkan pada sistem enkripsi yang sama, hanya dengan parameter berbeda:
- ALG1 adalah algoritma enkripsi asimetris yang menghasilkan hash dari string dan salt. ALG1 (str, salt) = hash1. Algoritma ini hanya digunakan di server.
- ALG2 adalah algoritma enkripsi asimetris yang menghasilkan hash dari string dan salt. ALG2 (str, salt) = hash2. Algoritme ini digunakan secara publik dan harus memungkinkan untuk diterapkan pada klien (dalam contoh kami, dalam javascript).
Selain itu, kami membutuhkan dua algoritme yang lebih sederhana:
- ALG_SALT adalah algoritma yang menghitung garam acak sebagai string karakter. ALG_SALT () = garam. Algoritma ini hanya digunakan di server.
- ALG_PASS adalah algoritma yang menghasilkan kata sandi sederhana acak. ALG_PASS () = lulus. Algoritma ini hanya digunakan di server.
Acara selangkah demi selangkah
- Pengguna masuk ke halaman registrasi, karena dia belum memiliki login.
- Server menampilkan formulir dengan dua bidang: login + kata rahasia sederhana.
- Pengguna memilih login - alice
- Pengguna memilih kata rahasia - kucing
- Pengguna mengklik tombol Kirim .
Server memeriksa dan memastikan bahwa pengguna alice tidak ada di database.
Server menghitung nilai-nilai berikut:
$salt_unique_for_each_user = ALG_SALT(); // "saltsalt"
$salt_for_password = ALG1("cat", $salt_unique_for_each_user); // "$argon2id$v=19$m=16,t=2,p=1$c2FsdHNhbHQ$jX94laSi6vo9AhS+bHwbkg"
$user_simple_password = ALG_PASS(); // "12345"
$user_simple_password_hashed = ALG2($user_simple_password , $salt_for_password); // "$argon2id$v=19$m=16,t=2,p=1$JGFyZ29uMmlkJHY9MTkkbT0xNix0PTIscD0xJGMyRnNkSE5oYkhRJGpYOTRsYVNpNnZvOUFoUytiSHdia2c$b+6ROJVsZ62UXA7hEAg0AQ"
Server membuat catatan di tabel pengguna dan menyimpan data:
INSERT INTO `users`
(
login,
password_hashed,
salt_unique_for_each_user,
salt_for_password
)
VALUES
(
"alice",
"$argon2id$v=19$m=16,t=2,p=1$JGFyZ29uMmlkJHY9MTkkbT0xNix0PTIscD0xJGMyRnNkSE5oYkhRJGpYOTRsYVNpNnZvOUFoUytiSHdia2c$b+6ROJVsZ62UXA7hEAg0AQ",
"saltsalt",
"$argon2id$v=19$m=16,t=2,p=1$c2FsdHNhbHQ$jX94laSi6vo9AhS+bHwbkg"
).
Server menampilkan halaman sukses registrasi kepada pengguna dengan pesan: “Alice pengguna telah berhasil dibuat. Gunakan sandi sementara 12345 untuk masuk. "
Pengguna dengan gembira berteriak: "Hore, saya mendaftar di situs dengan nama panggilan alice dan mereka memberi saya kata sandi 12345. Sungguh kata sandi yang lucu dan sederhana!". Tetapi apartemen pengguna memiliki peredaman suara yang sangat buruk, dan tetangga peretasnya mendengar semuanya.
- Peretas memasukkan alamat situs web ke dalam perambannya.
- Browser peretas mengirimkan cookie kosong.
- Server memeriksa permintaan peretas untuk melihat apakah ada cookie "garam". Tidak menemukannya.
- Sebelum peretas mengirimkan nama pengguna dan kata sandi yang dicuri, browser perlu mengetahui salt untuk mengenkripsi kata sandi dengannya.
- Browser peretas belum menyimpan garam di cookie "garam".
- Server mengirimkan formulir login dengan dua bidang: login + kata rahasia untuk memungkinkan pengguna mendapatkan garam.
Peretas itu bingung. Biarkan dia sekarang.
- Pengguna kembali ke halaman login.
- Browser pengguna mengirimkan cookie kosong.
- Server memeriksa permintaan pengguna untuk melihat apakah ada cookie "garam". Tidak menemukannya.
- Sebelum pengguna mengirimkan nama pengguna dan kata sandi, browser harus mengetahui garam untuk mengenkripsi kata sandi dengannya.
- Browser pengguna belum menyimpan garam di cookie "garam".
- Server mengirimkan formulir login dengan dua bidang: login + kata rahasia untuk memungkinkan pengguna mendapatkan garam.
- Pengguna memasukkan login - alice , secret - cat dan mengklik tombol " Kirim ".
Server menerima permintaan dan melihat bahwa kata rahasia dikirim alih-alih kata sandi.
- — alice `salt_unique_for_each_user` -> $db_salt_unique_for_each_user `salt_for_password -> $db_salt_for_password`.
- , . : $salt_for_password = ALG1(«cat», $db_salt_unique_for_each_user).
- $salt_for_password . . 12345, , . — ` salt = $db_salt_for_password`. : ` login = «alice»`.
Penjelasan : Server tidak memberi tahu dengan cara apa pun salt dikirim - benar atau tidak. Hasil penggunaannya akan jelas saat mereka mencoba masuk dengan nama pengguna dan kata sandi yang benar.
- Pengguna menerima respons server. Halamannya memuat ulang atau langsung berubah secara dinamis.
- Browser pengguna mengirimkan cookie: login = alice , salt = "$ argon2id $ v = 19 $ m = 16, t = 2, p = 1 $ c2FsdHNhbHQ $ jX94laSi6vo9AhS + bHwbkg" .
- Server memeriksa permintaan pengguna untuk melihat apakah ada cookie "garam". Temukan dia.
- Browser sudah memiliki salt untuk mengenkripsi password.
- Server mengirimkan form login dengan dua field: login (sudah memiliki nilai alice ) + password.
- Pengguna memasukkan sandi sederhana 12345 dan mengklik tombol " Kirim ".
- Browser mengintersep acara onSubmit .
- Menghitung $ password_hashed = ALG2 ("12345", "$ argon2id $ v = 19 $ m = 16, t = 2, p = 1 $ c2FsdHNhbHQ $ jX94laSi6vo9AhS + bHwbkg").
- Mengirim data "alice" / $ argon2id $ v = 19 $ m = 16, t = 2, p = 1 $ JGFyZ29uMmlkJHY9MTkkbT0xNix0PTIscD0xJGMyRnNkSE5oYkhRJGpYOTRsYVNpNnZvOU. Tidak ada.
Server menerima permintaan otentikasi:
- Data login + kata sandi: "alice" / $ password_hashed
- Masuk ke database, dapatkan nilai ` password_hashed` -> $ db_password_hashed.
- Bandingkan $ db_password_hashed === $ password_hashed?
- Hash cocok, otorisasi berhasil.
Catatan: Dalam contoh saya, server membandingkan hash secara langsung. Tetapi Anda tidak dapat menyimpan string dalam database yang sebenarnya sudah berupa kata sandi. Mereka dapat dicuri dan kemudian digunakan dalam bentuk kata sandi masuk. Oleh karena itu, Anda perlu hash - tidak peduli seberapa aneh kedengarannya. Ini artinya Anda membutuhkan garam ketiga. Tapi itu harus disimpan bukan di database, tapi di variabel lingkungan. Namun, ini sudah merupakan detail implementasi yang saya tinggalkan untuk kesederhanaan.
Sementara itu, peretas kami memutuskan untuk menguji formulir login aneh ini:
- Peretas memasukkan login - alice , secret - dog dan mengklik tombol " Kirim ".
- Server menerima permintaan peretas dan melihat bahwa kata rahasia dikirim alih-alih kata sandi.
- — alice `salt_unique_for_each_user` -> $db_salt_unique_for_each_user `salt_for_password` -> $salt_for_password.
- , , : $result_fake_salt = ALG1(«dog», $db_salt_unique_for_each_user). , .
Server mengirimkan kembali nilai garam yang dihitung ke browser pengguna. Headernya menunjukkan - `set cookie salt = $ result_fake_salt`. Login juga disimpan: `set the cookie login =" alice "`.
Penjelasan : Untuk membantu peretas dengan kerja kerasnya, server mengiriminya garam. Tetapi tidak mungkin untuk menentukan dari luar apakah kata rahasia itu benar atau tidak.
- Peretas menerima respons server. Halamannya memuat ulang atau langsung berubah secara dinamis.
- Browser peretas mengirimkan cookie: login = alice , salt = $ result_fake_salt .
- Server memeriksa permintaan pengguna untuk melihat apakah ada cookie "garam". Temukan dia.
- Browser peretas sudah memiliki salt untuk mengenkripsi kata sandi.
- : ( alice) + .
- 12345 "".
- onSubmit.
- $password_hashed = ALG2(«12345», $result_fake_salt).
- «alice»/$password_hashed.
Server menerima permintaan otentikasi - "alice" / $ password_hashed.
Masuk ke database, dapatkan nilai `password_hashed` -> $ db_password_hashed.
Bandingkan: $ password_hashed === $ db_password_hashed? Nggak.
Hash dari sandi yang awalnya identik ini tidak cocok. Karena mereka diasinkan dengan cara berbeda.
Peretas tidak menyerah dan pergi untuk mendaftarkan pengguna lain di situs.
Secara tidak sengaja, dia memasukkan kata rahasia yang sama dengan pengguna di belakang tembok - kucing .
Peretas mendapatkan salt valid untuk kata sandi akun baru, dan mencoba menggantinya di skrip hashing.
Untungnya, pembuatan garam sandi menggunakan salt kedua (`salt_unique_for_each_user`), yang dibuat dengan cara baru untuk setiap pengguna. Jadi pengguna yang berbeda, bahkan dengan kata sandi yang sama dan - yang terpenting - kata rahasia, akan memiliki garam yang berbeda. Dan garam pengguna dengan kata rahasia yang sama tidak akan cocok dengan garam orang lain. Dan mencocokkan kata sandi juga tidak akan menjadi masalah.
Sekarang, mengenai kerumitan kata sandi brute force dalam kamus. Jika kita memodifikasi ALG2, yang umum untuk server dan klien, dan membuatnya melelahkan, itu akan sangat mempersulit pencarian peretas. Izinkan saya mengingatkan Anda bahwa ALG2 adalah proses mendapatkan hash kata sandi yang dikirim ke server. Di server, hash ini telah dihitung dan disimpan di database:
- server akan melakukan operasi ALG2 hanya sekali saat menulis kata sandi ke database atau mengubah kata sandi ke yang baru
- klien hanya akan melakukan operasi ALG2 selama otentikasi (yang tidak boleh disamakan dengan otorisasi). Katakanlah klien melakukan kesalahan beberapa kali saat memasukkan kata sandi - tidak apa-apa.
- Peretas akan melakukan ini setiap saat untuk setiap kata sandi, yang dapat dia beri ucapan selamat. Sangat sinis bahwa upaya besar-besaran akan dilakukan pada sandi seperti 123/1234/12345.
Pada mesin yang lemah, operasi dapat memakan waktu lebih lama daripada mesin yang cepat. Ini bisa jadi masalah. Jadi, Anda tidak perlu memperumit algoritme.
Saya akan menyelesaikan deskripsi konsep dengan laras tar:
- Jika pengguna secara tidak sengaja memasukkan kata rahasia dengan tidak benar, dia akan berada dalam situasi di mana dia tidak dapat masuk menggunakan kata sandinya. Anda harus mengatur ulang kata rahasia (dalam kasus kami, hapus cookie) dan mengirim permintaan lagi. Hal ini dapat dilakukan secara transparan dengan menekan satu tombol, namun sebelumnya pengguna harus menebak-nebak. Anda dapat memaksa reset pada 5 upaya login yang salah.
- Dua pengguna di komputer yang sama harus terus-menerus membuang garam satu sama lain.
- Dua komputer yang berbeda akan menerima garam kata sandi yang sama
- Jika garam diubah di server melalui satu komputer, komputer lain dengan garam lama tidak akan tahu bahwa itu perlu diubah
- Anda dapat mencuri salt dari komputer Anda dan menggunakannya untuk melakukan serangan yang sangat cepat ke akun Anda, karena mengetahui bahwa kata sandinya sangat sederhana.
... dan sesendok madu:
- . , "cat" , "termorectal" — . , . , . , .
- . `salt_for_password` , , , . .