Nền Tảng Bảo Mật Web: CIA Triad & Threat Modeling

Chào mừng đến với Giai Đoạn 1 của Lộ Trình Học Bảo Mật Web! Bài viết này bao gồm các khái niệm bảo mật nền tảng mà mọi developer phải hiểu trước khi viết bất kỳ dòng code production nào.
Bảo mật không phải là ghi nhớ một danh sách lỗ hổng — mà là phát triển tư duy bảo mật. Những nền tảng này cung cấp cho bạn các mô hình tư duy để lý luận về bảo mật trong bất kỳ tình huống nào, trên bất kỳ ngôn ngữ hay framework nào.
Sau bài viết này, bạn sẽ có bộ công cụ khái niệm để suy nghĩ như một security engineer: xác định những gì cần bảo vệ, những gì đe dọa nó, và cách xây dựng hệ thống tự bảo vệ ở nhiều lớp.
Bạn Sẽ Học Được
✅ Hiểu CIA Triad và áp dụng cho quyết định ứng dụng thực tế
✅ Thực hiện threat modeling bằng framework STRIDE
✅ Lập bản đồ attack surface trong ứng dụng của bạn
✅ Thiết kế hệ thống với defense in depth
✅ Áp dụng nguyên tắc least privilege
✅ Hiểu cơ bản về zero trust architecture
✅ Tích hợp yêu cầu bảo mật vào quy trình phát triển
CIA Triad: Nền Tảng Của Bảo Mật
Mọi quyết định bảo mật đều quy về ba thuộc tính. Chúng cùng nhau tạo thành CIA Triad — nền tảng của bảo mật thông tin.
Confidentiality (Bảo Mật)
Confidentiality có nghĩa là bảo vệ dữ liệu khỏi truy cập trái phép. Chỉ những người đúng mới có thể đọc thông tin nhạy cảm.
Ví dụ trong ứng dụng web:
- Mật khẩu được lưu dạng hash, không bao giờ là plaintext
- User A không thể đọc tin nhắn riêng tư của User B
- API keys không bị expose trong JavaScript phía client
- Database credentials không được commit vào source code
- HTTPS mã hóa dữ liệu trong quá trình truyền
Khi confidentiality bị vi phạm:
- SQL injection làm lộ toàn bộ bảng user
- Insecure Direct Object Reference (IDOR) expose dữ liệu của user khác
- File
.envbị rò rỉ, lộ database credentials - Log các trường nhạy cảm (mật khẩu, thẻ tín dụng) ra file log
Kỹ thuật triển khai:
- Mã hóa at rest (cấp database, cấp field)
- Mã hóa in transit (TLS/HTTPS)
- Kiểm soát truy cập (ai có thể đọc gì)
- Che dấu dữ liệu (hiển thị
****1234thay vì số thẻ đầy đủ)
Integrity (Toàn Vẹn)
Integrity có nghĩa là đảm bảo dữ liệu chính xác và không bị sửa đổi bởi các bên trái phép — có chủ ý hoặc vô tình.
Ví dụ trong ứng dụng web:
- JWT token được ký số (payload bị tamper sẽ bị từ chối)
- Checksum trên file download
- Database transactions với đảm bảo ACID
- Input validation từ chối dữ liệu không đúng định dạng
- Audit log ghi lại ai thay đổi gì
Khi integrity bị vi phạm:
- Kẻ tấn công sửa JWT payload (đổi
"role": "user"thành"role": "admin") - Giá trong giỏ hàng bị thao túng phía client
- Kẻ tấn công thực hiện man-in-the-middle và sửa response API
- SQL injection chèn hoặc sửa đổi bản ghi
Kỹ thuật triển khai:
- Chữ ký mật mã (JWT, HMAC, digital signatures)
- Input validation phía server (không bao giờ tin client)
- Parameterized queries (ngăn sửa đổi dữ liệu qua injection)
- Checksums và xác minh hash
- Audit log bất biến
Availability (Khả Dụng)
Availability có nghĩa là hệ thống và dữ liệu có thể truy cập bởi người dùng được ủy quyền khi họ cần.
Ví dụ trong ứng dụng web:
- Rate limiting ngăn một user duy nhất làm quá tải API
- Auto-scaling xử lý traffic spikes
- Health checks tự động restart service bị lỗi
- CDN caching giảm tải cho origin server
- Database replication cho phép failover
Khi availability bị vi phạm:
- Tấn công Denial of Service (DoS) làm tràn ngập server
- Một query thiếu index gây slow query làm sập database
- Memory leak dần dần cạn kiệt tài nguyên server
- Upload file độc hại lấp đầy dung lượng đĩa
- Sự cố dependency cascade đến service của bạn
Kỹ thuật triển khai:
- Rate limiting và throttling
- Circuit breakers cho external dependencies
- Caching (Redis, CDN)
- Horizontal scaling và load balancing
- Health monitoring và auto-restart
CIA Trong Thực Tế: Cân Bằng Trade-offs
Quyết định bảo mật thực tế bao gồm cân bằng cả ba:
| Kịch bản | C | I | A | Trade-off |
|---|---|---|---|---|
| Tin nhắn mã hóa end-to-end | ✅ Cao | ✅ Cao | ⚠️ Trung bình | Tin nhắn không thể khôi phục nếu mất key |
| API public read-only | ⚠️ Thấp | ✅ Cao | ✅ Cao | Dữ liệu công khai nhưng phải chính xác |
| Admin override khẩn cấp | ⚠️ Thấp | ⚠️ Thấp | ✅ Cao | Break-glass access hy sinh C và I cho A |
| Ứng dụng offline-first | ✅ Cao | ⚠️ Trung bình | ✅ Cao | Thay đổi local có thể xung đột với server |
Insight quan trọng: Tối đa hóa cả ba đồng thời là không thể. Bảo mật là về việc đưa ra trade-offs có ý thức dựa trên threat model của bạn — không phải đạt được sự hoàn hảo.
Threat Modeling: Suy Nghĩ Như Kẻ Tấn Công
Threat modeling là quá trình xác định một cách có hệ thống những gì có thể xảy ra sai với ứng dụng của bạn. Làm điều này trước khi viết code, không phải sau khi bị tấn công.
Mục tiêu: biết rủi ro của mình để có thể ưu tiên phòng thủ.
Framework 4 Câu Hỏi
Mọi threat model đều trả lời bốn câu hỏi:
- Chúng ta đang xây dựng gì? — Hiểu hệ thống
- Điều gì có thể xảy ra sai? — Xác định các mối đe dọa
- Chúng ta sẽ làm gì về điều đó? — Lập kế hoạch giảm thiểu
- Chúng ta đã làm đủ tốt chưa? — Xác nhận mô hình
Bước 1: Hiểu Những Gì Bạn Đang Xây Dựng
Bắt đầu với Data Flow Diagram (DFD). Lập bản đồ:
- External entities — Users, dịch vụ bên thứ ba, hệ thống khác
- Processes — Các thành phần ứng dụng của bạn
- Data stores — Databases, file systems, caches
- Data flows — Cách dữ liệu di chuyển giữa chúng
- Trust boundaries — Ranh giới phân tách các mức độ tin tưởng khác nhau
Ví dụ cho một blog đơn giản có authentication:
Trust boundaries cần đánh dấu:
- Internet → Load Balancer (input không tin cậy vào đây)
- API → Database (ranh giới nội bộ tin cậy)
- API → Email Service (bên ngoài, một phần tin cậy)
Bước 2: Xác Định Mối Đe Dọa Với STRIDE
STRIDE là từ viết tắt cho sáu loại mối đe dọa, được phát triển bởi Microsoft. Với mỗi thành phần trong DFD, hỏi: "Điều này có thể bị STRIDE không?"
| Chữ cái | Mối Đe Dọa | Vi Phạm | Ví Dụ |
|---|---|---|---|
| S | Spoofing (Giả mạo) | Authentication | Kẻ tấn công giả vờ là user khác |
| T | Tampering (Giả mạo dữ liệu) | Integrity | Kẻ tấn công sửa đổi dữ liệu trong transit |
| R | Repudiation (Chối bỏ) | Non-repudiation | User chối bỏ đã đặt hàng |
| I | Information Disclosure (Rò rỉ thông tin) | Confidentiality | Error message lộ stack trace |
| D | Denial of Service | Availability | Flood requests làm sập server |
| E | Elevation of Privilege (Leo thang đặc quyền) | Authorization | User thường truy cập endpoint admin |
Áp Dụng STRIDE Cho Ví Dụ Blog
Login endpoint (POST /login):
| STRIDE | Mối Đe Dọa | Giảm Thiểu |
|---|---|---|
| S | Kẻ tấn công brute-force credentials | Rate limiting, khóa tài khoản, CAPTCHA |
| T | Request bị tamper trong transit | HTTPS/TLS |
| R | User chối bỏ đã đăng nhập | Audit log với IP, timestamp, user agent |
| I | Error message tiết lộ "user không tồn tại" vs "sai mật khẩu" | Error chung: "Thông tin đăng nhập không hợp lệ" |
| D | Flood login requests | Rate limit theo IP và theo tài khoản |
| E | Bypass login hoàn toàn | Xác thực session phía server ở mọi request |
Database (PostgreSQL):
| STRIDE | Mối Đe Dọa | Giảm Thiểu |
|---|---|---|
| S | Kẻ tấn công kết nối trực tiếp DB | Firewall rules, không expose công khai |
| T | SQL injection sửa đổi bản ghi | Parameterized queries, ORM |
| R | DBA chối bỏ đã thực hiện thay đổi | Database audit logging |
| I | SQL injection làm lộ các bảng nhạy cảm | Parameterized queries, DB user least privilege |
| D | Query gây full table scan, làm sập DB | Query timeouts, indexes, connection pooling |
| E | App DB user có đặc quyền SUPERUSER | Dùng tài khoản DB least-privilege |
Bước 3: Đánh Giá và Ưu Tiên Mối Đe Dọa
Không phải mối đe dọa nào cũng như nhau. Dùng điểm DREAD hoặc ma trận Rủi Ro = Khả Năng × Tác Động đơn giản hơn:
Ví dụ ưu tiên:
| Mối Đe Dọa | Khả Năng | Tác Động | Rủi Ro | Ưu Tiên |
|---|---|---|---|---|
| SQL injection trên login | Cao | Cao | Tới Hạn | Sửa trước |
| Brute force login | Cao | Trung bình | Cao | Sửa sớm |
| Admin DB user | Thấp | Cao | Cao | Sửa sớm |
| Thiếu audit logs | Trung bình | Thấp | Trung bình | Lên kế hoạch |
| Bypass CAPTCHA | Thấp | Thấp | Thấp | Sau |
Bước 4: Định Nghĩa Giảm Thiểu
Với mỗi mối đe dọa, định nghĩa một biện pháp giảm thiểu cụ thể:
Mối đe dọa: SQL injection trên login endpoint
Rủi ro: Tới Hạn
Giảm thiểu: Dùng parameterized queries qua ORM (TypeORM/SQLAlchemy)
Xác nhận: Code review + automated SQLi scanner trong CI/CD
Người phụ trách: Backend team
Timeline: Trước release tiếp theoPhân Tích Attack Surface
Attack surface của bạn là mọi thứ có thể tiếp cận bởi kẻ tấn công:
Quy tắc giảm thiểu attack surface:
- Tắt các tính năng không dùng (xóa routes không dùng, tắt debug endpoints)
- Giảm thiểu các cổng expose (chỉ 443 công khai, còn lại internal)
- Xóa dependencies không dùng
- Thu hồi các quyền không cần thiết
- Tắt tài khoản mặc định và đổi credentials mặc định
Defense in Depth: Không Bao Giờ Dựa Vào Một Lớp
Defense in depth là nguyên tắc rằng bảo mật nên đến từ nhiều lớp độc lập — để nếu một lớp thất bại, các lớp khác vẫn bảo vệ hệ thống.
Phép so sánh kinh điển: một lâu đài thời trung cổ không chỉ dựa vào bức tường ngoài. Nó có hào nước, tường ngoài, cổng thành, tường trong, lính gác và tháp pháo. Kẻ tấn công phải đánh bại tất cả những thứ này.
Mô Hình Lớp Bảo Mật
Mỗi lớp độc lập. Nếu kẻ tấn công bypass WAF (Lớp 1), họ vẫn phải đối mặt với TLS ở Lớp 2, auth ở Lớp 3, input validation ở Lớp 4, và parameterized queries ở Lớp 5.
Defense in Depth Thực Tế Cho Web API
Hãy theo dõi cách defense in depth bảo vệ chống lại một nỗ lực SQL injection:
Tấn công: POST /api/login với body {"email": "admin'--", "password": "x"}
| Lớp | Phòng Thủ | Điều Gì Xảy Ra |
|---|---|---|
| Network (WAF) | WAF rule phát hiện pattern SQLi | Request bị chặn ✅ |
| Rate Limiting | 5 lần đăng nhập thất bại → chặn IP | — |
| Input Validation | Validation định dạng email từ chối admin'-- | Request bị chặn ✅ |
| Application Logic | Auth service dùng ORM với parameterized queries | Query an toàn ✅ |
| Database | DB user chỉ có SELECT trên bảng users | Không thể sửa đổi ✅ |
| Monitoring | Pattern đăng nhập bất thường kích hoạt alert | Đội bảo mật được thông báo ✅ |
Ngay cả khi WAF bỏ sót tấn công, bốn lớp khác vẫn bắt được. Không có điểm lỗi đơn.
Anti-Pattern Defense in Depth
❌ Security through obscurity — Ẩn API endpoints hoặc dùng port không chuẩn không phải là lớp bảo mật. Kẻ tấn công có thể phát hiện chúng.
❌ Trust but don't verify — Tin tưởng rằng load balancer validate auth, nên API không kiểm tra. Nếu LB bị cấu hình sai, bạn không có bảo vệ nào.
❌ Single authentication check — Chỉ validate auth ở gateway, không trong các microservice riêng lẻ. Nếu một service được truy cập trực tiếp (vi phạm mạng nội bộ), không có auth nào áp dụng.
❌ All-or-nothing access — Hoặc full admin hoặc không có quyền truy cập. Thiếu quyền chi tiết có nghĩa là một tài khoản bị xâm phạm cho toàn bộ quyền truy cập.
Nguyên Tắc Least Privilege
Nguyên tắc least privilege (PoLP) nói rằng mọi hệ thống, người dùng và process chỉ nên có quyền tối thiểu cần thiết để thực hiện chức năng của nó — không hơn.
Tại Sao Quan Trọng
Nếu kẻ tấn công xâm phạm một thành phần có đặc quyền quá mức, họ kế thừa những đặc quyền đó. Giới hạn quyền giới hạn blast radius.
Least Privilege Trong Thực Tế
Tài khoản database:
-- ❌ XẤU: App dùng tài khoản superuser
-- Kẻ tấn công có thể DROP TABLE, CREATE USER, đọc pg_shadow
-- ✅ TỐT: App dùng tài khoản bị hạn chế
CREATE USER blog_app WITH PASSWORD 'secure_password';
GRANT SELECT, INSERT, UPDATE ON posts TO blog_app;
GRANT SELECT, INSERT ON comments TO blog_app;
GRANT SELECT ON users TO blog_app;
-- KHÔNG THỂ: DELETE posts, truy cập schema khác, tạo bảngQuyền file system:
# ❌ XẤU: App chạy với quyền root, đọc/ghi mọi nơi
# ✅ TỐT: App chạy với user riêng có quyền tối thiểu
useradd -r -s /bin/false blog_app
chown -R blog_app:blog_app /var/www/blog
chmod 750 /var/www/blog
chmod 640 /var/www/blog/config/database.ymlAPI keys và service accounts:
// ❌ XẤU: Một admin API key dùng ở mọi nơi
{
"stripe_key": "sk_live_admin_full_access_key"
}
// ✅ TỐT: Các key có phạm vi cho từng mục đích
{
"stripe_read_key": "rk_live_readonly_key", // cho báo cáo
"stripe_charge_key": "sk_live_charges_only" // cho thanh toán
}AWS IAM (Infrastructure):
// ❌ XẤU: Lambda function có AdministratorAccess
// ✅ TỐT: Lambda function có chính xác những gì cần
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": "arn:aws:s3:::my-blog-uploads/*"
}]
}Checklist Least Privilege
✅ Database users chỉ có các bảng và thao tác họ cần
✅ Application process chạy với user không phải root
✅ API keys và tokens có scope tối thiểu cần thiết
✅ Cloud IAM roles tuân theo least privilege
✅ Truy cập admin yêu cầu credentials riêng (không chỉ là role flag)
✅ Service-to-service calls dùng credentials riêng cho mỗi service
✅ Credentials được xoay định kỳ
Fail-Safe Defaults
Fail-safe defaults có nghĩa là khi có sự cố hoặc chưa có quyết định, hệ thống mặc định ở trạng thái an toàn nhất.
Ví Dụ Trong Code
❌ Fail-open (nguy hiểm):
function canAccessResource(user: User, resourceId: string): boolean {
try {
return checkPermission(user, resourceId);
} catch (error) {
// Lỗi trong kiểm tra quyền — giả định được phép!
return true; // ← SAI: mặc định là cấp quyền truy cập
}
}✅ Fail-secure (đúng):
function canAccessResource(user: User, resourceId: string): boolean {
try {
return checkPermission(user, resourceId);
} catch (error) {
logger.error('Permission check failed', { userId: user.id, resourceId, error });
return false; // ← ĐÚNG: mặc định là từ chối truy cập
}
}❌ Mặc định CORS quá rộng:
// Cho phép mọi origin — fail open
app.use(cors());✅ Mặc định CORS hạn chế:
app.use(cors({
origin: process.env.ALLOWED_ORIGINS?.split(',') ?? [],
credentials: true,
}));❌ Thiếu kiểm tra authorization:
// Nếu middleware thất bại, request đi qua
app.get('/admin/users', optionalAuth, listUsers);✅ Yêu cầu authorization rõ ràng:
// Phải pass auth, không có fallback
app.get('/admin/users', requireAuth, requireRole('admin'), listUsers);Zero Trust Architecture
Zero trust là cách tiếp cận giả định không có trust ngầm — ngay cả với các request từ bên trong mạng của bạn. Mọi request phải được xác thực và phân quyền, mọi lúc.
Mô hình bảo mật mạng cổ điển là "castle and moat": tin tưởng mọi thứ bên trong perimeter mạng, không tin gì bên ngoài. Zero trust thay thế bằng "không bao giờ tin, luôn luôn xác minh."
Nguyên Tắc Zero Trust
Zero Trust vs. Bảo Mật Perimeter
| Khía Cạnh | Bảo Mật Perimeter | Zero Trust |
|---|---|---|
| Mô hình trust | Tin tưởng bên trong mạng | Không tin gì mặc định |
| Kiểm soát truy cập | Dựa trên vị trí mạng | Dựa trên danh tính và ngữ cảnh |
| Lateral movement | Dễ dàng (bên trong được tin) | Khó (mỗi bước cần auth) |
| Remote work | Rủi ro (phức tạp VPN) | Tự nhiên (danh tính là perimeter) |
| Cloud | Khó triển khai | Phù hợp tự nhiên |
| Tác động vi phạm | Lớn (bên trong được tin) | Hạn chế (không có trust ngầm) |
Zero Trust Trong Ứng Dụng Web
Ngay cả không có cơ sở hạ tầng zero trust đầy đủ, bạn có thể áp dụng các nguyên tắc trong ứng dụng:
Xác minh mọi request rõ ràng:
// ❌ Tư duy perimeter: tin tưởng mạng nội bộ
app.get('/internal/admin', (req, res) => {
// "Endpoint này chỉ truy cập từ mạng nội bộ, nên an toàn"
return res.json(await getAllUsers());
});
// ✅ Zero trust: xác thực mọi request bất kể nguồn gốc
app.get('/internal/admin', requireServiceToken, requireScope('admin:read'), async (req, res) => {
return res.json(await getAllUsers());
});Giả định vi phạm trong thiết kế:
// Thiết kế như thể kẻ tấn công đã ở bên trong:
// - Mã hóa dữ liệu nhạy cảm ngay cả trong database của bạn
// - Dùng credentials riêng cho mỗi service
// - Log mọi truy cập vào tài nguyên nhạy cảm
// - Phân đoạn dữ liệu để một service bị xâm phạm không đọc được mọi thứ
const sensitiveField = encrypt(data.ssn, process.env.FIELD_ENCRYPTION_KEY);
await db.query(
'INSERT INTO users (email, ssn_encrypted) VALUES ($1, $2)',
[data.email, sensitiveField]
);Vòng Đời Phát Triển An Toàn (SDLC)
Bảo mật nên là một phần của mọi giai đoạn phát triển — không phải là phần bổ sung trước khi release.
Bảo Mật Trong Từng Giai Đoạn
Yêu Cầu Bảo Mật
Khi viết user stories, bao gồm yêu cầu bảo mật:
Tính năng: Đăng nhập user
Yêu cầu chức năng:
Là user, tôi muốn đăng nhập bằng email và mật khẩu.
Yêu cầu bảo mật:
- Mật khẩu phải được hash với bcrypt (cost factor ≥ 12)
- Rate limit: tối đa 5 lần thất bại mỗi 15 phút mỗi IP
- Khóa tài khoản sau 10 lần thất bại; yêu cầu xác minh email để mở khóa
- Log tất cả nỗ lực đăng nhập (thành công và thất bại) với IP và user agent
- Trả về "Thông tin đăng nhập không hợp lệ" cho cả email sai và mật khẩu sai
- Session token phải là random 128-bit, lưu server-side
- Session hết hạn sau 24 giờ không hoạt độngTriết Lý Kiểm Tra Đầu Vào
Không bao giờ tin đầu vào. Mọi dữ liệu đến từ bên ngoài ranh giới ứng dụng phải được validate trước khi sử dụng.
Nguồn đầu vào không tin cậy:
- HTTP request body
- Query parameters và URL parameters
- HTTP headers (bao gồm
Cookie,Authorization,User-Agent) - File uploads
- WebSocket messages
- Dữ liệu từ external APIs (ngay cả đối tác "tin cậy")
- Dữ liệu từ database của chính bạn (nếu được tạo từ user input)
// ❌ XẤU: Tin client
app.post('/posts', async (req, res) => {
const { title, content, authorId } = req.body;
await db.query('INSERT INTO posts ...', [title, content, authorId]);
});
// ✅ TỐT: Validate mọi thứ, override những gì client không nên kiểm soát
app.post('/posts', requireAuth, async (req, res) => {
const schema = z.object({
title: z.string().min(1).max(200).trim(),
content: z.string().min(1).max(50000),
// authorId đến từ session đã xác thực, KHÔNG phải từ request body
});
const { title, content } = schema.parse(req.body);
const authorId = req.user.id; // Từ auth middleware, không phải user input
await db.query('INSERT INTO posts ...', [title, content, authorId]);
});Mã Hóa Đầu Ra
Mã hóa đầu ra theo ngữ cảnh nó xuất hiện. Cùng một dữ liệu cần mã hóa khác nhau trong HTML, JavaScript, URLs và SQL.
// Ngữ cảnh HTML: mã hóa < > & " '
const safeHtml = escapeHtml(userInput);
// ví dụ: <script> trở thành <script>
// Ngữ cảnh URL: mã hóa ký tự đặc biệt
const safeUrl = encodeURIComponent(userInput);
// ví dụ: "hello world" trở thành "hello%20world"
// Ngữ cảnh JavaScript: mã hóa cho JS strings
const safeJs = JSON.stringify(userInput);
// Escape đúng cách dấu ngoặc kép và ký tự đặc biệt
// Ngữ cảnh SQL: dùng parameterized queries (không bao giờ nối chuỗi)
await db.query('SELECT * FROM users WHERE email = $1', [userInput]);Kết Hợp Tất Cả: Ví Dụ Thực Tế
Hãy áp dụng tất cả các khái niệm này cho một tính năng thực: hệ thống comment của user trên blog.
Threat Modeling Hệ Thống Comment
Chúng ta đang xây dựng gì?
- User gửi comment trên bài viết blog
- Comment được lưu trong database
- Comment được hiển thị cho tất cả khách
Điều gì có thể xảy ra sai? (STRIDE)
| Mối Đe Dọa | Loại | Rủi Ro |
|---|---|---|
| Kẻ tấn công đăng XSS payload trong comment | Tampering + Info Disclosure | Tới Hạn |
| Bot spam làm ngập phần comment | DoS | Cao |
| User đăng comment với danh nghĩa người khác | Spoofing | Cao |
| Admin xóa comment, user chối bỏ điều đó đã xảy ra | Repudiation | Trung bình |
| IDOR: sửa/xóa comment của user khác | Elevation of Privilege | Cao |
Defense in depth cho comments:
Checklist triển khai:
- ✅ Rate limit gửi comment theo user
- ✅ Yêu cầu xác thực để đăng
- ✅ Validate độ dài comment (min 1, max 2000 ký tự)
- ✅ Sanitize HTML để chỉ cho phép các tag an toàn (ví dụ:
<b>,<i>) hoặc strip toàn bộ HTML - ✅ Lưu
author_idtừ session, không bao giờ từ request body - ✅ Kiểm tra ownership trước khi cho phép sửa/xóa
- ✅ Parameterize tất cả database queries
- ✅ HTML-encode nội dung comment khi render
- ✅ Log tất cả hành động comment với timestamp, user, IP
Lỗi Phổ Biến Của Người Mới Bắt Đầu
Lỗi 1: Validate Chỉ Trên Client
// ❌ Chỉ validate ở frontend
<input type="email" required maxlength="100" />
// ✅ Luôn validate ở server cũng vậy
app.post('/subscribe', (req, res) => {
const { email } = req.body;
if (!isValidEmail(email) || email.length > 100) {
return res.status(400).json({ error: 'Email không hợp lệ' });
}
// ...
});Validate phía client là UX. Validate phía server là bảo mật. Kẻ tấn công sẽ dùng curl hoặc Burp Suite để bypass hoàn toàn frontend của bạn.
Lỗi 2: Tin Tưởng Internal Services
// ❌ "Đây là internal microservice, không cần auth"
app.get('/internal/user/:id', (req, res) => {
return User.findById(req.params.id);
});
// ✅ Zero trust: xác thực mọi service-to-service call
app.get('/internal/user/:id', verifyServiceToken, (req, res) => {
return User.findById(req.params.id);
});Nếu mạng nội bộ của bạn bao giờ bị xâm phạm (lateral movement, cấu hình sai cloud, insider độc hại), các internal services không được bảo vệ trở thành mục tiêu dễ dàng.
Lỗi 3: Error Messages Nhiều Thông Tin
// ❌ Tiết lộ thông tin về hệ thống
try {
await loginUser(email, password);
} catch (error) {
if (error.code === 'USER_NOT_FOUND') {
res.status(401).json({ error: 'Không tìm thấy user' }); // Xác nhận user không tồn tại
} else if (error.code === 'WRONG_PASSWORD') {
res.status(401).json({ error: 'Sai mật khẩu' }); // Xác nhận user tồn tại
}
}
// ✅ Thông báo chung không tiết lộ gì
try {
await loginUser(email, password);
} catch (error) {
logger.warn('Đăng nhập thất bại', { email, reason: error.code }); // Log chi tiết nội bộ
res.status(401).json({ error: 'Thông tin đăng nhập không hợp lệ' }); // Chung cho client
}Lỗi 4: Bảo Mật Là Phần Thêm Vào Sau
Tuần 1-3: Xây dựng tính năng
Tuần 4: "Hãy thêm bảo mật"
Tuần 5: Launch (bảo mật làm một phần)Bảo mật được gắn vào cuối luôn không đầy đủ. Tích hợp từ đầu — mỗi user story nên có yêu cầu bảo mật, mỗi PR nên bao gồm checklist security review.
Checklist Bảo Mật Cho Mọi Tính Năng
Trước khi đánh dấu bất kỳ tính năng nào là "hoàn thành," xác minh:
Authentication & Authorization:
✅ Mọi endpoint có kiểm tra auth phù hợp
✅ Authorization được kiểm tra ở tầng data, không chỉ routing
✅ User chỉ có thể truy cập dữ liệu của mình (hoặc dữ liệu được chia sẻ rõ ràng)
Đầu Vào & Đầu Ra:
✅ Tất cả user input được validate ở server
✅ Đầu ra được mã hóa theo ngữ cảnh (HTML, JSON, URL)
✅ Error messages không tiết lộ chi tiết nội bộ
Bảo Mật Dữ Liệu:
✅ Dữ liệu nhạy cảm không được log
✅ Database queries dùng parameterized statements
✅ Các trường nhạy cảm được mã hóa at rest
Availability:
✅ Rate limiting được áp dụng cho các endpoint thay đổi state
✅ Giới hạn kích thước file upload được thực thi
✅ Query timeouts được cấu hình
Observability:
✅ Các hành động liên quan bảo mật được log (login, logout, permission denied, truy cập data)
✅ Logs không chứa dữ liệu nhạy cảm (mật khẩu, tokens, PII)
Tóm Tắt và Bài Học Chính
✅ CIA Triad (Confidentiality, Integrity, Availability) là nền tảng của mọi quyết định bảo mật
✅ Threat modeling với STRIDE giúp bạn tìm hệ thống những gì có thể xảy ra sai trước khi xây dựng
✅ Attack surface là mọi thứ kẻ tấn công có thể tiếp cận — giảm thiểu mạnh mẽ
✅ Defense in depth có nghĩa là nhiều lớp độc lập — không có điểm lỗi đơn
✅ Nguyên tắc least privilege giới hạn blast radius khi có điều gì đó bị xâm phạm
✅ Fail-safe defaults có nghĩa là hệ thống mặc định ở trạng thái an toàn khi không chắc chắn
✅ Zero trust có nghĩa là không bao giờ tin ngầm — xác thực và phân quyền mọi request
✅ Validate tất cả input phía server — validate phía client là UX, không phải bảo mật
✅ Bảo mật thuộc về mọi giai đoạn phát triển — không phải là bước cuối cùng
Tiếp Theo
Bạn đã xây dựng tư duy bảo mật của mình. Bây giờ hãy xem các lỗ hổng cụ thể ảnh hưởng đến ứng dụng thực tế:
Tiếp theo: Giai Đoạn 2: OWASP Top 10 Giải Thích — Tất cả 10 loại lỗ hổng, cách chúng hoạt động và cách ngăn chặn.
Bài Viết Liên Quan
- Lộ Trình Học Bảo Mật Web — Tổng quan toàn bộ series
- Hướng Dẫn Toàn Diện Giao Thức HTTP — HTTPS, headers và cookies
- Spring Boot JWT Authentication — Triển khai auth trong Java
- FastAPI Authentication — JWT & OAuth2 trong Python
Đây là Phần 2 của Lộ Trình Học Bảo Mật Web. Bắt đầu từ đầu hoặc nhảy đến chủ đề bạn quan tâm.
📬 Đăng ký nhận bản tin
Nhận những bài viết mới nhất qua email mỗi tuần. Không spam, hủy đăng ký bất cứ lúc nào.
Chúng tôi tôn trọng quyền riêng tư của bạn. Hủy đăng ký bất cứ lúc nào.
💬 Comments
Đăng nhập để bình luận
Chúng tôi sẽ không đăng bất cứ điều gì mà không có sự cho phép của bạn.