Praktik terbaik apa yang harus digunakan dalam skrip login PHP?

27

Saya ingin menulis ulang skrip login saya untuk situs web klien agar lebih aman. Saya ingin tahu praktik terbaik apa yang dapat saya terapkan dalam hal ini. Panel kontrol yang dilindungi kata sandi ada dalam kelimpahannya, tetapi sangat sedikit yang tampaknya memuji praktik terbaik dalam hal penulisan kode, kecepatan dan keamanan.

Saya akan menggunakan PHP dan database MYSQL.

Saya dulu menggunakan md5, tapi saya melihat bahwa sha256 atau sha512 akan lebih baik (bersama dengan hash dan garam aman).

Beberapa skrip login mencatat alamat ip sepanjang sesi atau bahkan agen pengguna, tetapi saya ingin menghindarinya karena tidak kompatibel dengan server proxy.

Saya juga sedikit di belakang praktik terbaik dalam menggunakan sesi di PHP 5 (terakhir kali saya membaca adalah PHP 4) sehingga beberapa praktik terbaik dengan ini akan sangat membantu.

Terima kasih.

baritoneuk
sumber
1
Lemparkan skrip Anda di codereview.SE!
Chris
@ Chris CodeReview membantu dengan kejelasan kode dan sejenisnya. Anda tentu tidak seharusnya pergi ke mereka untuk keamanan. Jika ada, mereka hampir selalu mengingatkan Anda untuk tidak "menggulung sendiri" yang pada dasarnya adalah jawaban yang sama dengan yang akan ia dapatkan di komunitas pemrograman mana pun di SE.
Alternatex

Jawaban:

21

Pikiran terbaik adalah tidak menemukan kembali roda. Tapi, saya mengerti, di dunia PHP mungkin sulit untuk menemukan komponen berkualitas tinggi yang sudah melakukan itu (bahkan saya cukup yakin bahwa kerangka kerja mengimplementasikan hal-hal seperti itu dan implementasinya sudah diuji, solid, ditinjau kode, dll. )

Jika, karena beberapa alasan, Anda tidak bisa menggunakan kerangka kerja, inilah beberapa saran:

