0

Xây Dựng Luồng Login & Những "Cú Lừa" Mà Newbie Thường Mắc Phải

Chào anh em, lại là mình đây! 👋

Trong hành trình làm nghề, có lẽ "Login/Register" là một trong những task đầu tiên mà bất kỳ anh em sinh viên hay fresher nào cũng được giao khi bắt đầu làm web. Nhìn lướt qua thì có vẻ dễ: "Chỉ là nhận username/password, query vào database, đúng thì cho vào , sai thì đuổi ra thôi mà?".

Nhưng sự thật là, tính năng đăng nhập chính là cánh cổng bảo vệ toàn bộ dữ liệu của hệ thống. Làm chạy được thì dễ, nhưng làm sao cho "chuẩn" và "an toàn" thì lại là một câu chuyện hoàn toàn khác. Hôm nay, mình sẽ tổng hợp lại những kiến thức cốt lõi nhất về luồng Login ở Backend, đồng thời chia sẻ những "cái bẫy" xương máu mà mình thấy rất nhiều ở bạn mới (thâm chí cả các bạn đã đi làm 1-2 năm) vẫn hay dẫm phải nhé.

1. Luồng Login Cơ Bản Hoạt Động Như Thế Nào?

Về bản chất, khi user đăng nhập, chúng ta đang thực hiện quá trình Authentication (xác thực) - Tức là trả lời câu hỏi: "Anh là ai? Anh có gì chứng minh được anh là người đó không?".

Dưới đây là luồng đi cơ bản nhất:

  1. Client (Browser/App): Gửi thông tin (Email/Username + Password) lên Server.
  2. Server: Nhận dữ liệu, tìm user trong Database dựa vào Email/Username.
  3. Server: So sánh Password gửi lên với Password lưu trong Database.
  4. Server: Nếu đúng, tạo ra một "Chiếc vé" (Session hoặc Token) và gửi về Client.
  5. Client: Cất "chiếc vé" này đi. Từ về sau, mỗi lần API lấy dữ liệu, Client sẽ kẹp chiếc vé này vào request để Server biết đây là ai mà không cần bắt đăng nhập lại

2. Hai Trường Phái Cấp "Vé": Session/Cookie và JWT

Khi xác thực thành công, Server phải có cách nào đó để "nhớ" user. Hiện này có 2 cách phổ biến nhất:

  • Session / Cookie (Kiểu truyền thống): Server tạo một Session (phiên làm việc) lưu trong RAM hoặc Redis, sau đó trả về cho Browser một cái ID (Session ID) lưu vào Cookie. Mỗi lần Browser gửi request, nó tự động đính kèm Cookie này. Server cầm ID tra cứu lại xem thuộc về user nào.

    • Ưu điểm: Dễ quản lý, có thể "đá" (kick) user ra khỏi hệ thống ngay lập tức (thu hồi Session).
    • Nhược điểm: Tốn bộ nhớ Server nếu có hàng triệu user.
  • JWT - JSON Web Token (Kiểu hiện đại, Stateless); Server không lưu gì cả! Server dùng một SECRET_KEY để mã hóa thông tin user thành một chuỗi Token (JWT) và gửi cho Client. Lần sau Client gửi Token lên, Server dùng chính SECRET_KEY đó để giải mã và kiểm tra tính hợp lệ.

    • Ưu điểm: Scale server thoải mái (vì server không cần lưu trạng thái).
    • Nhược điểm: Khó thu hồi token trước khi nó hết hạn

3. Những "Cái Bẫy" Chết Người Khi Làm Login (Kinh Nghiệm Thực Chiến)

Đây mới là phần quan trọng nhất bài viết. Đọc kỹ nhé anh em, dính một lỗi thôi là bay luôn cả hệ thống đấy!

Bẫy #1: Lưu mật khẩu dưới dạng "Plain Text" hoặc dùng MD5

Nhiều bạn sinh viên lưu thẳng mật khẩu 123456 vào Database. Số khác "có tâm" hơn thì hash (băm) bằng MD5 hoặc SHA1.

Sự thật phũ phàng: Hacker chỉ cần 1 giây để tra ngược mã MD5 của 123456 trên Google. Nếu Database bị lộ, toàn bộ tài khoản của user sẽ "bay màu".

Cách giải quyết: Bắt buộc phải dùng các thuật toán băm mật khẩu chuyên dụng như Bcrypt hoặc Argon2. Các thuật toán này tự động sinh ra một chuỗi ngẫu nhiên (gọi là Salt) cộng dồn vào mật khẩu trước khi băm, giúp chống lại các cuộc tấn công bằng từ điển (Rainbow table).

// Ví dụ dùng thư viện bcrypt trong Node.js
const bcrypt = require('bcrypt');
const saltRounds = 10;

// Khi Đăng ký:
const hashedPassword = await bcrypt.hash(plainPassword, saltRounds);

// Khi Đăng nhập:
const isMatch = await bcrypt.compare(plainPassword, hashedPassword);

Bẫy #2: Báo lỗi quá chi tiết cho người dùng

Code của newbie: Nếu không tìm thấy email, báo lỗi "Email không tồn tại". Nếu sai mật khẩu, báo lỗi "Sai mật khẩu".

Hậu quả: Hacker có thể dùng tool tự động dò hàng loạt email để xem email nào đã đăng ký trong hệ thống của bạn (User Enumeration Attack).

Cách giải quyết: Hãy luôn dùng một thông báo chung chung: "Tài khoản hoặc mật khẩu không chính xác."

Bẫy #3: Không chặn số lần đăng nhập sai (No Rate Limiting)

Hacker viết một script thử đăng nhập vào tài khoản Admin của bạn 10,000 lần một phút với 10,000 mật khẩu khác nhau (Brute-force attack). Nếu API của bạn không chặn lại, sớm muộn gì chúng cũng mò ra.

Cách giải quyết: Implement Rate Limit. Ví dụ: Nếu một IP hoặc một Email đăng nhập sai quá 5 lần trong 5 phút -> Khóa tạm thời 15 phút hoặc bắt giải mã Captcha.

Bẫy #4: Lưu JWT vào LocalStorage một cách "vô tư"

Khi dùng JWT cho SPA (React/Vue), 90% các bài tutorial trên mạng dạy bạn lưu token vào LocalStorage.

Vấn đề: LocalStorage rất dễ bị đánh cắp nếu trang web của bạn dính lỗi XSS (Hacker chèn được script độc hại vào web). Script này có thể đọc LocalStorage và gửi Token của bạn về server của hacker. Thực chiến: Ở các hệ thống lớn, Token thường được lưu trong HttpOnly Cookie. Với tùy chọn HttpOnly, Javascript ở trình duyệt sẽ không thể đọc được Cookie này, loại bỏ hoàn toàn rủi ro bị trộm token qua XSS.

4. Lời Kết

Chức năng Login nhìn thì nhỏ gọn, nhưng nó đòi hỏi tư duy về bảo mật rất lớn. Là một Developer, dù bạn đang làm project môn học hay làm sản phẩm thực tế cho hàng triệu user, hãy luôn đặt tư duy bảo mật lên hàng đầu.

Hy vọng bài viết này sẽ giúp các bạn sinh viên và newbie có cái nhìn rõ ràng hơn về hệ thống Login. Đừng ngần ngại comment trao đổi ở dưới nhé! Chúc anh em code ít bug! 🚀


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí