Chào mọi người, Zeno quay trở lại rồi đây nghen! ☕✨
Sau bài mở bát về Injection, hôm nay tụi mình sẽ tiếp tục hành trình “soi” bảng phong thần với cái tên nằm ở vị trí thứ 4 trong bảng xếp hạng OWASP Top 10: A04:2025 - Cryptographic Failures (Lỗi Mật mã & Bảo mật Dữ liệu).
Nói tới “Cryptographic Failures” hay “Mật mã học”, nhiều anh em hay bị ngợp vì tưởng phải giải ma trận toán học hay bẻ khóa dữ thần như trong phim Hollywood. Nhưng sự thật nghiệt ngã ở thực tế là: Hacker hiếm khi bẻ gãy được các thuật toán mã hóa hiện đại, mà phần lớn hệ thống “bay màu” là do anh em dev… xài sai cách! :v
Giống như việc bạn mua một chiếc ổ khóa bằng thép nguyên khối siêu cứng chống cắt, nhưng lại lấy dây chun buộc nó vào cửa, hoặc tiện tay giấu luôn chìa khóa dưới thảm lau chân vậy đó. Chèng ơi!
Thôi, cùng Zeno xắn tay áo lên làm tới tới, bóc tách xem lỗi này sinh ra từ đâu và vá bằng Golang thế nào cho chuẩn bài nghen!
1. Bản chất của Cryptographic Failures là gì?
Trước đây trong bảng OWASP Top 10 (2017), lỗ hổng này mang tên là Sensitive Data Exposure (Rò rỉ dữ liệu nhạy cảm). Tuy nhiên, từ phiên bản 2021 đến 2025, OWASP đã đổi tên thành Cryptographic Failures để chỉ đích danh nguyên nhân gốc rễ: Sự thất bại hoặc sai sót trong quá trình áp dụng mật mã học (Cryptography) để bảo vệ dữ liệu.
Định nghĩa chuẩn bài: Cryptographic Failures xảy ra khi hệ thống thiếu mã hóa, mã hóa bằng thuật toán yếu/lỗi thời, hoặc quản lý chìa khóa mật mã kém cẩn thận, khiến dữ liệu nhạy cảm bị lộ lọt hoặc bị can thiệp.
Các mã lỗi CWE liên quan phổ biến theo tiêu chuẩn OWASP:
- CWE-327: Sử dụng thuật toán mã hóa bị hỏng hoặc có rủi ro cao (Broken or Risky Cryptographic Algorithm).
- CWE-331: Độ ngẫu nhiên/Hỗn loạn không đủ lớn (Insufficient Entropy).
- CWE-338: Sử dụng bộ sinh số giả ngẫu nhiên yếu về mặt mật mã (Weak PRNG).
- CWE-326: Độ dài khóa mã hóa không đủ mạnh (Inadequate Encryption Strength).
Dữ liệu nhạy cảm cần bảo vệ bao gồm:
- Mật khẩu người dùng, token phiên làm việc (Session Tokens, JWT).
- Thông tin cá nhân (PII): Số CCCD, số điện thoại, địa chỉ, hồ sơ y tế.
- Thông tin tài chính: Số thẻ tín dụng (PAN), CVV, tài khoản ngân hàng.
- Bí mật hệ thống: Secret Keys, API Keys, Database Passwords.
Nếu các dữ liệu này không được bảo vệ đúng cách cả khi truyền đi trên mạng (In-Transit) lẫn khi lưu trữ trong Database (At-Rest), hệ thống của bạn coi như “mở cửa đón trộm” rồi nghen!
2. Phân biệt nhập môn: Hashing (Băm) vs Encryption (Mã hóa)
Trước khi đi sâu vào bug, Zeno phải “clear” ngay cái khái niệm này vì rất nhiều bạn vẫn hồn nhiên bảo: “Dự án em mã hóa mật khẩu bằng SHA256” :D
Hãy nhớ thần chú này của Zeno nha:
- Mã hóa (Encryption) là đường 2 chiều: Bạn khóa lại được thì phải mở ra được (cần có Khóa/Key).
- Mục đích: Giấu dữ liệu để truyền qua mạng hoặc lưu trữ, nhưng sau này hệ thống vẫn cần giải mã ra để đọc lại (Ví dụ: Số thẻ tín dụng, CCCD, Email).
- Thuật toán phổ biến: AES-256-GCM (Đối xứng - Symmetric), RSA / ECC (Bất đối xứng - Asymmetric).
- Băm dữ liệu (Hashing) là đường 1 chiều (One-Way Function): Đã băm thành chả rồi thì không cách nào ráp lại thành con lợn được!
- Mục đích: Kiểm tra tính toàn vẹn (Integrity) hoặc lưu mật khẩu. Khi người dùng đăng nhập, ta băm pass nhập vào rồi so sánh với chuỗi băm trong DB.
- Thuật toán băm mật khẩu chuẩn (Key Stretching): Argon2id, Bcrypt, PBKDF2.
| Tiêu chí | Hashing (Băm dữ liệu) | Encryption (Mã hóa) |
|---|---|---|
| Tính thuận nghịch | 1 chiều (Không giải mã ngược lại được) | 2 chiều (Giải mã được nếu có Khóa đúng) |
| Độ dài kết quả | Cố định (Dù băm 1 chữ hay 1 cuốn sách) | Biến đổi theo độ dài dữ liệu gốc |
| Yếu tố đi kèm | Cần thêm Salt (chuỗi ngẫu nhiên chống Rainbow Table) | Cần Key (Khóa) & IV/Nonce |
| Ứng dụng chính | Lưu mật khẩu, verify checksum file | Mã hóa kết nối HTTPS, lưu số thẻ tín dụng |
3. 4 “Bẫy Tử Thần” gây ra Cryptographic Failures
Lỗi mã hóa thường đến từ 4 kịch bản “sập hầm” kinh điển sau:
3.1. Dữ liệu truyền đi không mã hóa (In-Transit)
Dùng HTTP thay vì HTTPS, hoặc SMTP/FTP truyền rõ Plaintext. Hacker đứng cùng mạng Wifi công cộng (như ở quán cà phê) xài Wireshark bắt gói tin một cái là thấy sạch sẽ Username/Password luôn. Mèng ơi!
3.2. Dùng thuật toán lỗi thời hoặc dùng sai mục đích
- Dùng MD5 hay SHA-1 để băm mật khẩu: Mấy cái này giờ các trang giải mã online giải trong 0.001 giây bằng Rainbow Table.
- Dùng SHA-256 để lưu mật khẩu: SHA-256 là Cryptographic Hash Function được thiết kế để tính toán cực nhanh. Hacker dùng Card màn hình (GPU) có thể thử hàng tỷ pass/giây để Brute-force. Mật khẩu phải xài thuật toán băm chậm cố ý (Password Hashing Algorithm) như Bcrypt hay Argon2id!
3.3. Khóa mật mã (Key Management) kém an toàn
Khóa rất an toàn nhưng lại hardcode thẳng vào mã nguồn: const secretKey = "my-super-secret-123". Xong vô tình bấm git push lên GitHub công khai. Thế là xong phim!
3.4. Dùng bộ sinh số ngẫu nhiên yếu (Weak PRNG)
Lấy hàm Math.random() (JS) hay math/rand (Go) để tạo Token reset mật khẩu hoặc JWT secret. Mấy hàm này là Pseudo-Random Number Generator (PRNG), tạo số ngẫu nhiên theo công thức toán học nội tại, hacker hoàn toàn đoán được chuỗi ngẫu nhiên tiếp theo!
4. Thực hành Chống Lỗi Mật Mã bằng Golang!
Golang hỗ trợ gói crypto trong thư viện chuẩn rất ngon lành, nhưng nếu dùng nhầm API thì vẫn “ăn hành” như thường nghen.
4.1. Case 1: Mã hóa dữ liệu At-Rest (AES-ECB vs AES-GCM)
[Cảnh báo] Code lỗi “toang nặng” (CWE-327): Lỗi này vừa hardcode Key, vừa tự viết vòng lặp mã hóa dạng ECB (Electronic Codebook). Chế độ ECB mã hóa từng khối 16-byte độc lập mà không có IV/Nonce. Các khối dữ liệu giống nhau sẽ ra ciphertext giống hệt nhau, làm lộ cấu trúc dữ liệu!
| |
[Khuyến nghị] Code chuẩn bài với AES-256-GCM (AEAD):
Dùng chế độ AES-GCM (Galois/Counter Mode) - một thuật toán AEAD (Authenticated Encryption with Associated Data). Nó vừa mã hóa bảo mật vừa tạo Authentication Tag chống sửa đổi dữ liệu. Key đọc từ biến môi trường (os.Getenv).
| |
4.2. Case 2: Sinh Token bảo mật (math/rand vs crypto/rand)
Anh em hay có thói quen tạo Token khôi phục mật khẩu bằng math/rand. Cái này là “biếu” tài khoản cho hacker luôn đó!
[Cảnh báo] Code dùng PRNG yếu (CWE-338):
| |
[Khuyến nghị] Code chuẩn 100 điểm với CSPRNG:
Sử dụng gói crypto/rand. Gói này gọi trực tiếp bộ sinh số ngẫu nhiên an toàn mật mã của Hệ điều hành (Cryptographically Secure PRNG - CSPRNG), hacker bó tay toàn tập!
| |
4.3. Case 3: Băm mật khẩu chuẩn bài bằng Bcrypt
Tuyệt đối không băm pass bằng SHA256 hay MD5 nha! Trong Go, chuẩn nhất là dùng thư viện golang.org/x/crypto/bcrypt (nó tự động nêm thêm Salt (muối) ngẫu nhiên vào mật khẩu — thấy chưa, đôi khi cuộc sống hay code mặn mà một chút lại tốt hơn hẳn! :v).
| |
4.4. Case 4: Cấu hình Server ép TLS 1.3 cứng cựa
Đừng để server Golang của bạn chấp nhận kết nối từ các giao thức cũ kỹ dễ dính lỗi như TLS 1.0 hay 1.1 nghen.
| |
5. Cạm Bẫy Bảo Mật JWT trong Hệ Thống Backend
JWT (JSON Web Token) sử dụng mật mã học để ký số và xác thực, nhưng nếu cấu hình sai thì rất dễ dính chấu!
5.1. Lỗi thuật toán ký số: HS256 vs RS256
- HS256 (HMAC-SHA256 Đối xứng): Dùng cùng 1 Secret Key để cả Auth Service ký token và các Service khác verify token. Nếu một microservice bất kỳ lộ Secret Key, hacker có thể tự tạo ra JWT hợp lệ với quyền Admin luôn!
- RS256 (RSA Signature Bất đối xứng): Auth Service dùng Private Key để ký, còn các service phụ chỉ cần giữ Public Key để kiểm tra signature. Dù service phụ bị hack cũng không ai lấy được Private Key để giả mạo token.
5.2. Lỗi đổi thuật toán “alg”: “none” (CWE-347)
Nhiều thư viện JWT cũ ngây thơ chấp nhận token có header {"alg": "none"} (không dùng chữ ký). Hacker chỉ cần sửa user_id thành Admin rồi bỏ phần chữ ký đi là nghiễm nhiên vượt qua khâu đăng nhập!
Lời khuyên của Zeno: Luôn chỉ định rõ thuật toán cho phép (Whitelist) khi parse JWT trong Go:
1 2 3 4 5 6 7token, err := jwt.Parse(tokenString, func(token *jwt.Token) (any, error) { // Ép bắt buộc phải là RS256! if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok { return nil, fmt.Errorf("thuật toán không hợp lệ nha: %v", token.Header["alg"]) } return verifyKey, nil })
6. Tự Động Hóa Quét Secret
Làm dev thì ai cũng có lúc bất cẩn. Cách tốt nhất để không bị rò rỉ Key là ép máy móc tự gác cổng!
Anh em nên cài đặt công cụ như gitleaks hoặc trufflehog vào pre-commit hook hoặc CI/CD pipeline để nó tự động chặn nếu lỡ tay commit file .env hay AWS Key nhé:
| |
Tóm lại
Bảo mật mật mã nghe thì cao siêu nhưng nguyên tắc vàng đầu tiên của dân IT chính là: “ĐỪNG TỰ PHÁT MINH LẠI BÁNH XE” (Don’t roll your own crypto)!
- Luôn dùng thư viện chuẩn (
crypto/rand,bcrypt,AES-GCM). - Giấu Secret Key vào Vault / Env Variables.
- Ép HTTPS & TLS 1.3 trên mọi kết nối.
Chúc anh em code an toàn, không bao giờ bị “thủng lưới mật mã” nghen! Hẹn gặp lại ở các bài OWASP tiếp theo! :D