Tôi từng nghĩ bảo mật là việc của mấy hệ thống ngân hàng hoặc app xử lý tiền.
App của tôi chỉ là một sản phẩm phục vụ người dùng thông thường, không thẻ tín dụng, không giao dịch tài chính — thì có gì mà không an toàn?
Cho đến ngày tôi nhận được file báo cáo pentest.
Gần 30 trang PDF.
Pentest không chỉ tìm bug – nó tìm thói quen xấu
Điều làm tôi sốc không phải là số lượng lỗi, mà là:
Hầu hết các lỗi đều đến từ những thứ tôi làm vì… tiện.
- Lưu token để khỏi phải login lại
- Cache API response cho app mượt hơn
- Để client xử lý một phần logic cho nhanh
Tất cả đều “hợp lý” — cho đến khi nhìn từ góc độ attacker.

1. Insecure Local Storage – lỗi mà app mobile nào cũng từng dính
Pentest phát hiện gì?
- Access token
- User ID
được lưu trong:
- AsyncStorage (React Native)
- Cache database trên iOS
- SharedPreferences trên Android
Tất cả đều plain text.
“Nhưng user đâu có jailbreak?”
Pentest không quan tâm điều đó.
Họ chỉ cần khả năng, không cần xác suất cao.
- Root / jailbreak
- Backup iTunes
- adb pull
→ Data nằm đó, đọc được.
Tôi đã fix như thế nào?
- Tách data thành 3 nhóm:
- Sensitive: token, credential → Secure Storage
- Semi-sensitive: user profile → encrypt
- Public: config, UI state → cache bình thường
- React Native:
- Dùng
react-native-encrypted-storage
- Dùng
- Nguyên tắc mới:
Không có lý do gì để token nằm trong AsyncStorage
2. Cache – thứ giết bảo mật trong im lặng
Cache là con dao hai lưỡi.
Vấn đề pentest chỉ ra
- Cache vẫn tồn tại sau khi logout
- App mở lại vẫn đọc được data cũ
- Một số API response chứa thông tin nhạy cảm
Về UX: ổn
Về security: thảm họa
Tôi đã thay đổi tư duy
- Logout không còn là:
- clear token
- Mà là:
- clear cache
- reset persisted state
- invalidate session backend
Nếu user logout mà data còn ở lại, thì logout chỉ là giả
3. Authentication không đủ – phải có Authorization
Một lỗi mà dev backend hay tự tin nhầm.
Trước đây
- Check token hợp lệ
- Tin rằng client gửi đúng ID
Pentest làm gì?
- Dùng token hợp lệ
- Gửi ID của user khác
- Và… server trả data thật
Fix
- Backend không bao giờ tin client
- Mọi query đều gắn với
request.user
Không còn:
get(id=xxx)
Mà là:
get(id=xxx, user=request.user)
Bài học:
Token chỉ chứng minh bạn là ai, không chứng minh bạn được phép làm gì
4. Obfuscation không phải phép màu
Tôi từng bật Proguard / R8 và nghĩ:
“OK, reverse chắc khó lắm rồi”
Pentest chứng minh điều ngược lại.
Họ vẫn:
- Đọc được flow logic
- Hiểu rule xử lý
- Phát hiện hard-code
Tôi làm gì?
- Đưa toàn bộ logic quan trọng về backend
- Client chỉ còn:
- hiển thị
- gửi request
- Không hard-code:
- secret
- rule đặc biệt
- flag quan trọng
Client chỉ nên là người đưa thư, không phải người quyết định
5. File, video, binary – nơi bảo mật hay bị bỏ quên
Vấn đề
- API trả URL trực tiếp
- Không check quyền download
- File có thể share tự do
Fix
- API trả binary stream
- Verify token trước khi trả
- Token có expiry ngắn
- Không public URL
Pentest comment một câu rất gắt:
“Any downloadable resource is data.”
6. Sau pentest, tôi mất gì và được gì?
Tôi mất:
- Thời gian
- Một ít performance
- Code phức tạp hơn
Tôi được:
- Tư duy bảo mật đúng
- Kiến trúc sạch hơn
- Ít phụ thuộc client
- Và quan trọng nhất: yên tâm
Kết luận
Pentest không làm app tôi hoàn hảo.
Nhưng nó bóc trần những giả định sai lầm mà tôi tin suốt thời gian dài.
https://www.cloudflare.com/learning/security/glossary/what-is-penetration-testing
