Apakah ada implementasi javascript SHA-256 yang secara umum dianggap dapat dipercaya?

99

Saya menulis login untuk forum, dan perlu hash sisi klien kata sandi di javascript sebelum mengirimkannya ke server. Saya mengalami masalah dalam mencari tahu implementasi SHA-256 mana yang benar-benar dapat saya percayai. Saya berharap akan ada semacam skrip otoritatif yang digunakan semua orang, tetapi saya menemukan banyak proyek berbeda semua dengan penerapannya sendiri.

Saya menyadari bahwa menggunakan crypto orang lain selalu merupakan lompatan keyakinan kecuali Anda memenuhi syarat untuk meninjaunya sendiri, dan bahwa tidak ada definisi universal tentang "dapat dipercaya", tetapi ini sepertinya sesuatu yang umum dan cukup penting sehingga harus ada semacam konsensus tentang apa yang akan digunakan. Apakah saya hanya naif?

Edit karena muncul banyak di komentar: Ya, kami melakukan hash yang lebih ketat lagi di sisi server. Hash sisi klien bukanlah hasil akhir yang kita simpan di database. Hash sisi klien adalah karena klien manusia memintanya. Mereka belum memberikan alasan khusus mengapa, mungkin mereka suka berlebihan.

jono
sumber
9
Bukan untuk keluar dari topik, tapi mengapa Anda mencirikan kata sandi di sisi klien?
Steve
5
@ Bahkan tidak dekat. "Jangan gulingkan milik Anda sendiri" berlaku untuk menemukan algoritme Anda sendiri, menulis penerapan algoritme Anda sendiri, mengembangkan protokol Anda sendiri di atas algoritme kripto, atau hampir semua hal di atas menggunakan abstraksi tingkat tinggi yang tersedia. Jika Anda pikir Anda akan aman berpegang teguh pada inti yang aman, dan hanya menulis kode perekat, Anda akan mengalami waktu yang buruk.
Stephen Touset
26
jika Anda menggunakan kata sandi yang di-hash tanpa protokol tantangan / respons, maka kata sandi yang di-hash adalah kata sandi dan itu benar-benar sama dengan mengirimkan kata sandi dalam teks yang jelas.
ddyer
26
@ddyer Ada beberapa nilai dalam melindungi sandi teks biasa pengguna untuk semua situs lain tempat mereka mungkin menggunakannya, jika bukan untuk situs kami secara khusus. Ini adalah perbaikan mudah yang mungkin tidak akan membantu kami tetapi berpotensi membantu pengguna jika kami mengacaukan suatu tempat. Dan seperti yang saya katakan, permintaan klien, tidak ada yang bisa saya lakukan bahkan jika saya mau.
jono
5
@Anorov Saya lebih dari terbuka untuk mengubah pikiran saya :) tetapi dalam kasus ini, saya tidak begitu mengerti bagaimana poin Anda berlaku. Kami melakukan hash kata sandi dua kali: sekali di sisi klien dengan SHA-256 sederhana, dan sekali di sisi server dengan sesuatu yang lebih menuntut. Yang pertama untuk melindungi teks biasa jika terjadi MITM atau sejenisnya, dan yang kedua untuk perlindungan kasar. Bahkan jika Anda memiliki database dan hash admin, Anda tidak dapat menggunakannya secara langsung untuk memvalidasi login.
jono

Jawaban:

114

OUTDATED: Banyak browser modern sekarang memiliki dukungan kelas satu untuk operasi crypto. Lihat jawaban Vitaly Zdanevich di bawah ini.


The Stanford JS Crypto Library berisi implementasi SHA-256. Sementara crypto di JS tidak benar-benar merupakan upaya yang diperiksa dengan baik seperti platform implementasi lainnya, yang satu ini setidaknya sebagian dikembangkan oleh, dan sampai batas tertentu disponsori oleh, Dan Boneh , yang merupakan nama mapan dan tepercaya dalam kriptografi , dan berarti bahwa proyek tersebut memiliki pengawasan oleh seseorang yang benar-benar tahu apa yang dia lakukan. Proyek ini juga didukung oleh NSF .

