Bảo mật di động là một tập hợp các biện pháp bảo vệ ứng dụng, dữ liệu người dùng và cơ sở hạ tầng máy chủ khỏi các cuộc tấn công và rò rỉ. Theo OWASP Mobile Top 10 (2024), lưu trữ dữ liệu không an toàn vẫn là lỗ hổng phổ biến nhất trong các ứng dụng di động. Trong bài viết này, chúng ta sẽ tìm hiểu các mối đe dọa chính, phương pháp mã hóa, lưu trữ an toàn, xác thực và bảo vệ mã — tất cả những gì một nhà phát triển mới bắt đầu cần biết.
Những Điểm Chính
OWASP (Open Web Application Security Project) là một tổ chức phi lợi nhuận công bố bảng xếp hạng các lỗ hổng bảo mật di động nguy hiểm nhất. OWASP Mobile Top 10 là danh sách giúp các nhà phát triển hiểu được cần tập trung vào điều gì trước tiên. Trong phiên bản 2024, các vấn đề liên quan đến lưu trữ không an toàn, xác thực yếu và giao tiếp mạng không an toàn dẫn đầu bảng xếp hạng.
M1: Lưu trữ dữ liệu không an toàn — vấn đề phổ biến nhất: mật khẩu, token và dữ liệu cá nhân vẫn còn trong SharedPreferences, NSUserDefaults hoặc tệp cục bộ mà không được mã hóa. M2: Xác thực yếu — thiếu xác minh phía máy chủ, mật khẩu yếu. M3: Giao tiếp mạng không an toàn — thiếu HTTPS hoặc xác minh chứng chỉ SSL không chính xác. M4 và M5 liên quan đến mật mã học và sử dụng API không đúng cách.
M6: Ủy quyền không an toàn — người dùng có thể truy cập dữ liệu của người dùng khác bằng cách thay thế ID trong yêu cầu. M7: Chèn mã (SQL Injection, XSS). M8: Thao tác ứng dụng — đóng gói lại, thay thế mã. M9 và M10 — rò rỉ dữ liệu qua thư viện bên thứ ba và dịch ngược. Đối với mỗi mối đe dọa này, đều có các biện pháp đối phó đã được kiểm chứng và tại IT Sectr, chúng tôi đã áp dụng chúng trong tất cả các dự án từ năm 2017.
Tấn công MITM xảy ra khi kẻ tấn công chặn lưu lượng truy cập giữa ứng dụng và máy chủ. Điều này có thể thực hiện thông qua giả mạo DNS, ARP spoofing hoặc kết nối với mạng Wi-Fi không được bảo vệ. Chứng chỉ SSL/TLS và Certificate Pinning được sử dụng để bảo vệ.
Certificate Pinning là cơ chế trong đó ứng dụng xác minh rằng chứng chỉ máy chủ khớp với chứng chỉ được lưu trữ trước trong mã ứng dụng. Ngay cả khi kẻ tấn công thay thế chứng chỉ thông qua proxy (ví dụ: Burp Suite), ứng dụng sẽ từ chối kết nối. Pinning có hai loại: Public Key Pinning và Certificate Hash Pinning.
AES (Advanced Encryption Standard) là thuật toán mã hóa đối xứng, nền tảng của bảo mật dữ liệu trên thiết bị. AES sử dụng cùng một khóa để mã hóa và giải mã dữ liệu. AES hỗ trợ khóa 128, 192 hoặc 256 bit. Trong phát triển di động, AES-256 được sử dụng để mã hóa dữ liệu trên thiết bị: tệp, bộ nhớ đệm, bản ghi trong cơ sở dữ liệu cục bộ.
Các chế độ AES: GCM (khuyến nghị) — cung cấp xác thực dữ liệu, CBC — chế độ cơ bản với chuỗi khối, ECB — không an toàn, không sử dụng. Đối với iOS, AES có sẵn thông qua CommonCrypto (CCOptions), đối với Android — thông qua Cipher trong Java Cryptography Architecture (JCA). Quan trọng: khóa mã hóa không bao giờ được lưu trữ trong mã ứng dụng — hãy sử dụng Keychain/Keystore.
Mã hóa bất đối xứng: RSA — sử dụng một cặp khóa (công khai và riêng tư). RSA được sử dụng để mã hóa một lượng nhỏ dữ liệu — thường là để trao đổi khóa đối xứng giữa máy khách và máy chủ. Độ dài khóa RSA tối thiểu là 2048 bit (khuyến nghị 4096). Trên iOS, RSA có sẵn thông qua Security Framework (SecKeyCreateRandomKey), trên Android — thông qua KeyPairGenerator trong Android Keystore.
Băm (SHA-256, SHA-3) là phép biến đổi dữ liệu không thể đảo ngược thành chuỗi có độ dài cố định. Băm được sử dụng để xác minh tính toàn vẹn dữ liệu và lưu trữ mật khẩu. Đối với mật khẩu, nhất định phải sử dụng bcrypt, scrypt hoặc Argon2 — SHA-256 thông thường dễ bị tấn công bằng bảng rainbow. SSL/TLS là giao thức mã hóa lưu lượng mạng giữa máy khách và máy chủ. Tiêu chuẩn hiện đại là TLS 1.3, cung cấp Perfect Forward Secrecy (PFS).
TLS 1.3 nhanh hơn các phiên bản tiền nhiệm: bắt tay chỉ mất một vòng thay vì hai. Trên Android, phiên bản TLS tối thiểu được cấu hình thông qua SSLSocket, trên iOS — thông qua ATS (App Transport Security), mặc định yêu cầu TLS 1.2 trở lên. ATS chỉ có thể bị vô hiệu hóa cho các miền cụ thể với lý do chính đáng.
Keychain (Chùm khóa) là kho lưu trữ an toàn trong iOS / macOS dành cho mật khẩu, khóa mã hóa, chứng chỉ và token. Dữ liệu trong Keychain được mã hóa bằng khóa phần cứng duy nhất cho mỗi thiết bị. Quyền truy cập vào Keychain được kiểm soát thông qua Security Framework (SecItemAdd, SecItemCopyMatching). Keychain tự động khóa khi thiết bị bị khóa và được mã hóa bằng Secure Enclave.
Android Keystore là kho lưu trữ hệ thống cho các khóa mật mã, được cách ly khỏi ứng dụng. Bắt đầu từ Android 6.0 (API 23), Keystore sử dụng hỗ trợ phần cứng (TEE — Trusted Execution Environment) trên các thiết bị có chip bảo mật. Các khóa trong Keystore không bao giờ rời khỏi khu vực bảo mật — ứng dụng chỉ nhận được một handle cho các hoạt động mã hóa và ký.
| Tham số | iOS Keychain | Android Keystore |
|---|---|---|
| Loại dữ liệu lưu trữ | Mật khẩu, token, khóa, chứng chỉ | Khóa mật mã |
| Hỗ trợ phần cứng | Secure Enclave (tất cả iPhone có A7+) | TEE (Android 6+, tùy thuộc vào chip) |
| Mã hóa | AES-256 phần cứng | AES/GCM với khóa phần cứng |
| Sinh trắc học | Face ID / Touch ID để truy cập | BiometricPrompt để truy cập |
| iCloud / sao lưu | Đồng bộ qua iCloud Keychain | Không đồng bộ với đám mây |
| Hiệu suất | Chậm hơn (mã hóa phần cứng) | Nhanh hơn (TEE) |
SharedPreferences và NSUserDefaults không được thiết kế để lưu trữ dữ liệu nhạy cảm — chúng lưu trữ thông tin ở dạng văn bản thuần túy. Để bảo vệ dữ liệu, hãy sử dụng EncryptedSharedPreferences (Android) hoặc mã hóa dữ liệu trước khi lưu vào UserDefaults (iOS). Tại IT Sectr, chúng tôi luôn sử dụng Keychain và Keystore cho token truy cập và mật khẩu.
OAuth 2.0 là giao thức ủy quyền được ủy thác, cung cấp quyền truy cập an toàn vào tài nguyên người dùng mà không truyền mật khẩu. Trong các ứng dụng di động, Authorization Code Flow với PKCE (Proof Key for Code Exchange) được sử dụng phổ biến nhất. PKCE ngăn chặn việc chặn mã ủy quyền — một yêu cầu bắt buộc đối với ứng dụng di động.
OpenID Connect (OIDC) là phần mở rộng trên OAuth 2.0 để xác thực người dùng. OIDC thêm ID Token ở định dạng JWT chứa thông tin người dùng (tên, email, id). Luồng OAuth 2.0 + OIDC bao gồm: chuyển hướng người dùng đến trang đăng nhập, nhận mã ủy quyền, trao đổi mã lấy token (access + refresh + id), sử dụng token truy cập cho các yêu cầu API.
JWT (JSON Web Token) là định dạng token nhỏ gọn, an toàn cho URL chứa các claim ở định dạng JSON. JWT bao gồm ba phần: tiêu đề (loại và thuật toán ký), tải trọng (dữ liệu) và chữ ký. Token truy cập là token ngắn hạn (15–60 phút) để truy cập API. Token làm mới là token dài hạn (ngày/tuần) để lấy token truy cập mới mà không cần đăng nhập lại.
Token phiên là phương pháp truyền thống trong đó máy chủ lưu trữ phiên trong cơ sở dữ liệu hoặc Redis và máy khách nhận được một định danh ngẫu nhiên. Trong phát triển di động, JWT được ưa chuộng hơn: nó không yêu cầu lưu trữ phiên phía máy chủ, chứa tất cả thông tin bên trong và dễ dàng xác minh. Tuy nhiên, JWT không thể bị thu hồi ngay lập tức — đây là sự đánh đổi được giải quyết bằng thời gian sống ngắn của token truy cập và việc sử dụng token làm mới.
Face ID và Touch ID trên iOS, Xác thực vân tay trên Android — các phương pháp xác thực sinh trắc học sử dụng các đặc điểm vật lý độc đáo của người dùng. Trên iOS, sinh trắc học hoạt động thông qua LocalAuthentication (LAContext), trên Android — thông qua BiometricPrompt (Android 9+) hoặc FingerprintManager (không được khuyến khích). Sinh trắc học được sử dụng để mở khóa ứng dụng, xác nhận thanh toán và truy cập dữ liệu được bảo vệ.
Các sắc thái quan trọng: sinh trắc học là UX tiện lợi, nhưng không phải là sự thay thế cho xác thực máy chủ. Sau khi xác minh sinh trắc học thành công, ứng dụng nên lấy token truy cập từ máy chủ. Trên Android, hãy đảm bảo kiểm tra thiết bị sử dụng sinh trắc học Loại 3 (Mạnh), không chỉ nhận dạng khuôn mặt dựa trên camera (Loại 1).
ProGuard là công cụ làm rối mã, nén và tối ưu hóa bytecode Java cho Android, tăng cường bảo mật mã chống lại dịch ngược. R8 là phiên bản kế nhiệm, được tích hợp trong Gradle từ Android Studio 3.4. R8 thực hiện bốn nhiệm vụ: nén (loại bỏ các lớp và phương thức không sử dụng), tối ưu hóa (nội tuyến phương thức, đơn giản hóa mã), làm rối mã (đổi tên lớp và phương thức thành tên ngắn) và tiền xác minh (kiểm tra bytecode).
DexGuard là phiên bản thương mại của ProGuard với bảo vệ nâng cao: mã hóa chuỗi, làm rối tài nguyên, bảo vệ chống đóng gói lại, kiểm soát tính toàn vẹn APK. Đối với hầu hết các dự án, R8 là đủ, nhưng đối với các ứng dụng tài chính và ngân hàng, DexGuard cung cấp thêm một lớp bảo mật. R8 được kích hoạt thông qua build.gradle: minifyEnabled = true và proguardFiles.
Root Detection (Android) và Jailbreak Detection (iOS) là các cơ chế kiểm tra xem quyền siêu người dùng đã được cấp trên thiết bị hay chưa. Trên các thiết bị bị xâm phạm, có thể đọc bộ nhớ tiến trình, chặn lưu lượng và thay thế mã. Để kiểm tra trên Android, sử dụng sự hiện diện của tệp nhị phân SU, khóa chữ ký thử nghiệm và các cờ xây dựng không tiêu chuẩn.
RASP (Runtime Application Self-Protection) là công nghệ bảo vệ ứng dụng trong thời gian thực thi. RASP phát hiện các nỗ lực gỡ lỗi, đóng gói lại, chèn mã và kết thúc ứng dụng khi phát hiện mối đe dọa. Ví dụ về giải pháp RASP: Dexter, Guardsquare, Promon. RASP hoạt động trong thời gian chạy và phản ứng với các bất thường — không giống như làm rối mã tĩnh, bảo vệ mã trước khi thực thi.
Dịch ngược là quá trình khôi phục mã nguồn từ ứng dụng đã được biên dịch. Công cụ: JADX (trình dịch ngược APK), Ghidra, IDA Pro, Hopper. Bảo vệ chống Dịch ngược là sự kết hợp giữa làm rối mã, mã hóa chuỗi, kiểm tra tính toàn vẹn và Root Detection. Không có biện pháp bảo vệ hoàn toàn — mục tiêu là làm cho việc dịch ngược đủ tốn kém đối với kẻ tấn công.
Câu Hỏi Thường Gặp
Bắt đầu với OWASP Mobile Top 10 — đây là lộ trình về các lỗ hổng phổ biến nhất. Sau đó, hãy nghiên cứu HTTPS và chứng chỉ SSL, cấu hình Certificate Pinning và chuyển sang lưu trữ an toàn qua Keychain / Keystore.
AES (đối xứng) — một khóa để mã hóa và giải mã, nhanh, phù hợp với khối lượng dữ liệu lớn. RSA (bất đối xứng) — một cặp khóa (công khai và riêng tư), chậm hơn, được sử dụng để trao đổi khóa đối xứng.
Bạn chỉ nên mã hóa dữ liệu bí mật: mật khẩu, token, dữ liệu cá nhân người dùng, thông tin thanh toán. Hình ảnh, văn bản và cài đặt giao diện không yêu cầu mã hóa — điều này sẽ làm tăng kích thước và làm chậm ứng dụng.
Token làm mới là token dài hạn cho phép lấy Token truy cập mới mà không cần nhập lại mật khẩu. Điều này tăng cường bảo mật — Token truy cập có thời gian sống 15–60 phút và ngay cả khi bị rò rỉ, kẻ tấn công cũng không thể sử dụng nó lâu dài.
Có, R8 phải được bật cho các bản dựng phát hành Android. Đây không chỉ là bảo vệ chống Dịch ngược mà còn giảm kích thước APK và tối ưu hóa hiệu suất. Nếu không có R8, mã của bạn có thể được dịch ngược thành dạng có thể đọc được chỉ bằng một lệnh JADX.
Tổng Kết
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.