Saran terkait keamanan

  • Gunakan PBKDF2 atau Bcrypt jika Anda bisa . Itu dilakukan untuk itu.

    Dasar Pemikiran: kedua algoritme dapat membuat proses hashing menjadi lambat secara sewenang-wenang, yang persis seperti yang Anda inginkan ketika hashing password (alternatif yang lebih cepat berarti kekuatan kasar yang lebih mudah). Idealnya, Anda harus menyesuaikan parameter sehingga proses menjadi lebih lambat dan lebih lambat dari waktu ke waktu pada perangkat keras yang sama, sementara perangkat keras baru yang lebih cepat dirilis.

  • Jika Anda tidak bisa, setidaknya jangan gunakan MD5 / SHA1. Tak pernah. Lupakan itu . Gunakan SHA512 sebagai gantinya, misalnya. Gunakan garam juga.

    Dasar Pemikiran: MD5 dan SHA1 terlalu cepat. Jika penyerang memiliki akses ke database Anda yang berisi hash dan memiliki (bahkan tidak terlalu) mesin yang kuat, kasar memaksa kata sandi cepat dan mudah. Jika tidak ada garam, kemungkinan penyerang menemukan kata sandi yang sebenarnya meningkat (yang bisa membahayakan tambahan jika kata sandi itu digunakan kembali di tempat lain).

  • Di PHP 5.5.0 dan yang lebih baru, gunakan password_hashdan password_verify.

    Dasar Pemikiran: memanggil fungsi yang disediakan oleh kerangka kerja itu mudah, sehingga risiko membuat kesalahan berkurang. Dengan kedua fungsi tersebut, Anda tidak perlu memikirkan parameter yang berbeda seperti hash. Fungsi pertama mengembalikan string tunggal yang kemudian dapat disimpan dalam database. Fungsi kedua menggunakan string ini untuk verifikasi kata sandi.

  • Lindungi diri Anda dari kekerasan . Jika pengguna mengirimkan kata sandi yang salah ketika dia sudah mengirimkan kata sandi yang salah lain 0,01 detik yang lalu, itu alasan yang baik untuk memblokirnya. Sementara manusia bisa mengetik cepat, mereka mungkin tidak dapat yang cepat.

    Perlindungan lain adalah dengan menetapkan batas kegagalan per jam. Jika pengguna mengirimkan 3600 kata sandi yang salah dalam satu jam, 1 kata sandi per detik, sulit untuk percaya bahwa ini adalah pengguna yang sah.

    Dasar Pemikiran: jika kata sandi Anda di-hash dengan cara yang tidak aman, brute force bisa sangat efektif. Jika kata sandi disimpan dengan aman, brute force masih menyia-nyiakan sumber daya server dan bandwidth jaringan Anda, menyebabkan kinerja yang lebih rendah untuk pengguna yang sah. Deteksi brute force tidak mudah untuk dikembangkan dan dilakukan dengan benar, tetapi untuk sistem apa pun kecuali yang kecil, itu sangat berharga.

  • Jangan meminta pengguna Anda untuk mengubah kata sandi mereka setiap empat minggu. Ini sangat menjengkelkan dan mengurangi keamanan, karena mendorong keamanan berbasis post-it.

    Dasar Pemikiran: gagasan bahwa memaksa kata sandi untuk diubah setiap n minggu melindungi sistem dari brute force adalah salah. Serangan brute force biasanya berhasil dalam hitungan detik, menit, jam atau hari, yang membuat perubahan kata sandi bulanan tidak relevan. Di sisi lain, pengguna buruk dalam mengingat kata sandi. Jika, lebih dari itu, mereka perlu mengubahnya, mereka akan mencoba menggunakan kata sandi yang sangat sederhana atau hanya mencatat kata sandi mereka pada post-it-nya.

  • Audit segalanya, setiap saat. Simpan log masuk, tetapi jangan pernah menyimpan kata sandi dalam log audit. Pastikan bahwa log audit tidak dapat dimodifikasi (yaitu Anda dapat menambahkan data di akhir, tetapi tidak mengubah data yang ada). Pastikan bahwa log audit tunduk pada cadangan reguler. Idealnya, log harus disimpan pada server khusus dengan akses yang sangat ketat: jika server lain diretas, penyerang tidak akan dapat menghapus log untuk menyembunyikan keberadaannya (dan jalur yang diambil selama serangan).

  • Jangan ingat kredensial pengguna dalam cookie, kecuali jika pengguna meminta untuk melakukannya (kotak centang "Ingat saya" harus tidak dicentang secara default untuk menghindari kesalahan manusia).

Kemudahan penggunaan saran

  • Biarkan pengguna mengingat kata sandi jika dia mau, bahkan jika sebagian besar browser sudah memiliki fitur ini.
  • Jangan menggunakan pendekatan Google ketika alih-alih meminta nama pengguna dan kata sandi, pengguna kadang-kadang diminta hanya kata sandi , nama pengguna sudah ditampilkan di a <span/>. Peramban tidak dapat mengisi bidang kata sandi dalam hal ini (setidaknya Firefox tidak dapat melakukannya), sehingga memaksa untuk keluar, lalu masuk dengan formulir biasa, diisi oleh peramban.
  • Jangan gunakan popup yang diaktifkan JavaScript untuk masuk. Ini merusak fitur mengingat kata sandi browser (dan mengisap dalam semua kasus).
  • Biarkan pengguna memasukkan nama pengguna atau alamat suratnya . Ketika saya mendaftar, kadang-kadang nama pengguna yang ingin saya masukkan sudah diambil, jadi saya harus membuat yang baru. Saya memiliki semua kesempatan untuk melupakan nama ini dalam dua jam.
  • Selalu simpan tautan ke fitur "Lupa kata sandi" di dekat formulir masuk. Jangan tampilkan hanya ketika pengguna gagal masuk: pengguna yang sama sekali tidak ingat kata sandinya sama sekali tidak tahu bahwa ia harus mengirimkan yang salah untuk melihat tautan "Lupa kata sandi saya".
  • Jangan menggunakan keamanan dengan post-it .
  • Jangan membuat aturan bodoh yang terkait dengan kata sandi untuk membuatnya lebih lemah . Contoh: "Kata sandi Anda harus dimulai dengan huruf kecil."; "Kata sandi Anda tidak boleh mengandung spasi."
