Chủ đề bảo mật
Duyệt theo nhóm, lọc theo vai trò của bạn
Server-side
13Lỗ hổng phía máy chủ — nơi phần lớn thiệt hại thật xảy ra
Access control là phần trả lời câu hỏi "người dùng NÀY được làm gì với đối tượng NÀY". Lỗi phân quyền xảy ra khi hệ thống đã biết bạn là ai (xác thực xong) nhưng không kiểm bạn có được phép hay không. IDOR/BOLA là dạng phổ biến nhất: đổi `id=42` thành `id=43` và đọc được dữ liệu của người khác.
API security là tập những lỗ hổng xuất hiện vì API **không có giao diện để giấu lỗi sau**. Trong một ứng dụng render server-side, UI quyết định người dùng thấy gì; trong một API, mọi endpoint là bề mặt công khai và client chỉ là một trong nhiều thứ gọi tới nó. OWASP API Security Top 10 là bản danh mục của họ lỗi này.
Xác thực là phần trả lời "người này có đúng là họ nói không". Lỗ hổng xác thực là mọi cách một kẻ tấn công vượt qua bước đó mà không có đúng thông tin đăng nhập: đoán mật khẩu hàng loạt, bỏ qua một bước, dùng lại thông tin bị lộ, hay khai thác các luồng phụ (đặt lại mật khẩu, "nhớ tôi").
Lỗ hổng logic nghiệp vụ là khi kẻ tấn công dùng tính năng **đúng như nó được viết**, theo một cách mà người thiết kế không dự tính. Không có payload, không có ký tự đặc biệt, không có gì để escape — chỉ là một chuỗi thao tác hợp lệ dẫn tới một kết quả sai.
Command injection là lỗi khi input người dùng đi vào một lệnh shell và được shell đọc như CÚ PHÁP thay vì tham số. `;`, `|`, `&&`, `$(...)`, backtick và newline đều là toán tử của shell, nên một chuỗi chứa chúng không còn là một đối số mà trở thành lệnh thứ hai.
Lỗ hổng upload file là khi server tin những gì client nói về file: tên file, phần mở rộng, `Content-Type`. Cả ba đều do kẻ tấn công đặt. Hậu quả xấu nhất là **thực thi code**: một file `.aspx`/`.php`/`.jsp` nằm trong thư mục mà web server chịu chạy, rồi được gọi qua URL.
Rò rỉ thông tin là khi ứng dụng tiết lộ những gì kẻ tấn công cần để tấn công tiếp: stack trace, phiên bản framework, đường dẫn nội bộ, tên bảng, khoá API trong bundle JavaScript, hay chỉ là **sự khác nhau** giữa hai thông báo lỗi. Bản thân nó ít khi là sự cố; nó là bước một của mọi sự cố khác.
Path traversal là lỗi khi tên file do người dùng cung cấp được ghép vào đường dẫn hệ thống mà không kiểm, nên `../` đưa được thao tác đọc/ghi ra ngoài thư mục dự kiến. Kết quả điển hình là đọc `/etc/passwd`, `web.config`, `.env`, hoặc khoá SSH — và ở dạng ghi thì nó là ghi đè file, tức là thực thi code.
Race condition là lỗi khi hai request đồng thời đọc cùng một trạng thái cũ rồi cùng hành động dựa trên nó. Mẫu kinh điển là **kiểm rồi mới dùng** (TOCTOU): cả hai request thấy "mã giảm giá còn hiệu lực", cả hai áp dụng nó, và mã dùng được hai lần. Không có payload nào — chỉ có thời điểm.
SQL injection xảy ra khi dữ liệu người dùng gửi lên được ghép vào câu truy vấn dưới dạng CÚ PHÁP thay vì dữ liệu. Kết quả là kẻ tấn công không chỉ đổi được giá trị tìm kiếm, mà đổi được chính câu lệnh: thêm điều kiện, gộp bảng khác, hay chạy câu lệnh thứ hai. NoSQL injection là cùng một lỗi ở tầng khác — thay vì chuỗi SQL thì kẻ tấn công gửi lên một *object* toán tử mà driver diễn giải như truy vấn.
SSRF là lỗi cho phép kẻ tấn công quyết định server của bạn sẽ gửi request HTTP đi đâu. Ứng dụng nhận một URL từ người dùng — để lấy ảnh preview, gọi webhook, import dữ liệu — rồi tự đi gọi URL đó. Vì request phát ra từ TRONG mạng tin cậy, nó tới được những thứ mà kẻ tấn công không tự gọi được từ Internet.
Cache deception là ngược lại của cache poisoning: thay vì đầu độc một trang công khai, kẻ tấn công lừa cache **lưu một trang RIÊNG TƯ của nạn nhân** (trang tài khoản, dữ liệu cá nhân) rồi tự đọc nó. Cơ chế là cache và server bất đồng về "URL này là tài nguyên tĩnh hay động".
XXE là lỗi khi một bộ phân tích XML xử lý **external entity** — một cơ chế của chuẩn XML cho phép tài liệu khai báo "chỗ này thay bằng nội dung của file/URL kia". Kẻ tấn công gửi lên XML khai báo một entity trỏ tới `/etc/passwd` hay `http://169.254.169.254/`, và parser đọc nó hộ.
Client-side
7Trình duyệt là môi trường thù địch
Clickjacking là khi trang của bạn bị nhúng vào một iframe **trong suốt** trên trang của kẻ tấn công, xếp phía trên một nội dung mồi. Người dùng nghĩ họ bấm vào nút trên trang kẻ tấn công, nhưng cú bấm thật đi vào trang của bạn — với đầy đủ phiên đăng nhập của họ.
Content Security Policy là một header nói cho trình duyệt biết **script nào được phép chạy** trên trang của bạn. Nó không vá XSS — nó chuyển "kẻ tấn công chèn được code và code chạy" thành "kẻ tấn công chèn được HTML mà không chạy được gì". Đây là phòng thủ theo chiều sâu, không phải bản vá.
CORS là cơ chế cho phép một trang web đọc response từ một origin khác — nó **nới lỏng** same-origin policy, không thắt chặt nó. Cấu hình sai CORS nghĩa là bạn đã cho phép một trang của kẻ tấn công đọc dữ liệu của người dùng bạn, bằng chính phiên đăng nhập của họ.
XSS là lỗi khi dữ liệu do người dùng kiểm soát được trình duyệt diễn giải thành CODE thay vì nội dung. Script của kẻ tấn công chạy trong origin của bạn, nên nó có mọi thứ mà JavaScript của bạn có: cookie không HttpOnly, token trong localStorage, và quyền gửi request đã kèm session của nạn nhân.
CSRF là lỗi khi một trang web khác làm cho trình duyệt của nạn nhân gửi một request có tác dụng tới ứng dụng của bạn. Trình duyệt **tự động kèm cookie** vào request đi tới domain đó, nên request tới với đầy đủ phiên đăng nhập của nạn nhân dù nó phát ra từ một trang hoàn toàn khác.
Lỗ hổng DOM-based là khi JavaScript của chính trang lấy dữ liệu từ một **nguồn** kẻ tấn công kiểm soát (URL, `location.hash`, `postMessage`, `localStorage`) và đưa nó vào một **đích** nguy hiểm (`innerHTML`, `eval`, `location`) — tất cả xảy ra trong trình duyệt, không đi qua server.
WebSocket mở một kênh hai chiều lâu dài giữa trình duyệt và server. Vấn đề bảo mật cốt lõi là bắt tay WebSocket **không bị same-origin policy ràng buộc như fetch/XHR** và **trình duyệt tự kèm cookie** — nên nếu server không tự kiểm `Origin`, một trang khác mở được kết nối kèm phiên của nạn nhân (CSWSH).
Advanced & protocol
10Giao thức, deserialization, LLM — nhóm khó nhất
GraphQL là một lớp truy vấn cho phép client tự chọn hình dạng dữ liệu nó nhận. Vấn đề bảo mật đến từ chính sự linh hoạt đó: **client quyết định câu truy vấn**, nên bề mặt tấn công là toàn bộ graph — không phải một tập endpoint cố định — và phân quyền, giới hạn tài nguyên phải áp ở tầng field, không ở tầng route.
Tấn công Host header là khi ứng dụng tin giá trị header `Host` (hoặc `X-Forwarded-Host`) — một giá trị do client gửi và kẻ tấn công đổi tự do — và dùng nó để dựng URL, chọn cấu hình, hay điều hướng. Hậu quả điển hình là password-reset poisoning (link đặt lại trỏ tới domain kẻ tấn công) và cache poisoning.
HTTP request smuggling là khi hai máy chủ trên cùng một đường — thường một reverse proxy/CDN ở trước và một backend ở sau — **bất đồng về chỗ một request kết thúc**. Kẻ tấn công lợi dụng sự bất đồng đó để nhét phần đầu của một request thứ hai vào cuối request thứ nhất, và phần đó được backend xử lý như một request riêng — nhưng gắn vào kết nối của người dùng kế tiếp.
Deserialization không an toàn là khi ứng dụng khôi phục object từ dữ liệu không tin cậy. Vấn đề không phải là dữ liệu — mà là **kẻ tấn công quyết định class nào được khởi tạo**. Chọn đúng class có sẵn trong classpath là chạy được code, và không cần một lỗ hổng nào khác.
JWT là một token **tự mô tả**: nó mang cả dữ liệu (claim) lẫn thông tin về cách nó được ký (header `alg`). Vấn đề nằm ở chỗ đó — một token tự nói cách kiểm nó thì nó cũng tự nói dối được, và phần lớn lỗ hổng JWT là việc thư viện **tin header của token** thay vì tin cấu hình của bạn.
OAuth 2.0 là giao thức **uỷ quyền** — nó cấp cho một ứng dụng quyền truy cập tài nguyên thay mặt người dùng. OpenID Connect (OIDC) là một lớp **xác thực** dựng trên OAuth để trả lời "người này là ai". Phần lớn lỗ hổng đến từ việc dùng OAuth (uỷ quyền) như thể nó là xác thực, và từ việc bỏ sót các phép kiểm mà giao thức đòi hỏi nhưng không cưỡng chế được.
Prototype pollution là lỗi riêng của JavaScript: kẻ tấn công đặt một thuộc tính lên `Object.prototype` — prototype dùng chung của MỌI object — nên một trường mà họ chèn xuất hiện trên mọi object trong ứng dụng. Đường vào thường là một hàm merge/clone/set-path đệ quy nhận khoá `__proto__` từ dữ liệu người dùng.
SSTI là khi input người dùng đi vào **template** thay vì vào dữ liệu mà template render. Template engine được thiết kế để chạy code — vòng lặp, điều kiện, gọi thuộc tính — nên một chuỗi người dùng trở thành mã template là quyền chạy code, thường tới tận RCE.
Cache poisoning là khi kẻ tấn công làm một cache (CDN, reverse proxy) lưu một response độc hại, rồi cache đó phục vụ response đó cho mọi người dùng sau. Cơ chế gốc là **cache key không bao gồm một input mà response THỰC SỰ phụ thuộc vào** — nên hai request "giống nhau theo cache" lại cho hai response khác nhau, và bản độc hại được lưu.
Tấn công web LLM là họ lỗ hổng xuất hiện khi ứng dụng đưa một mô hình ngôn ngữ vào luồng: nhận input người dùng, ghép vào prompt, rồi cho mô hình gọi công cụ hoặc đọc dữ liệu. Vấn đề gốc là **mô hình không phân biệt được đâu là chỉ dẫn của bạn và đâu là dữ liệu của kẻ tấn công** — với nó, cả hai đều là văn bản trong cùng một cửa sổ ngữ cảnh.
SDLC & supply chain
7PortSwigger không phủ nhóm này. Developer cần nó hằng ngày
Bạn có chắc image đang chạy trong prod là image bạn đã build không?
Pipeline có quyền deploy prod. Ai sửa được pipeline thì sửa được prod.
Phần lớn code trong sản phẩm của bạn không do bạn viết. A03 là hạng mục hoàn toàn mới của Top 10:2025.
Secret trong env hiện ra ở `docker inspect`, `/proc`, và log crash.
Dạng bài gần công việc thật nhất: đọc diff, đánh dấu dòng có vấn đề, và không kêu bừa.
Đọc chuẩn như đang audit: nếu phải trả lời có/không cho requirement này, tôi trả lời được không?
Topic duy nhất bắt buộc với cả năm vai, vì nó là hoạt động nhóm chứ không phải kỹ năng cá nhân.
Cryptography & data
5Mật mã dùng sai còn tệ hơn không dùng
Mã hoá không phải một cái công tắc. Câu hỏi thật là: khoá ở đâu, ai đọc được?
Khoá không xoay được là khoá sẽ không bao giờ được xoay.
Argon2id, không phải SHA-256. Và nếu dùng được passwordless thì không lưu gì cả là tốt nhất.
Dữ liệu bạn không lưu là dữ liệu không thể rò.
HSTS, phiên bản tối thiểu, chain, và ngày hết hạn mà không ai theo dõi.
Infrastructure & runtime
6Nơi lớp phòng thủ cuối cùng sống
Non-root, read-only rootfs, drop ALL capability — ba dòng cấu hình chặn phần lớn kịch bản leo thang.
A10 là hạng mục mới của Top 10:2025 và dễ bị coi nhẹ: khi API bên ngoài timeout, code của bạn fail-open hay fail-closed?
Pod Security Standards, NetworkPolicy default-deny, RBAC tối thiểu. Over-privileged service account là lỗi phổ biến nhất.
Log mà không ai đọc thì bằng không có log. Câu hỏi cho CTO: chúng ta có biết mình đang bị tấn công không?
Egress deny-by-default biến SSRF từ một sự cố thành một dòng log.
Không phải chống DDoS — mà chống một người dùng hợp lệ làm điều hợp lệ một triệu lần.
An toàn số cá nhân
3Cho người dùng thường — tách riêng khỏi nội dung AppSec
SMS là lớp bảo vệ yếu nhất trong các lớp thứ hai. Passkey là mạnh nhất.
Một mật khẩu mạnh bạn nhớ được thì không mạnh. Dùng trình quản lý mật khẩu.
Không phải nhận ra email xấu — mà là có thói quen kiểm tra trước khi bấm.