Bảo mật ứng dụng web cho lập trình viên: tấn công và phòng thủ đo thật
Loạt bài phòng thủ, giáo dục về bảo mật ứng dụng web cho lập trình viên backend: tái hiện các lỗ hổng phổ biến (SQL injection, XSS, CSRF, timing attack...) trong môi trường lab an toàn rồi chỉ cách phòng đúng. Mỗi bài demo THẬT bằng Go/PostgreSQL trong container, đo hành vi thật, KHÔNG bịa.
12/12 phần đã đăng
Lập trình
1
SQL injection: vì sao nối chuỗi làm lộ cả tài khoản admin, và prepared statement chặn triệt để
SQL injection vẫn nằm trong OWASP Top 10 sau hai thập kỷ, vì gốc rễ của nó đơn giản mà dễ mắc: trộn lẫn code và data. Bài này tái hiện thật trong lab PostgreSQL cô lập: một query đăng nhập nối chuỗi bị input ' OR '1'='1 biến điều kiện thành luôn-đúng, trả về 2 dòng (bypass đăng nhập, lộ cả admin); cùng input đó qua prepared statement trả 0 dòng — bị coi là chuỗi mật khẩu, không bao giờ thành SQL. Đây là kiến thức phòng thủ.
22/09/2026
· 6 phút đọc
2
Băm mật khẩu: vì sao SHA-256 nhanh là SAI, và bcrypt chậm 1,3 triệu lần là cố ý
Dùng SHA-256 để băm mật khẩu nghe có vẻ an toàn — nó là hàm băm mạnh mà. Nhưng chính sự NHANH của nó khiến nó sai cho mật khẩu. Bài này đo thật trong Go: SHA-256 băm một mật khẩu trong 38 nano-giây (26 triệu lần/giây), còn bcrypt cost 10 mất 51ms — chậm hơn 1,3 triệu lần, cố ý, để brute-force bất khả thi; mỗi cost +1 chậm gấp đôi. Kèm vai trò của salt chống rainbow table.
22/09/2026
· 6 phút đọc
3
Timing attack: so sánh token bằng == làm rò rỉ bí mật qua thời gian — và cách so constant-time
So sánh một token hay chữ ký bằng == trông vô hại, nhưng nó thoát sớm ở byte đầu tiên khác nhau — nên thời gian so sánh tiết lộ bạn đã đoán đúng bao nhiêu byte. Bài này đo thật trong Go: so sánh thoát sớm cho thời gian tuyến tính theo số byte khớp (từ 1.2ns khi khớp 0% lên 50.692ns khi khớp 100%), còn ConstantTimeCompare phẳng ~54.700ns bất kể khớp bao nhiêu. Về lý thuyết, kênh thời gian đó cho phép dò token từng byte.
22/09/2026
· 6 phút đọc
4
XSS: khi dữ liệu người dùng biến thành mã, và vì sao html/template tự escape theo ngữ cảnh
XSS xảy ra khi dữ liệu người dùng được nhúng vào trang mà không mã hoá, biến thành mã JavaScript chạy trong trình duyệt nạn nhân. Bài này đo thật trong Go: text/template để nguyên thẻ script (lỗ hổng), html/template tự mã hoá thành text vô hại; và cùng một chuỗi được escape KHÁC nhau tuỳ ngữ cảnh — HTML dùng <, JavaScript dùng \u003c, URL dùng %3c. Mã hoá đúng ngữ cảnh là chìa khoá chống XSS.
22/09/2026
· 6 phút đọc
5
CSRF: vì sao cookie xác thực không đủ, và token chặn request giả mạo từ site khác
Trình duyệt tự động đính kèm cookie cho mọi request tới một domain — kể cả request do một trang độc khác kích hoạt. Đó là gốc rễ của CSRF: nếu server chỉ dựa vào cookie, kẻ tấn công khiến trình duyệt nạn nhân tự gửi request hợp lệ. Bài này tái hiện thật trong Go: một bank chỉ dựa cookie bị chuyển tiền (số dư 1000→900) từ request cross-site; thêm CSRF token, cùng request đó bị chặn (403, số dư không đổi). Cookie xác thực AI gửi; token xác thực request đến TỪ ĐÂU.
22/09/2026
· 6 phút đọc
6
JWT: chữ ký bảo vệ token khỏi sửa đổi, và bẫy alg=none biến verifier thành kẻ phản bội
JWT cho phép server tin một token mà không cần tra DB — nhờ chữ ký. Nhưng chữ ký chỉ bảo vệ khi bạn verify đúng. Bài này tự cài JWT HS256 trong Go và tái hiện thật: sửa payload role=admin mà không có secret để ký lại thì bị phát hiện ngay (chữ ký không khớp); nhưng một verifier ngây thơ TIN trường alg trong token sẽ bị bẫy alg=none qua mặt — chấp nhận token giả không chữ ký. Verify đúng phải ép thuật toán mong đợi.
22/09/2026
· 7 phút đọc
7
Command injection và path traversal: khi input biến thành lệnh shell hoặc thoát khỏi thư mục
Cùng gốc với SQL injection: khi input người dùng được ghép vào một lệnh shell, nó biến thành lệnh chạy trên server; khi được ghép vào đường dẫn file, ../ cho phép đọc file ngoài thư mục cho phép. Bài này tái hiện thật trong Go: shell nối chuỗi chạy lệnh thứ hai sau dấu chấm phẩy, còn exec.Command tách tham số coi input là một đối số vô hại; nối đường dẫn thô đọc được file bí mật ngoài baseDir, còn làm sạch + kiểm thuộc baseDir thì từ chối.
22/09/2026
· 6 phút đọc
8
Brute-force và rate limiting: biến thời gian dò mật khẩu từ 15 giờ thành 380.000 năm
Một endpoint đăng nhập không giới hạn số lần thử là lời mời brute-force: kẻ tấn công cứ thử mật khẩu đến khi trúng. Bài này đo thật trong Go: không giới hạn, kẻ tấn công thử 18 triệu lần/giây (dò hết không gian 10^12 trong 15 giờ); thêm rate limit 5 lần sai/phút, 99.995/100.000 request bị chặn và thời gian brute-force vọt lên 380.000 năm. Kèm các đánh đổi: DoS khoá tài khoản, IP chung, và vì sao CAPTCHA/MFA mạnh hơn.
22/09/2026
· 6 phút đọc
9
TLS/HTTPS: InsecureSkipVerify biến HTTPS thành vô dụng — mã hoá mà không xác thực là vô nghĩa
HTTPS làm hai việc: mã hoá dữ liệu VÀ xác thực bạn đang nói chuyện với đúng server. Tắt phần xác thực (InsecureSkipVerify) giữ lại mã hoá nhưng vứt bỏ sự an toàn. Bài này tái hiện thật trong Go: client mặc định từ chối cert tự ký với lỗi x509, nhưng InsecureSkipVerify=true chấp nhận bất kỳ cert nào — mở cửa cho kẻ người-đứng-giữa. Cách đúng khi cần tin cert riêng là thêm vào RootCAs, không bao giờ tắt verify.
22/09/2026
· 6 phút đọc
10
Quản lý bí mật: đừng hardcode, đừng log secret — và một kiểu Secret tự che trong Go
Hai cách rò rỉ bí mật phổ biến nhất không phải do bị hack: hardcode secret vào code (lộ qua git) và vô tình in secret ra log/error/JSON. Bài này đo thật trong Go: một config với mật khẩu kiểu string thường lộ nguyên trong cả log và JSON; một kiểu Secret tự che (override String/MarshalJSON) in ra *** ở mọi nơi nhưng code vẫn dùng được giá trị thật qua Reveal(). Mặc định an toàn thay vì nhớ che từng chỗ.
22/09/2026
· 6 phút đọc
11
Broken Access Control: lỗ hổng #1 OWASP và IDOR — vì sao server biết bạn là ai nhưng vẫn để bạn đọc dữ liệu người khác
Đăng nhập xong không có nghĩa được làm mọi thứ. Broken Access Control đứng #1 OWASP Top 10 vì rất dễ dính: server xác thực đúng bạn là ai, nhưng quên kiểm bạn có quyền với ĐỐI TƯỢNG này không. Bài này tái hiện IDOR trong lab cô lập: Alice chỉ đổi một số id trong URL là đọc được đơn hàng nhạy cảm của Bob (HTTP 200); thêm một dòng kiểm sở hữu phía server, cùng request đó trả về 403. Phòng thủ, không tấn công.
22/09/2026
· 9 phút đọc
12
Tổng kết sê-ri Bảo mật web: ánh xạ 11 lỗ hổng vào OWASP Top 10, checklist production và cây quyết định review
Mười một bài, mười một lỗ hổng tái hiện trong lab — giờ ghép lại thành một bản đồ dùng được. Bài tổng kết này ánh xạ từng lỗ hổng vào OWASP Top 10:2021 để bạn thấy bức tranh rủi ro, kèm một checklist chạy trước khi lên production và một cây quyết định dán cạnh màn hình khi review code: input vào SQL thì prepared statement, ra HTML thì escape, là id đối tượng thì kiểm sở hữu. Phòng thủ, không tấn công.
22/09/2026
· 8 phút đọc