Lompat ke kandungan utama
7 minit bacaan

Log masuk dengan CFML: kata laluan, sesi dan keselamatan dalam satu fail

Kata laluan tidak boleh disimpan sebagai teks biasa, dan sesi tidak akan berfungsi tanpa dua tetapan yang betul. Tutorial ini menunjukkan kedua-duanya, dengan kod yang boleh dicuba terus dan dimuat turun.

Coldfusion ColdFusionCFMLtutorialkeselamatanlog masuk

Cuba borang log masuk itu (daftar akaun demo, kemudian masuk) Boleh dicuba terus di laman ini. Data dikosongkan sendiri, dan jangan guna kata laluan sebenar.

Muat turun kod penuh (fail zip) log-masuk.cfm + Application.cfc + jadual.sql + arahan pemasangan dalam Bahasa Melayu.

Papan kekunci komputer dengan kad kunci akses di atas meja gelap bercahaya biru
Imej dijana AI.

Borang log masuk ialah tugasan pertama yang perlu difahami oleh setiap pembangun web, dan juga tugasan yang paling banyak disalah tulis. Masalahnya bukan borangnya. Masalahnya ialah apa yang berlaku kepada kata laluan itu selepas butang ditekan.

Tutorial ini membina satu sistem log masuk yang lengkap dalam satu fail: daftar akaun, log masuk, kawasan yang hanya kelihatan selepas masuk, dan keluar. Semuanya menggunakan teg CFML asas, dan setiap bahagian dalam tutorial ini sudah pun dicuba pada aplikasi sebenar sebelum ia ditulis di sini, supaya kod yang awak salin benar-benar berjalan.

Aplikasi yang dibina boleh dicuba terus, dan kodnya boleh dimuat turun. Data demo dikosongkan sendiri, jadi jangan guna kata laluan sebenar awak di situ.

Perkara paling penting: kata laluan tidak pernah disimpan

Hampir setiap tutorial menunjukkan baris ini, dan hampir setiap tutorial berhenti di situ.

<cfset cincang = hash( kata, "SHA-256" )>

Baris itu berkesan, tetapi ia tidak selamat untuk kata laluan. Angka di bawah ini diukur pada satu pelayan kecil, dan ia menunjukkan masalahnya dengan jelas.

  • SHA-256 sekali sahaja: kira-kira 1,250,000 cincang sesaat, walaupun dijalankan dalam CFML atas mesin yang sederhana.
  • SHA-256 yang sama, diulang 100,000 kali: kira-kira 12 percubaan sesaat.

Perbezaannya 100,000 kali ganda, dan ia bahasa yang sama. Sebabnya mudah. Cincang biasa direka supaya pantas, kerana komputer perlu mengesahkan fail besar dengan pantas. Untuk kata laluan, kepantasan itulah musuhnya. Penyerang yang berjaya mencuri jadual pengguna boleh mencuba berjuta-juta teka-teki sesaat ke atas cincang yang pantas, dan berjuta-juta teka-teki itu cukup untuk memecahkan kata laluan yang lemah dalam masa yang singkat.

Cara yang betul ialah mengulanginya, dan menambahkan garam, iaitu nilai rawak yang berbeza bagi setiap pengguna. Garam bermaksud dua orang yang menggunakan kata laluan yang sama tetap mempunyai cincang yang berbeza.

<!--- garam rawak untuk setiap pengguna --->
<cfset garam   = generateSecretKey( "AES" )>

<!--- SHA-256 diulang 100,000 kali (PBKDF2) --->
<cfset cincang = generatePBKDFKey( "PBKDF2WithHmacSHA256", kata, garam, 100000, 256 )>
Satu perkara yang patut awak tahu tentang enjin. Fungsi bcrypt, iaitu fungsi yang biasa digunakan untuk mencincang kata laluan, tidak disediakan oleh enjin yang menjalankan contoh ini. Yang ada ialah generatePBKDFKey, iaitu SHA-256 yang diulang di dalam enjin, dan itulah yang digunakan di atas. Kalau awak tidak pasti fungsi apa yang ada pada enjin awak sendiri, getFunctionList() menyenaraikan kesemuanya. Cara itu lebih baik daripada meneka nama fungsi, dan ia juga cara untuk tahu sama ada enjin awak perlukan ganti yang lain. Kalau enjin awak tiada generatePBKDFKey, gelung hash() sendiri memberi kesan yang sama, cuma lebih perlahan, iaitu sekitar 20,000 ulangan dalam 21 milisaat pada mesin sederhana.

Jadual: apa yang perlu disimpan