Arseni Mourzenko
sumber
Tidak menemukan bcrypt. Apa alasan Anda merekomendasikan hal itu? Kenapa tidak md5 / sha1? Terima kasih untuk sisa sarannya!
baritoneuk
Alasannya sederhana: bcryptdilakukan oleh orang-orang yang sangat berkualitas. Ini juga diuji, ditinjau, dll. Jadi tidak ada alasan untuk menerapkan hal yang sama sendiri. Ini seperti membuat algoritma kriptografi Anda sendiri untuk situs web Anda. * "Mengapa tidak md5 / sha1?": Lihat misalnya en.wikipedia.org/wiki/MD5#Security
Arseni Mourzenko
5
bcrypt lambat dan seperti yang disebutkan diimplementasikan oleh para profesional. md5 adalah algoritma hashing bukan enkripsi yang tidak sama persis. Memukul file dengan cepat bagus, md5 akan digunakan di sini ... membuat kata sandi tidak perlu begitu cepat, bcrypt dapat membantu di sini. Alasan saya mengatakan ini adalah bahwa brute force menjadi lebih sulit ketika algoritma crypt "lambat".
Chris
2
@baritoneuk: minimum itu keren. Tetapi saya tidak dapat menghitung berapa banyak situs yang pernah saya kunjungi yang memiliki batasan seperti panjang maksimal , tidak ada karakter non-alfanumerik, dll. Yang membingungkan, jika Anda akan tetap menggunakan kata sandi - yang membuat saya khawatir bahwa mereka tidak , mereka hanya menyimpannya di database mereka di tempat yang jelas.
Carson63000
1
@ Brian Ortiz: tidak selalu. Terkadang ide yang bagus untuk menetapkan batas. Jika Anda tidak melakukannya, apakah Anda menguji apakah aplikasi Anda bekerja untuk waktu yang lama? Bagaimana? Jika Anda tidak mengujinya, bagaimana Anda bisa yakin apakah itu berhasil? Sebaliknya, saat menetapkan batas ke nilai yang masuk akal, seperti 100, Anda dapat dengan mudah menguji kata sandi 100 karakter.
Arseni Mourzenko
6
  1. Situs Anda harus menggunakan HTTPS. Anda tidak boleh menampilkan halaman login atau menerima login dari koneksi yang tidak dienkripsi.
  2. Cookie Anda harus dibatasi hanya untuk HTTP dan dibatasi untuk mengamankan koneksi jika Anda menggunakan HTTPS.
  3. Proses masuk harus memakan waktu tidak kurang dari 2 detik (1 jika Anda pikir 2 detik terlalu lama). Gunakan hash aman saat menyimpan dan memverifikasi kata sandi, dan gunakan garam yang lebih sulit ditebak. Gunakan bcryptjika memungkinkan. Jika tidak, gunakan beberapa jenis hash iterated lainnya.
  4. Jangan pernah membuat kueri basis data Anda dengan cara apa pun yang mengharuskan penggunaan fungsi seperti mysql_real_escape_string. Jangan pernah menggunakan penggabungan string untuk membuat kueri Anda. Gunakan kueri yang disiapkan dan parametrized. Sebagian besar, jika tidak semua, driver DB untuk PHP mendukungnya. Jika Anda tidak tahu bagaimana melakukannya, meluangkan waktu belajar bagaimana prepare, binddan executemenggunakan apa pun yang DB yang Anda gunakan. Ini satu - satunya cara pasti untuk melindungi diri Anda dari injeksi SQL. PDO mendukung ini.
  5. Dorong pengguna Anda untuk tidak menggunakan kata sandi yang sama yang mereka gunakan untuk email mereka. Ingatkan mereka bahwa jika situs Anda disusupi dan jika mereka menggunakan kata sandi yang sama di kedua tempat, seseorang dapat membajak email mereka.