Namun, perlu diperhatikan
bahwa ... ... bahwa jika Anda melakukan hash pada sisi klien kata sandi sebelum mengirimkannya, maka hash adalah kata sandinya , dan kata sandi asli menjadi tidak relevan. Penyerang hanya perlu mencegat hash untuk menyamar sebagai pengguna, dan jika hash itu disimpan tanpa modifikasi di server, maka server menyimpan kata sandi yang sebenarnya (hash) dalam teks biasa .

Jadi keamanan Anda sekarang lebih buruk karena Anda memutuskan untuk menambahkan perbaikan Anda sendiri ke skema yang sebelumnya dipercaya.

tylerl
sumber
32
Namun, jika Anda melakukan hash lagi di server, praktik ini sangat sah.
Nick Brunt
13
@NickBrunt jika Anda melakukan hash di server maka Anda tidak menyimpan kata sandi yang ditransmisikan, tetapi Anda belum menambahkan keamanan apa pun selain mentransmisikan kata sandi asli yang bertentangan dengan transmisi hash kata sandi, karena dalam kedua kasus yang Anda transmisikan adalah nyata password.
tylerl
54
Benar, tetapi itu tidak lebih buruk daripada mengirim kata sandi yang mungkin digunakan di tempat lain di Internet dalam teks biasa.
Nick Brunt
15
Saya setuju dengan @NickBrunt. Lebih baik jika penyerang membaca string hash acak yang mungkin hanya cocok dengan aplikasi ini daripada kata sandi asli yang mungkin digunakan di beberapa tempat.
Aebsubis
13
Saya perlu menggunakan hashing sisi klien karena saya tidak ingin server pernah melihat sandi teks biasa pengguna. Bukan untuk keamanan tambahan pada layanan yang AKU sediakan, tetapi untuk pengguna. Saya tidak hanya menyimpan hash pengguna yang diasinkan dengan garam konstan (konstan per pengguna, tidak secara global) tetapi juga melakukan hashing ulang dengan garam sesi acak setiap login yang TIDAK memberikan sedikit keamanan ekstra melalui jaringan terhadap sniffing. Jika server dikompromikan, game akan berakhir, tetapi itu berlaku untuk apa pun yang tidak benar-benar p2p.
Hatagashira
55

Di https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest saya menemukan cuplikan ini yang menggunakan modul js internal:

async function sha256(message) {
    // encode as UTF-8
    const msgBuffer = new TextEncoder().encode(message);                    

    // hash the message
    const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer);

    // convert ArrayBuffer to Array
    const hashArray = Array.from(new Uint8Array(hashBuffer));

    // convert bytes to hex string                  
    const hashHex = hashArray.map(b => ('00' + b.toString(16)).slice(-2)).join('');
    return hashHex;
}

Perhatikan bahwa crypto.subtlehanya tersedia pada httpsatau localhost- misalnya untuk pengembangan lokal python3 -m http.serverAnda dengan Anda perlu menambahkan baris ini ke /etc/hosts: 0.0.0.0 localhost

Reboot - dan Anda dapat membuka localhost:8000dengan bekerja crypto.subtle.

Vitaly Zdanevich
sumber
2
Ini luar biasa. Terima kasih. Apakah API yang sebanding tersedia di Node.js?
Con Antonakos
2
Pustaka 'crypto' bawaan seharusnya berfungsi dengan
tytho
18

Implementasi SHA-256 Forge cepat dan andal.

Untuk menjalankan pengujian pada beberapa implementasi JavaScript SHA-256, buka http://brillout.github.io/test-javascript-hash-implementations/ .

Hasil pada mesin saya menyarankan forge untuk menjadi implementasi tercepat dan juga jauh lebih cepat daripada Perpustakaan Crypto Javascript Stanford (sjcl) yang disebutkan dalam jawaban yang diterima.

Forge berukuran 256 KB, tetapi mengekstrak kode terkait SHA-256 mengurangi ukurannya menjadi 4,5 KB, lihat https://github.com/brillout/forge-sha256

