Câu 1Khi server lưu mật khẩu của người dùng, thứ được đặt trong cơ sở dữ liệu là gì?
Cách chức năng đăng nhập hoạt động — Xác thực, session và Cookie
Bài viết này nằm trong khóa Kiến thức nền tảng về CNTT, xây dựng từ đầu những kiến thức CNTT thực tế tối thiểu mà bạn cần để lập trình và vibe coding.
Chức năng đăng nhập gồm xác thực để xác nhận đúng là người đó, và session để sau đó server vẫn nhận ra cùng một người. Các sơ đồ cho thấy hash và Cookie mỗi thứ đảm nhận việc gì.
Bài viết này nói về cách chức năng đăng nhập hoạt động.
Chức năng đăng nhập gồm hai cơ chế: một cơ chế xác nhận người dùng đúng là người họ khai, và một cơ chế tiếp tục nhận ra chính người đó về sau.
"Băm (hash)", "phương thức session" và "JWT" là cách gọi cùng một chức năng đăng nhập ở từng tình huống khác nhau.
Nhánh trên là bước kiểm tra chỉ làm một lần, nhánh dưới là bước kiểm tra làm ở mọi request sau đó.
Xác thực là xác nhận bạn là ai, phân quyền là quyết định bạn được làm gì
Xác thực (authentication: xác nhận người dùng đúng là người họ khai) là phán đoán được thực hiện lúc đăng nhập.
Phân quyền (authorization: quyết định người dùng đã được xác nhận thì được làm gì) là một phán đoán khác, diễn ra sau đó.
- Căn cứ — ID và mật khẩu; sau khi đăng nhập là session ID trong Cookie
- Khi không qua — đưa về màn hình đăng nhập
- Mật khẩu chỉ được kiểm tra một lần, sau đó là session ID
- Căn cứ — ai là chủ của đặt chỗ, người dùng có phải quản trị viên không
- Khi không qua — trả về "bạn không có quyền"
- Kiểm tra lại mỗi lần thao tác
Dù cùng là thao tác của tanaka, hai phán đoán vẫn được quyết định riêng.
Có hàng dừng ở xác thực, có hàng qua xác thực rồi dừng ở phân quyền.
Các thiết lập kiểu "chỉ quản trị viên dùng được", "chỉ xem được dữ liệu của mình" chính là phân quyền.
Xác thực là xác nhận bạn là ai, phân quyền là quyết định bạn được làm gì
Việc đăng nhập được và việc được phép thực hiện thao tác là hai điều được quyết định riêng.
Xác nhận người dùng có đúng là người họ khai là xác thực, quyết định người đó có được thực hiện thao tác đó không là phân quyền, và phân quyền được server kiểm tra lại mỗi lần thao tác.
Lưu mật khẩu dưới dạng hash, rồi so hash với hash
Điều đầu tiên cần nắm về xác thực là server không lưu chính mật khẩu.
Thứ được lưu là hash (hash: chuỗi ký tự tạo ra từ chuỗi gốc theo một quy trình cố định và không khôi phục lại được chuỗi gốc).
| Ký tự bạn nhập | Băm ngay tại chỗ | So với hash đã lưu | Kết quả |
|---|---|---|---|
| hanabi2026 (đúng) | a3f9...c1 | Giống a3f9...c1 | Đăng nhập thành công |
| hanabi2025 (khác 1 ký tự) | 7b20...e4 | Khác a3f9...c1 | Đăng nhập thất bại |
| Hanabi2026 (khác chữ hoa) | d15c...8a | Khác a3f9...c1 | Đăng nhập thất bại |
Chỉ khác một ký tự thôi thì hash tạo ra đã hoàn toàn khác.
Việc kiểm tra vẫn làm được dù hash không khôi phục lại được, vì thứ đem ra so cũng là hash.
Thứ server nắm giữ chỉ là một chuỗi không khôi phục được.
- email — tanaka@example.com
- password_hash — a3f9...c1
- created_at — ngày giờ tài khoản được tạo
- Chuỗi ký tự đã nhập hanabi2026
- Dữ liệu ở dạng khôi phục lại được ký tự đã nhập
- Trạng thái đang đăng nhập hay không
Mật khẩu được lưu dưới dạng hash để phòng khi nội dung cơ sở dữ liệu bị lộ.
Những chuỗi hay được dùng thì đoán ra được, nên trước khi băm sẽ thêm vào một chuỗi khác nhau theo từng người dùng (salt).
HTTPS lúc gửi là biện pháp để dữ liệu không bị đọc trên đường đi, khác với chuyện lưu ở dạng nào.
Thứ được lưu là hash, thứ đem so cũng là hash
Server không giữ mật khẩu mà người dùng đã nhập.
Thứ server giữ chỉ là một chuỗi không khôi phục được, và mỗi lần đăng nhập nó đưa ký tự vừa nhập qua đúng quy trình đó rồi xem chuỗi tạo ra có giống chuỗi đã lưu hay không.
Sau khi đăng nhập, server biết vẫn là cùng một người nhờ session ID trong Cookie
Dù đăng nhập thành công, request mở trang tiếp theo vẫn là một request khác.
HTTP là stateless (một lần xử lý không nhớ lần xử lý trước), nên server không giữ thông tin ai vừa đăng nhập lúc nãy.
Việc đăng nhập đã qua được ghi lại ở phía server, và trình duyệt được giao một chuỗi trỏ tới bản ghi đó.
Bản ghi để lại ở phía server là session, chuỗi trỏ tới nó là session ID, còn cơ chế giao chuỗi đó cho trình duyệt và bắt gửi kèm mỗi lần là Cookie.
- Cookie: session_id=3f9a1c...e81b
- Tự động gắn kèm mọi request tới cùng site
- Ngoài chuỗi này ra thì không giữ gì khác
- Bản ghi session 3f9a1c...e81b là tanaka
- Bản ghi này có thời hạn hiệu lực
- Đăng xuất thì bản ghi này bị xóa
Phía trình duyệt chỉ có chuỗi 3f9a1c...e81b, không chứa tên cũng không chứa quyền.
Đó là đăng nhập của ai thì chỉ biết được khi chuỗi đó được ghép với bản ghi ở phía server.
Chỉ hàng trên được ghép với bản ghi, nên danh sách đặt chỗ trả về mà không phải hỏi lại mật khẩu.
Việc trao nhận này diễn ra dưới dạng hai dòng sau.
# Dòng server trả về khi đăng nhập thành công
Set-Cookie: session_id=3f9a1c...e81b; HttpOnly; Secure
# Từ đó về sau, dòng được gắn vào mọi request trình duyệt gửi tới cùng site
Cookie: session_id=3f9a1c...e81b
Hai mục phía sau Set-Cookie là chỉ thị cho trình duyệt.
HttpOnly khiến JavaScript trên trang không đọc được Cookie này, còn Secure chỉ cho gửi khi dùng HTTPS.
Nội dung Cookie có thể bị người dùng sửa, nên tin ngay nội dung đó hay không sẽ cho kết quả khác nhau.
Cookie được lưu trong trình duyệt, nên người dùng có thể sửa nội dung rồi gửi đi.
Trong Cookie chỉ đặt session ID, còn phán đoán thì dựa vào bản ghi ở phía server.
Trong khung bên trái chỉ đặt một chuỗi không mang ý nghĩa gì.
Mọi căn cứ để quyết định đó là ai và được làm gì đều nằm trong khung bên phải.
Trình duyệt chỉ được giao một chuỗi trỏ tới bản ghi
Ngay lúc request đi tới, server không biết đó là request của ai.
Server biết được là nhờ bản ghi tạo lúc đăng nhập và chuỗi trình duyệt gửi mỗi lần được ghép với nhau, còn nếu bản ghi bị xóa hoặc hết hạn thì dù gửi cùng chuỗi đó bạn vẫn quay về màn hình đăng nhập.
Khác nhau giữa phương thức session và JWT — server giữ bản ghi, hay chuỗi tự giữ
"Phương thức session hay JWT" là lựa chọn xem bên nào giữ bằng chứng đã đăng nhập.
Cách còn lại là không để bản ghi trên server mà giao chính bằng chứng đó cho trình duyệt.
Chuỗi bằng chứng được gắn kèm và gửi mỗi lần là token, và JWT (JSON Web Token: chuỗi gói chung ID người dùng, thời hạn hiệu lực và chữ ký) là định dạng tiêu biểu.
- ID người dùng của tanaka
- Thời hạn hiệu lực (dùng được tới thời điểm này)
- Chuỗi tính ra từ nội dung
- Sửa nội dung thì phép tính không còn khớp
Nhờ có chữ ký, server biết chuỗi nhận được là do chính mình phát hành mà không cần đối chiếu với bản ghi.
| Khía cạnh | Phương thức session | JWT |
|---|---|---|
| Thứ được giao khi đăng nhập thành công | session_id=3f9a1c...e81b | eyJhbGci... ID, thời hạn và chữ ký |
| Nơi đặt căn cứ để phán đoán | Bản ghi bên trong server | Nội dung bên trong chuỗi |
| Cách kiểm tra mỗi lần | Đối chiếu với bản ghi | Xác minh chữ ký |
JWT tự mang nội dung nên được chấp nhận mà không cần bản ghi trên server.
Với phương thức session, xóa bản ghi là request tiếp theo không qua được nữa.
JWT qua được cho tới khi hết hạn, nên khi muốn chặn ngay thì bạn giữ riêng một danh sách các token đã thu hồi.
Bài này vẽ phương thức session dưới dạng Cookie và JWT dưới dạng chuỗi, nhưng thực tế cũng có cấu hình đặt JWT vào trong Cookie.
Dù chỗ đặt có đổi, khác biệt vẫn nằm ở chỗ đối chiếu với bản ghi hay kiểm tra chữ ký.
Server giữ bản ghi, hay chuỗi tự giữ luôn nội dung
Ở cả hai phương thức, việc trình duyệt gắn kèm và gửi một chuỗi mỗi lần là giống nhau.
Khác nhau là server làm gì khi nhận được: phương thức session đi tra bản ghi của chính nó, còn JWT kiểm tra chữ ký gắn trên chuỗi.
Kiểm tra kiến thức
Hãy trả lời từng câu hỏi một.
Câu 2Khi bạn mở một trang khác sau khi đăng nhập, vì sao server biết vẫn là cùng người dùng đó?
Câu 3Một người dùng đã đăng nhập định xóa đặt chỗ của người khác và bị server từ chối. Đây là phán đoán nào?