greyfade
sumber
+1 untuk HTTPS, karena ada lebih banyak lalu lintas melalui jaringan WiFi publik. Tapi saya tidak setuju dengan poin 3: 2 detik terlalu panjang dari sudut pandang pengguna. 0,5 s. tidak apa-apa, terutama jika teknik anti-brute-force lain digunakan.
Arseni Mourzenko
Saya bingung pada poin nomor 4. Bagaimana Anda lolos dari konvensi pelarian data dengan mysql_real_escape_string? Semua data yang dikirimkan pengguna tidak boleh dipercaya dan difilter sesuai.
chrisw
@chrisw: Ya, semua input pengguna harus ditangani seolah-olah itu berbahaya. Tetapi ketika Anda menggunakan query parametrized, itu adalah, tidak ada bar, cara terbaik untuk menghentikan injeksi SQL . Jika Anda tidak menggunakan kueri yang disiapkan dan parametrized, Anda rentan terhadap injeksi SQL. mysql_real_escape_stringadalah keamanan palsu - itu bukan jaminan. Kueri parametrized adalah.
greyfade
Saya setuju sekarang setelah melakukan beberapa bacaan lebih lanjut ke dalamnya. Fungsi: mysql_real_escape_string umumnya aman untuk sebagian besar, saya tidak akan menganggapnya sebagai kewajiban BESAR, tapi saya bisa melihat bagaimana menggunakan pernyataan yang disiapkan jauh lebih efisien.
chrisw
1
Anda bisa menggunakan PDO.
Felix G
1

Gunakan hash satu arah asin (lebih disukai SHA512 menggunakan http://au2.php.net/manual/en/function.hash.php ) ... dengan cara itu, jika seseorang melakukan hack database Anda, bahkan jika mereka tahu garamnya , mereka tidak dapat mengekstrak kata sandi (tanpa tabel pelangi, yang akan menjadi terlalu lama untuk SHA512).

Gunakan HTTPS.

Jangan mengirim konfirmasi nama pengguna / kata sandi melalui email kembali ke pengguna ketika mereka mendaftar.

Jika Anda mengizinkan cookie untuk 'ingat saya' - tambahkan login tambahan untuk mengakses fungsi administrasi dan pengeditan profil.

Jika seorang pengguna salah kata sandi, jangan katakan mereka salah kata sandi (katakanlah mereka mendapat 'nama pengguna atau kata sandi salah' setiap saat) - dengan begitu, sedikit petunjuk tentang apa nama pengguna yang valid.

Coba dan terapkan nama layar versus login - dengan cara itu, lebih sedikit pengguna akan menampilkan nama login mereka pada daftar pengguna.

HorusKol
sumber
Sepenuhnya setuju dengan sedikit tentang tidak mengirim email dalam teks biasa melalui email. Saya telah kehilangan hitungan berapa kali situs web melakukan ini. Jika Anda memiliki 1 kata sandi untuk semua situs (yang untungnya saya tidak) maka berpotensi semua situs web terganggu. Situs web semacam itu mungkin menyimpan kata sandi dalam teks biasa juga. sigh
baritoneuk
1
  • Jangan pernah gunakan algoritma hashing MD5 atau SHA1. Selalu berpegang pada yang lebih baru seperti SHA512
  • Selalu gunakan garam acak yang kuat
  • Jangan pernah mengirim email kepada pengguna kata sandi mereka, bahkan jika "Lupa Kata Sandi"
  • Jangan pernah menggunakan fungsi mysql_ *. Mereka sudah lama terdepresiasi. Tetap pada PDO
  • Jangan pernah menyimpan kata sandi dalam sesi atau cookie
Robin Thomas
sumber
0

Berikut ini sedikit saran: pastikan cookie Anda tidak dapat diakses dengan JS untuk mencegah pencurian, lihat Melindungi Cookie Anda: Artikel HttpOnly oleh Jeff Atwood

... Melalui konstruksi cerdas, URL cacat hanya berhasil mencicit melewati pembersih. Kode yang diberikan terakhir, ketika dilihat di browser, memuat dan mengeksekusi skrip dari server jauh itu.

... siapa pun yang memuat halaman profil pengguna yang disuntikkan skrip ini tanpa disadari telah mengirimkan cookie browser mereka ke server jauh yang jahat!

Seperti yang telah kami lakukan, begitu seseorang memiliki cookie browser Anda untuk situs web tertentu, mereka pada dasarnya memiliki kunci kerajaan untuk identitas Anda di sana ...

James
sumber