brillout
sumber
Saya berhasil menginstalnya dengan npm install node-forge, tetapi sekarang, bagaimana cara menggunakan perpustakaan membuat hash dari string foo?
pengguna
17

Bagi yang tertarik, berikut kode untuk membuat hash SHA-256 menggunakan sjcl:

import sjcl from 'sjcl'

const myString = 'Hello'
const myBitArray = sjcl.hash.sha256.hash(myString)
const myHash = sjcl.codec.hex.fromBits(myBitArray)
Danny Sullivan
sumber
5
Ini harus dimasukkan dalam jawaban yang diterima untuk mereka yang ingin menggunakan solusi di sana. (Heck, bahkan dokumen mereka sendiri tidak memiliki potongan seperti ini)
MagicLegend
Ini jawaban terbaik. Saya mencoba solusi kripto tetapi tidak berhasil untuk saya.
Augusto Gonzalez
12

Tidak, tidak ada cara menggunakan JavaScript browser untuk meningkatkan keamanan kata sandi. Saya sangat menyarankan Anda membaca artikel ini . Dalam kasus Anda, masalah terbesar adalah masalah telur ayam:

Apa "masalah telur ayam" dengan pengiriman kriptografi Javascript?

Jika Anda tidak mempercayai jaringan untuk memberikan kata sandi, atau, lebih buruk lagi, tidak mempercayai server untuk tidak menyimpan rahasia pengguna, Anda tidak dapat mempercayai mereka untuk mengirimkan kode keamanan. Penyerang yang sama yang mengendus kata sandi atau membaca buku harian sebelum Anda memperkenalkan crypto hanya membajak kode crypto setelah Anda melakukannya.

[...]

Mengapa saya tidak dapat menggunakan TLS / SSL untuk mengirimkan kode kripto Javascript?

Kamu bisa. Ini lebih sulit daripada kedengarannya, tetapi Anda mengirimkan kripto Javascript dengan aman ke browser menggunakan SSL. Masalahnya adalah, setelah membuat saluran aman dengan SSL, Anda tidak lagi memerlukan kriptografi Javascript; Anda memiliki kriptografi "nyata".

Yang mengarah ke ini:

Masalah dengan menjalankan kode crypto di Javascript adalah bahwa hampir semua fungsi yang bergantung pada crypto dapat diganti secara diam-diam oleh konten apa pun yang digunakan untuk membangun halaman hosting. Keamanan kripto dapat dibatalkan di awal proses (dengan menghasilkan angka acak palsu, atau dengan merusak konstanta dan parameter yang digunakan oleh algoritme), atau nanti (dengan memasukkan materi kunci kembali ke penyerang), atau --- dalam skenario yang paling mungkin --- dengan melewati crypto seluruhnya.

Tidak ada cara yang dapat diandalkan untuk setiap bagian kode Javascript untuk memverifikasi lingkungan eksekusinya. Kode kripto Javascript tidak bisa bertanya, "apakah saya benar-benar berurusan dengan generator nomor acak, atau dengan beberapa faksimili yang disediakan oleh penyerang?" Dan tentu saja tidak dapat menyatakan "tidak ada yang diizinkan untuk melakukan apa pun dengan rahasia crypto ini kecuali dengan cara yang saya, penulis, setujui". Ini adalah dua properti yang sering disediakan di lingkungan lain yang menggunakan kripto, dan tidak mungkin di Javascript.

Pada dasarnya masalahnya adalah ini:

  • Klien Anda tidak mempercayai server Anda, jadi mereka ingin menambahkan kode keamanan ekstra.
  • Kode keamanan itu dikirimkan oleh server Anda (yang tidak mereka percayai).

Atau sebagai alternatif,

  • Klien Anda tidak mempercayai SSL, jadi mereka ingin Anda menggunakan kode keamanan ekstra.
  • Kode keamanan itu dikirim melalui SSL.

Catatan: Selain itu, SHA-256 tidak cocok untuk ini, karena sangat mudah untuk memaksa kata sandi non-iterasi tanpa salt . Jika Anda tetap memutuskan untuk melakukannya, cari implementasi dari bcrypt , scrypt atau PBKDF2 .

