Business Logic Flaw: Thứ khiến nhiều hệ thống “không bị hack” nhưng vẫn mất tiền
Mình từng tham gia review flow redeem voucher của một hệ thống khá lớn.
Nhìn qua thì mọi thứ có vẻ ổn:
- Authentication đầy đủ
- Rate limit có
- Input validation rõ ràng
- Logging chi tiết
- WAF cũng đang chạy
- QA test pass sạch
Flow đại khái như sau:
- User nhập mã voucher
- Backend check voucher còn hạn không
- Check user có đủ điều kiện dùng không
- Mark voucher thành “đã dùng”
- Trả kết quả
Rất bình thường. Nhưng khi đào sâu hơn, mình phát hiện có một khoảng trống nhỏ giữa bước validate và bước update trạng thái voucher. Khoảng đó chỉ vài trăm mili giây.
Bình thường chẳng sao. Nhưng nếu ai đó spam nhiều request song song đúng thời điểm, rất nhiều request sẽ cùng pass validation trước khi voucher bị mark là used.
Kết quả: một voucher dùng được nhiều lần.
Không cần SQL Injection, không cần bypass auth, không cần payload lạ. Chỉ cần hiểu business flow là đủ gây thiệt hại.
Business Logic Flaw thực ra là gì?
Đó là khi hệ thống cho phép user làm một việc mà business không hề muốn cho phép, dù mọi request đều hợp lệ về mặt kỹ thuật.
User vẫn login bình thường, request đúng format, đúng endpoint, không có payload đáng ngờ. Nhưng logic kinh doanh bị hở.
Nó không phải lỗi code crash, không phải memory corruption. Nó là:
- Flow bị hở
- Assumption về thứ tự xử lý sai
- State không được quản lý chặt
- Hoặc nhiều service hiểu business rule khác nhau
Tại sao loại bug này khó phát hiện?
Vì nó không nhìn giống bug.
Khi test từng request riêng lẻ, mọi thứ đều đúng. Scanner, WAF, SAST, DAST… gần như vô dụng vì chúng không hiểu ngữ nghĩa business.
Vấn đề thường nằm ở:
- Timing
- Concurrency
- Retry mechanism
- Cache inconsistency
- Event replay
- Partial failure trong async flow
Những thứ này chỉ lộ ra khi hệ thống chạy thật dưới tải production.
Những thứ dev hay gặp
Hầu hết feature ban đầu đều được viết theo happy path:
- User bấm một lần
- Network ổn
- Queue xử lý đúng thứ tự
- Webhook tới đúng lúc
Production thì khác hoàn toàn.
Double click thanh toán, retry khi timeout, mobile resend request vì mạng chập chờn, queue chậm, webhook trễ, event bị replay… Từng cái một thì nhỏ, ghép lại là thành thảm họa.
Case đau nhất mình từng gặp
Một hệ thống wallet tự tin implement idempotency bằng request_id. Mỗi request transfer đều có id riêng, nếu trùng thì bỏ qua.
Implementation dùng Redis cache với TTL. Nghe rất hợp lý.
Đến khi traffic tăng mạnh:
- Queue xử lý chậm
- Transaction kéo dài
- Redis key expire sớm hơn dự kiến
Kết quả: cùng một lệnh chuyển tiền được xử lý nhiều lần.
Không có attacker nào “thông minh”. Chỉ là hệ thống tự phá logic của chính nó dưới áp lực production.
Business Logic Flaw hay xuất hiện ở đâu?
- Payment & Wallet
- Voucher & Promotion
- Loyalty point / Reward
- Referral program
- Booking & Inventory
- Flash sale
- Game economy
- Subscription
Nơi nào có tiền, tài nguyên, lợi ích, ranking thì sẽ có người thử abuse.
Các dạng phổ biến
1. Race Condition ở business layer
Classic nhất. Voucher một lần dùng, nhưng nhiều request cùng pass bước check “chưa dùng” trước khi update trạng thái.
Mình từng chứng kiến flash sale bị redeem gấp nhiều lần số lượng cho phép chỉ vì race condition.
2. State transition không chặt
Entity có lifecycle: Pending → Paid → Processing → Delivered.
Nhưng API cho phép update status khá tự do → dễ bị nhảy cóc trạng thái.
Microservice càng dễ dính vì mỗi service chỉ biết một phần flow.
3. Authorization chỉ đúng một nửa
Check user đã login, nhưng quên check “order này có phải của user đó không”.
POST /api/order/cancel với orderId của người khác — lỗi ngu ngốc nhưng cực kỳ phổ biến.
4. Arithmetic & Balance issue
- Quantity âm
- Refund vượt quá số dư
- Rounding error
- Precision khi convert currency
Có hệ thống loyalty từng bị farm point nhờ rounding xuống. Lệch mỗi lần rất nhỏ, nhưng farm hàng nghìn lần thì thành tiền lớn.
AI đang làm vấn đề này tệ hơn
AI code CRUD và validation rất nhanh, sạch. Nhưng nó gần như không tự nghĩ đến:
- Concurrent request thì sao?
- Retry và replay thì sao?
- Webhook tới trễ thì sao?
- Queue duplicate message thì sao?
Nó optimize cho “chạy được”, chứ chưa optimize cho “khó bị abuse”.
Làm sao để giảm?
Không phải thêm WAF hay scanner.
Cách hiệu quả nhất là:
- Threat modeling và abuse case review khi design
- Senior engineer review business flow + state machine
- Viết rõ business invariants và enforce ở một nơi
- Dùng strong consistency cho phần quan trọng (payment, voucher, balance)
- Implement idempotency đúng cách (DB unique constraint + outbox pattern thay vì chỉ Redis TTL)
- Monitoring anomaly (spike redeem, lạ state transition…)
Và quan trọng nhất: luôn tự hỏi khi review feature:
“Nếu mình là thằng muốn phá flow này, mình sẽ làm gì?”
Câu hỏi đó thường giúp phát hiện vấn đề nhanh hơn bất kỳ tool nào.
Business Logic Flaw là loại bug làm mờ ranh giới giữa backend engineering, distributed system, product sense và security.
Người fix tốt nó không phải người biết nhiều payload nhất, mà là người hiểu rõ business, hiểu state, hiểu production thật sự hoạt động ra sao, và hiểu con người sẽ làm gì khi có đủ incentive.
Vì cuối cùng, nhiều hệ thống không bị “hack” — chúng chỉ bị sử dụng đúng theo những gì code cho phép.
All rights reserved