Bối Cảnh & Tại Sao Threat Modeling Cứu Bạn Lúc 2 Giờ Sáng
Sau khi server bị tấn công SSH brute-force lúc nửa đêm, tôi nhận ra một điều khó chịu: tôi đã dành hàng tuần để xây dựng ứng dụng nhưng chưa đến một tiếng để suy nghĩ xem ai có thể tấn công nó và bằng cách nào. Vụ xâm nhập đó không hề tinh vi — nó hoàn toàn có thể đoán trước được. Và hoàn toàn có thể ngăn chặn, nếu tôi đặt ra những câu hỏi đúng từ một tháng trước.
Threat modeling là thực hành đặt ra những câu hỏi đó — một cách có hệ thống — trước khi bạn viết code. Không phải để đánh dấu ô tuân thủ. Không phải nghi lễ hàng quý. Đây là kỷ luật thiết kế buộc bạn phải tư duy như kẻ tấn công trong khi thay đổi vẫn còn rẻ.
Hầu hết các nhóm bỏ qua nó vì nghe có vẻ trừu tượng. Đó là quyết định sai. System Science Institute của IBM cho thấy việc vá một lỗ hổng bảo mật trên môi trường production tốn gấp 15 lần so với phát hiện nó ở giai đoạn thiết kế. STRIDE cho bạn một bộ từ vựng cụ thể để tìm ra những lỗ hổng đó trên bảng trắng, không phải trong buổi họp rút kinh nghiệm sau sự cố.
STRIDE Thực Sự Có Nghĩa Là Gì
STRIDE là mô hình phân loại mối đe dọa được phát triển tại Microsoft năm 1999. Mỗi chữ cái tương ứng với một loại tấn công:
- S — Spoofing (Giả mạo): Giả vờ là người khác (JWT token giả, caller API giả mạo)
- T — Tampering (Giả mạo dữ liệu): Sửa đổi dữ liệu trong quá trình truyền hoặc lưu trữ (SQL injection, thao túng request body)
- R — Repudiation (Chối bỏ): Phủ nhận một hành động đã xảy ra (thiếu audit log, không có request signature)
- I — Information Disclosure (Lộ thông tin): Rò rỉ dữ liệu nhạy cảm (thông báo lỗi quá chi tiết, trường DB không được mã hóa)
- D — Denial of Service (Từ chối dịch vụ): Làm gián đoạn tính khả dụng (API endpoint không giới hạn, làm cạn kiệt tài nguyên)
- E — Elevation of Privilege (Leo thang đặc quyền): Đạt được quyền truy cập trái phép (RBAC bị hỏng, lỗ hổng IDOR)
Đừng chỉ học thuộc lòng từ viết tắt. Hãy dùng nó như một checklist để kiểm tra từng thành phần trong sơ đồ hệ thống của bạn. Với mỗi trust boundary, mỗi luồng dữ liệu, mỗi tiến trình — hỏi xem những danh mục STRIDE nào áp dụng. Đó là toàn bộ phương pháp.
Thiết Lập Bộ Công Cụ Threat Modeling
Giấy phép đắt tiền là tùy chọn. Đây là những gì thực sự được dùng hàng ngày:
Lựa Chọn 1: OWASP Threat Dragon (Miễn Phí, Chạy Trên Trình Duyệt)
Threat Dragon cho phép bạn vẽ sơ đồ luồng dữ liệu (DFD) và ghi chú các mối đe dọa theo từng thành phần. Chạy nó trên máy local với Docker trong chưa đầy hai phút:
docker pull owasp/threat-dragon:latest
docker run -it --rm \
-p 3000:3000 \
-e ENCRYPTION_JWT_REFRESH_SIGNING_KEY='your-secret-key' \
-e ENCRYPTION_JWT_SIGNING_KEY='your-signing-key' \
-e ENCRYPTION_KEYS='{"current":{"issuer":"tdServer","keys":[{"isPrimary":true,"kty":"oct","use":"enc","alg":"A256GCM","kid":"k1","k":"your-base64-key"}]}}' \
owasp/threat-dragon:latest
Truy cập http://localhost:3000 và bắt đầu vẽ kiến trúc của bạn. Không cần tài khoản, không SaaS, không bị ràng buộc nhà cung cấp.
Lựa Chọn 2: Threat Modeling với Python + pytm
pytm cho phép bạn định nghĩa kiến trúc bằng Python và tự động tạo ra cả DFD lẫn báo cáo mối đe dọa. Ưu điểm thực sự: các threat model nằm trong version control, ngay cạnh code mà chúng mô tả.
pip install pytm
from pytm import TM, Server, Datastore, Dataflow, Boundary, Actor
tm = TM("Web API Threat Model")
tm.description = "Phân tích STRIDE cho REST API backend"
tm.isOrdered = True
internet = Boundary("Internet")
app_boundary = Boundary("Application Server")
db_boundary = Boundary("Database Layer")
user = Actor("User", inBoundary=internet)
api = Server("REST API", inBoundary=app_boundary)
auth = Server("Auth Service", inBoundary=app_boundary)
db = Datastore("PostgreSQL", inBoundary=db_boundary)
Dataflow(user, api, "HTTPS Request")
Dataflow(api, auth, "Token Validation")
Dataflow(api, db, "SQL Query")
Dataflow(db, api, "Query Result")
Dataflow(api, user, "HTTPS Response")
tm.process()
Chạy với lệnh:
python threat_model.py --report report.html
python threat_model.py --dfd # tạo sơ đồ DFD
Kết quả đầu ra ánh xạ các mối đe dọa được tạo tự động vào các danh mục STRIDE cho từng thành phần. Nó không thay thế được phán đoán của con người, nhưng sẽ phát hiện những lỗ hổng rõ ràng và tích hợp trực tiếp vào CI pipeline của bạn.
Áp Dụng STRIDE vào Kiến Trúc Web API Thực Tế
Lấy một thiết lập điển hình: REST API hướng người dùng, backend PostgreSQL, xác thực JWT, tích hợp thanh toán bên thứ ba. Đi qua từng trust boundary với STRIDE và bạn sẽ thấy bất ngờ ở cả ba tầng.
Trust Boundary: Internet → API Gateway
Đây là nơi kích hoạt nhiều danh mục STRIDE nhất trong toàn hệ thống:
- Spoofing (Giả mạo): Kẻ tấn công có thể giả mạo header
X-Forwarded-Forđể vượt qua giới hạn tốc độ theo IP không? Có — nếu API của bạn tin tưởng header đó vô điều kiện. Cách sửa: chỉ chấp nhận nó khi IP nguồn nằm trong dải CIDR của load balancer. - Denial of Service (Từ chối dịch vụ): Có giới hạn tốc độ trên các endpoint chưa xác thực như
/registerhay/forgot-passwordkhông? Nếu không có, cả hai đều là vector DoS miễn phí. Kẻ tấn công với 5 đô/tháng có thể làm cạn kiệt connection pool của DB trong vài phút. - Tampering (Giả mạo dữ liệu): Request body có được validate theo schema trước khi xử lý không? Trường
role: "admin"không được kiểm tra trong payload đăng ký đã khiến không ít team lao đao trên production.
# Ví dụ: Validate request nghiêm ngặt với Pydantic
from pydantic import BaseModel, field_validator
from typing import Literal
class RegisterRequest(BaseModel):
email: str
password: str
# Không bao giờ nhận role từ client — gán phía server
@field_validator('email')
@classmethod
def email_must_be_valid(cls, v):
if '@' not in v:
raise ValueError('email không hợp lệ')
return v.lower().strip()
Trust Boundary: API → Auth Service
- Spoofing (Giả mạo): Thuật toán ký JWT của bạn có được khóa cứng thành
RS256hayHS256không? Tấn công kinh điểnalg: nonevẫn hoạt động trên các thư viện cho phép nó theo mặc định — và cơ sở dữ liệu CVE có các mục từ năm 2023 cho đúng vấn đề này. - Repudiation (Chối bỏ): Khi token được cấp, bạn có ghi log
user_id,ipvàissued_atphía server không? Bỏ qua điều này và bạn không thể tái hiện lại những gì đã xảy ra trong một phiên bị xâm phạm. - Information Disclosure (Lộ thông tin): Thông báo lỗi xác thực của bạn có phân biệt giữa “không tìm thấy người dùng” và “sai mật khẩu” không? Cả hai nên trả về cùng một thông báo chung chung. Việc liệt kê người dùng chỉ cần khoảng 10 dòng Python để tự động hóa.
import jwt
# Luôn chỉ định rõ các thuật toán được phép
def verify_token(token: str, public_key: str) -> dict:
try:
payload = jwt.decode(
token,
public_key,
algorithms=["RS256"], # Không bao giờ dùng ["RS256", "none"]
options={"require": ["exp", "iat", "sub"]}
)
return payload
except jwt.ExpiredSignatureError:
raise ValueError("Token đã hết hạn")
except jwt.InvalidTokenError:
raise ValueError("Token không hợp lệ") # Thông báo chung, không lộ chi tiết
Trust Boundary: API → Database
- Tampering (Giả mạo dữ liệu): Tất cả các query có được tham số hóa không? Nối chuỗi trực tiếp vào SQL là điều không bao giờ được chấp nhận — không phải năm 2024, không phải với công cụ nội bộ, không bao giờ.
- Elevation of Privilege (Leo thang đặc quyền): DB user của ứng dụng có quyền
DROP TABLEkhông? Không nên có. Một role có ít đặc quyền nhất có thể giới hạn mức độ thiệt hại khi thông tin xác thực bị rò rỉ. - Information Disclosure (Lộ thông tin): Các trường PII (email, số điện thoại, CMND) có được mã hóa ở cấp cột không — chứ không chỉ ở cấp đĩa? Mã hóa toàn đĩa không bảo vệ dữ liệu được truy vấn dưới dạng plaintext bởi một tiến trình ứng dụng bị xâm phạm.
-- Tạo role ứng dụng với đặc quyền tối thiểu
CREATE ROLE api_user WITH LOGIN PASSWORD 'strong-random-password';
GRANT SELECT, INSERT, UPDATE ON users, sessions TO api_user;
GRANT SELECT ON products TO api_user;
-- Không DELETE, không DROP, không thay đổi schema
Xác Minh & Giữ Cho Threat Model Luôn Sống
Một threat model chỉ tồn tại trên Confluence thực chất đã chết. Kiến trúc thay đổi theo thời gian. Microservice mới xuất hiện. Tích hợp bên thứ ba bị thay thế. Mô hình cần phải thay đổi cùng với chúng.
Checklist Review Threat Model
Chạy danh sách này mỗi khi bạn triển khai tính năng, endpoint hoặc tích hợp mới — chỉ mất khoảng năm phút:
- Chúng ta có thêm trust boundary mới không? (microservice mới, API bên thứ ba mới)
- Có luồng dữ liệu mới nào mang PII hoặc thông tin xác thực không?
- Các endpoint mới có được bảo vệ bởi xác thực và phân quyền không?
- Chúng ta có đưa vào bề mặt chưa xác thực mới nào không?
- Thành phần mới có được giới hạn tốc độ không?
- Phản hồi lỗi từ thành phần mới có đủ chung chung để tránh lộ thông tin nội bộ không?
Tích Hợp Threat Modeling vào CI/CD
Với pytm, bạn có thể tạo và so sánh báo cáo mối đe dọa trên mỗi pull request. Các mối đe dọa mới sẽ chặn việc merge cho đến khi có người review:
# Trong CI pipeline (ví dụ GitHub Actions)
- name: Tạo báo cáo threat model
run: |
pip install pytm
python threat_model.py --report threat_report_new.html
diff threat_report_baseline.html threat_report_new.html > threat_diff.txt
if [ -s threat_diff.txt ]; then
echo "Threat model đã thay đổi — cần review"
cat threat_diff.txt
exit 1
fi
Không cần kỹ sư bảo mật ngồi canh từng PR. Pipeline sẽ tự động đánh dấu thay đổi. Review xảy ra đúng lúc cần thiết — trong quá trình code review, không phải sáu tháng sau trong buổi rút kinh nghiệm.
Ưu Tiên Thực Tế: Đánh Giá Rủi Ro Cho Từng Mối Đe Dọa
Không phải mối đe dọa nào được xác định cũng cần được giải quyết trong sprint này. Chấm điểm từng mối đe dọa trên hai trục:
- Khả năng xảy ra: 1 (khó xảy ra) đến 3 (có khả năng cao)
- Mức độ ảnh hưởng: 1 (thấp) đến 3 (nghiêm trọng)
- Điểm rủi ro: Khả năng xảy ra × Mức độ ảnh hưởng
Điểm 6–9: đưa vào sprint hiện tại như một task bảo mật. Điểm 3–5: backlog, kèm deadline. Điểm 1–2: ghi chép lại, chấp nhận, xem xét lại vào quý sau. Cách này giúp nhóm tiếp tục tiến độ mà không coi mọi mối đe dọa lý thuyết là blocker P0 chặn release.
Thói Quen Duy Nhất Thay Đổi Tất Cả
Dành 30 phút threat modeling vào đầu mỗi phiên thiết kế tính năng. Trước khi bất kỳ code nào được viết. Chỉ cần bảng trắng và checklist STRIDE. Hỏi điều gì có thể xảy ra sai ở mỗi trust boundary. Ghi lại các mối đe dọa. Phân công biện pháp giảm thiểu. Xong.
Sự cố SSH brute-force đó dạy tôi rằng kẻ tấn công không chờ đúng lúc thuận tiện. Chúng tấn công lúc nửa đêm thứ Sáu, khi kỹ sư trực đang ngủ và không ai nhìn vào dashboard. Threat modeling là cách bạn có những cuộc trò chuyện đó trong giờ làm việc — khi sửa chữa chỉ tốn một buổi chiều thay vì cả tuần.