Brendan Long
sumber
1
Ini tidak benar. Keamanan kata sandi dapat ditingkatkan dengan melakukan hashing di sisi klien. Juga salah bahwa SHA-256 tidak cocok selama sesuatu seperti PBKDF2 digunakan di sisi server. Ketiga, meskipun artikel matasano berisi wawasan yang berguna, intinya salah karena kami sekarang memiliki aplikasi browser yang secara fundamental mengubah masalah ayam dan telur.
pengguna239558
2
Anda benar bahwa PBKDF2 harus digunakan pada klien. Kemarahan terhadap kripto javascript sisi klien mengasumsikan bahwa halaman awal dan permintaan berikutnya memiliki properti keamanan yang sama. Itu jelas tidak benar untuk aplikasi browser yang halaman awalnya dipasang secara terpisah dan statis. Ini juga tidak benar untuk halaman mana pun dengan semantik cache-forever. Dalam kasus ini, TOFU yang persis sama dengan saat menginstal program sisi klien. Hal ini juga berlaku dalam penyiapan yang lebih kompleks di mana penayangan halaman statis dan dinamis dipisahkan di server.
pengguna239558
3
Adalah sah untuk menghindari administrator sistem memiliki akses ke kata sandi pengguna, jadi hashing di sisi klien mencegah DBA atau profesional TI lainnya untuk melihat kata sandi pengguna dan mencoba menggunakannya kembali untuk mengakses layanan / sistem lain, karena banyak pengguna mengulangi kata sandi mereka .
Yamada
1
@Xenland Yang Anda lakukan hanyalah membuat kata sandi baru yaitu "token + kata sandi". Semua masalah asli masih berlaku. Misalnya, penyerang dapat mengganti JavaScript dengan kode untuk mengirimi mereka token + kata sandi.
Brendan Long
2
@Xenland Masalah ayam dan telur tidak ada hubungannya dengan permintaan yang Anda kirim. Masalahnya adalah klien Anda menggunakan kode JavaScript yang diunduh melalui koneksi yang tidak aman untuk melakukan pekerjaan keamanan. Satu-satunya cara untuk mengirim JavaScript dengan aman ke browser web adalah dengan menyajikannya melalui TLS, dan setelah Anda melakukannya, Anda tidak perlu melakukan apa pun karena koneksi Anda sudah aman. Tidak masalah apa protokol Anda jika Anda menggunakan kode JavaScript yang dapat diganti oleh penyerang.
Brendan Long
10

Saya menemukan implementasi ini sangat mudah digunakan. Juga memiliki lisensi gaya BSD yang murah hati:

jsSHA: https://github.com/Caligatio/jsSHA

Saya membutuhkan cara cepat untuk mendapatkan representasi hex-string dari hash SHA-256. Hanya butuh 3 baris:

var sha256 = new jsSHA('SHA-256', 'TEXT');
sha256.update(some_string_variable_to_hash);
var hash = sha256.getHash("HEX");
cobbzilla.dll
sumber
1

Selain lib Stanford yang disebutkan Tylerl. Saya menemukan jsrsasign sangat berguna (repo Github di sini: https://github.com/kjur/jsrsasign ). Saya tidak tahu bagaimana tepatnya dapat dipercaya, tetapi saya telah menggunakan API-nya SHA256, Base64, RSA, x509 dll dan bekerja dengan cukup baik. Bahkan, itu termasuk lib Stanford juga.

Jika semua yang Anda ingin lakukan adalah SHA256, jsrsasign mungkin berlebihan. Tetapi jika Anda memiliki kebutuhan lain di bidang terkait, saya rasa itu cocok.

Menjauh
sumber
2
Lebih aman dan lebih cepat untuk menggunakan developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API
Vitaly Zdanevich
2
Setuju, tetapi untuk bersikap adil, saya menjawab pertanyaan ini pada tahun 2015 sementara spesifikasi API kripto web mozilla diterbitkan Jan 2017
Faraway