Perhatikan tiga medan untuk kata laluan, bukan satu. Ini disengajakan, kerana garam dan bilangan putaran perlu disimpan bersama cincang supaya ia boleh disahkan kemudian, dan supaya ia boleh dinaikkan pada masa hadapan tanpa memaksa semua orang menukar kata laluan.

CREATE TABLE demo_pengguna (
  id             INT AUTO_INCREMENT PRIMARY KEY,
  nama           VARCHAR(60)  NOT NULL,
  emel           VARCHAR(120) NOT NULL,
  kata_hash      VARCHAR(120) NOT NULL,
  garam          VARCHAR(60)  NOT NULL,
  putaran        INT          NOT NULL,
  cubaan         INT          NOT NULL DEFAULT 0,
  sekat_sehingga DATETIME     NULL,
  dicipta        DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
  UNIQUE KEY uniq_emel (emel)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

Medan cubaan dan sekat_sehingga mengira percubaan log masuk yang salah. Medan UNIQUE pada emel pula memastikan satu emel hanya boleh didaftarkan sekali, dan ia melindungi sistem daripada dua akaun yang sama yang dicipta serentak.

Mendaftar: sahkan dahulu, simpan kemudian

<cfset nama = trim( form.nama )>
<cfset emel = lcase( trim( form.emel ) )>
<cfset kata = form.kata>

<cfif len( nama ) lt 2>
    <cfset mesej = "Nama mesti antara 2 hingga 60 aksara.">
<cfelseif not reFind( "^[^@\s]+@[^@\s]+\.[a-zA-Z]{2,}$", emel )>
    <cfset mesej = "Emel itu tidak sah.">
<cfelseif len( kata ) lt 8>
    <cfset mesej = "Kata laluan mesti sekurang-kurangnya 8 aksara.">
<cfelse>
    <!--- garam, cincang, kemudian simpan --->
</cfif>

Tiga perkara dalam kod ini yang berbaloi diperhatikan. Pertama, emel ditukar kepada huruf kecil supaya Ali@Contoh.my dan ali@contoh.my dianggap sama. Kedua, panjang minimum kata laluan ialah lapan aksara, dan had itu lebih berguna daripada peraturan kerumitan yang menyusahkan orang. Ketiga, ungkapan yang menyemak bentuk emel itu sengaja ringkas. Pengesahan emel yang sempurna memerlukan penghantaran e-mel sebenar, dan itu perkara lain.

Log masuk: membandingkan kata laluan

Cincang tidak boleh diterbalikkan, jadi kata laluan yang tersimpan tidak boleh dibaca semula. Apa yang berlaku semasa log masuk ialah kata laluan yang ditaip dicincang semula, menggunakan garam dan putaran yang sama, kemudian dua cincang itu dibandingkan.

<cfquery name="qCari" datasource="biz">
    SELECT id, nama, kata_hash, garam, putaran
    FROM demo_pengguna
    WHERE emel = <cfqueryparam value="#emel#" cfsqltype="cf_sql_varchar" maxlength="120">
</cfquery>

<cfset cincangCuba = generatePBKDFKey( "PBKDF2WithHmacSHA256", kata, qCari.garam, qCari.putaran, 256 )>
<cfif cincangCuba eq qCari.kata_hash>
    <!--- kata laluan betul --->
</cfif>
Kalau cincang itu ditulis dalam huruf besar pada satu tempat dan huruf kecil pada tempat lain, perbandingan akan gagal walaupun kata laluannya betul. Ini masalah yang selalu berlaku apabila sistem dipindahkan antara pangkalan data. Simpan dan bandingkan dengan format yang sama sepanjang masa.

Ralat yang sama untuk dua keadaan berbeza

Ini satu baris yang kelihatan remeh, tetapi ia membezakan sistem yang faham keselamatan daripada yang tidak. Apabila log masuk gagal, mesejnya mestilah sama, sama ada emel itu tidak wujud atau kata laluannya salah.

<cfset mesej = "Emel atau kata laluan tidak sepadan.">

Sebabnya mudah. Kalau sistem memberitahu "emel itu tiada" apabila emel tidak dikenali, sesiapa sahaja boleh menggunakan borang awak untuk menyemak emel mana yang berdaftar. Ia dipanggil pendedahan maklumat melalui mesej ralat, dan ia langkah pertama dalam serangan yang menyasarkan akaun tertentu.

Sesi: bagaimana sistem mengingati pengguna

Selepas log masuk berjaya, sistem perlu mengingati pengguna itu antara satu halaman dengan halaman seterusnya. Mekanisme itu dipanggil sesi, dan dalam CFML ia disimpan dalam skop session.

<cfset session.penggunaId = qCari.id>
<cfset session.nama       = qCari.nama>
<cfset session.emel       = emel>

Sesi bermakna pelayan yang mengingati, bukan pelayar. Pelayar hanya menyimpan satu penanda kecil, dan penanda itulah yang menentukan sesi mana milik siapa. Ini jauh lebih selamat daripada menyimpan status log masuk dalam kuki yang boleh dibaca dan diubah oleh pengguna.

Ini perangkap yang paling mudah terlepas pandang, dan ia tidak menunjukkan sebarang ralat. Sesi memerlukan satu tetapan dalam Application.cfc, iaitu this.setClientCookies = true. Kalau ia false, enjin tidak menghantar penanda sesi, setiap permintaan menjadi sesi yang baharu, dan log masuk akan kelihatan berjaya tetapi terus lupa pengguna. Dengan false, sesi hilang pada permintaan berikutnya. Dengan true, ia kekal.

Keluar, dan menukar id sesi

Keluar bukan sekadar memadam pemboleh ubah. Ia perlu mengosongkan keseluruhan skop sesi, dan menukar id sesi itu sendiri.

<cfset structClear( session )>
<cfset sessionRotate()>
<cflocation url="/demo/log-masuk?ok=keluar" addtoken="no">

Kenapa perlu menukar id sesi. Bayangkan seseorang berjaya menghantar satu pautan yang mengandungi id sesi awak kepada awak, dan awak membukanya. Kalau id sesi tidak pernah berubah selepas log masuk, penyerang itu kini memegang id yang sah. Amalan yang betul ialah menukar id sesi sebaik sahaja identiti pengguna berubah, iaitu semasa log masuk dan semasa keluar. Fungsi sessionRotate itu yang melakukannya.

Melambatkan percubaan meneka

Kata laluan yang kukuh tidak berguna kalau seseorang boleh mencuba berjuta-juta kombinasi melalui borang. Jadi borang log masuk perlu melambatkan percubaan itu.

<cfset MAKS_CUBAAN = 5>
<cfset SEKAT_MINIT = 15>

<!--- selepas kata laluan salah --->
<cfset cubaanBaru = qCari.cubaan + 1>
<cfif cubaanBaru gte MAKS_CUBAAN>
    <!--- set sekat_sehingga = sekarang + 15 minit --->
</cfif>

Dalam kod ini, lima percubaan salah akan menyekat akaun itu selama lima belas minit. Sepanjang tempoh itu, kata laluan yang betul pun tetap ditolak. Itulah maksudnya: sekatan tidak boleh dipintas dengan meneka sekali lagi. Perhatikan satu perkara penting, iaitu had itu dikira pada akaun, bukan hanya pada alamat IP, kerana penyerang boleh menukar alamat IP dengan mudah.

Empat perkara lain yang selalu dilupakan

  • Kodkan setiap nilai sebelum dipaparkan. Nama pengguna boleh mengandungi kod, dan kod yang dipaparkan mentah boleh dijalankan oleh pelayar orang lain.
  • Jangan catat kata laluan dalam fail log. Ia kedengaran jelas, tetapi ia berlaku kerana orang mencatat keseluruhan borang semasa menyiasat masalah.
  • Gunakan HTTPS. Tanpa ia, kata laluan dihantar dalam bentuk yang boleh dibaca di tengah jalan, dan semua kerja cincang tadi menjadi tidak berguna.
  • Kuki sesi perlu ditanda Secure supaya ia tidak dihantar melalui sambungan tidak selamat, HttpOnly supaya kod dalam halaman tidak boleh membacanya, dan SameSite supaya ia tidak dihantar dari laman lain tanpa kebenaran awak.

Apa yang perlu awak uji pada borang awak sendiri

Setiap perkara di atas diuji melalui alamat awam, bukan dibaca daripada kod. Ujian itu mencipta akaun, mencuba kata laluan salah, log masuk dengan betul, keluar, menyekat akaun selepas lima percubaan, dan menghantar teks suntikan SQL dalam medan emel. Semuanya lulus, dan kata laluan tidak pernah muncul dalam jadual.

Empat pemeriksaan yang berbaloi dilakukan ke atas borang awak sendiri selepas ia siap. Pertama, pastikan kata laluan tidak muncul pada mana-mana medan dalam jadual pengguna. Kedua, pastikan emel yang salah dan kata laluan yang salah memberi mesej yang sama. Ketiga, cuba lima kali dengan kata laluan salah, kemudian pastikan walaupun kata laluan betul ditolak. Keempat, selepas keluar, tekan butang kembali pada pelayar dan pastikan halaman ahli tidak muncul semula. Hantar POST tanpa mengikut lencongan, kemudian minta halaman lencongan itu secara berasingan.

Cuba sendiri

Apa yang perlu ditambah sebelum ia digunakan oleh orang sebenar

Kod dalam tutorial ini lengkap untuk belajar, dan ia selamat dalam perkara-perkara asas. Tetapi ia belum menjadi sistem akaun yang lengkap, dan perbezaannya berbaloi difahami supaya awak tidak menyangka ia sudah siap.

  • Lupa kata laluan. Setiap sistem sebenar memerlukannya. Ia mesti melalui e-mel, dan pautan pemulihan itu mesti luput dalam masa yang singkat serta hanya boleh digunakan sekali.
  • Pengesahan emel. Tanpa ia, sesiapa sahaja boleh mendaftar menggunakan emel orang lain, dan orang itu tidak pernah tahu.
  • Catatan audit. Sistem sebenar perlu merekod siapa masuk, bila, dan dari mana, supaya kejadian luar biasa boleh disiasat kemudian.
  • Log keluar automatik. Sesi dalam kod ini tamat selepas 30 minit tanpa aktiviti. Untuk sistem yang menyimpan data sensitif, tempoh itu perlu dipendekkan, dan pengguna perlu dimaklumkan sebelum ia tamat.
  • Pengesahan dua langkah. Ia menambah satu langkah, dan ia menghalang sebahagian besar serangan yang menggunakan kata laluan yang dicuri.
  • Semakan kata laluan yang pernah bocor. Kalau kata laluan itu muncul dalam senarai kebocoran awam, sistem boleh menolaknya semasa pendaftaran.

Susunan yang dicadangkan ialah tambah lupa kata laluan dan pengesahan e-mel dahulu, kerana kedua-duanya berkaitan dengan kepercayaan pengguna. Catatan audit datang selepas itu, kerana ia lebih berguna apabila sudah ada sesuatu yang perlu disiasat.

Halaman contoh itu boleh dicuba terus, dan kod penuhnya boleh dimuat turun bersama skrip jadual dan arahan pemasangan. Kalau awak mahu mengikuti urutan yang sama seperti siri ini, mulakan dengan tutorial CRUD dahulu, kerana log masuk ialah lapisan di atas corak yang sama.

Baca juga di laman ini

Sumber yang disemak

Maklumat dalam tulisan ini disemak dengan sumber di bawah. Bila keadaan berubah (harga, waktu, terma), sumber inilah yang paling tepat — bukan halaman ini.

Dapat tulisan baharu terus ke e-mel

Sekali sekala sahaja: panduan web, berita AI, dan tempat menarik di Kelantan. Tiada iklan, dan e-mel encik tidak dikongsi dengan sesiapa.

Berhenti langgan bila-bila masa, satu klik.

Kongsi WhatsApp Facebook

Berkaitan halaman ini

Pautan afiliasi Shopee — saya mungkin menerima komisen, harga awak tetap sama.

Senarai penuh ada di kedai afiliasi.

Rujukan yang mungkin berguna

Pautan rujukan — anda dapat ganjaran pendaftaran, saya juga dapat ganjaran. Tiada kos tambahan untuk anda.

Bank

GXBank — FlexiCredit — Had kredit peribadi sehingga RM150,000

GXBank ialah bank digital di Malaysia yang beroperasi sepenuhnya dalam aplikasi. FlexiCredit memberi had kredit peribadi sehingga RM150,000 yang boleh dikeluarkan serta-merta ke akaun GX apabila diperlukan. Memohon tidak dikenakan caj.

Ganjaran: Sehingga RM100 untuk anda selepas permohonan FlexiCredit diluluskan dan penarikan dibuat (tertakluk syarat GXBank)

Afiliasi

Shopee Affiliate — Jana komisen dengan berkongsi pautan produk Shopee

Program afiliasi rasmi Shopee. Kongsi pautan produk, dan anda menerima komisen bagi setiap jualan yang berlaku melalui pautan anda. Daftar melalui pautan ini dan masukkan kod rujukan 7FZB2E9 — saya menerima ganjaran rujukan, dan anda tidak dikenakan sebarang kos tambahan.

Ganjaran: Komisen bagi setiap jualan melalui pautan anda (kadar ditetapkan Shopee mengikut kategori produk)

Senarai penuh (bank digital, minyak, penghantaran) ada di halaman rujukan. Maklumat di atas ialah ringkasan terma penyedia — sila semak terma penuh di laman rasmi mereka.