Danh sách kiểm tra lưu trữ an toàn Kotlin cho token, khóa và PII
Danh sách kiểm tra lưu trữ an toàn Kotlin để chọn giữa Android Keystore, EncryptedSharedPreferences và mã hóa cơ sở dữ liệu cho token, khóa và PII.

Những gì bạn đang cố gắng bảo vệ (nói rõ ràng)
Lưu trữ an toàn trong một ứng dụng doanh nghiệp có một mục tiêu: nếu ai đó lấy được điện thoại (hoặc file ứng dụng của bạn), họ vẫn không nên đọc hay tái sử dụng những gì bạn lưu. Điều đó bao gồm dữ liệu ở trạng thái nghỉ (trên đĩa) và cả các bí mật rò rỉ qua bản sao lưu, logs, báo cáo sự cố, hoặc công cụ gỡ lỗi.
Một bài kiểm tra đơn giản trong đầu: một người lạ có thể làm gì nếu họ mở thư mục lưu trữ của app bạn? Trong nhiều ứng dụng, thứ giá trị nhất không phải là ảnh hay cài đặt. Là những chuỗi nhỏ mở khóa truy cập.
Lưu trữ trên thiết bị thường bao gồm token phiên (để người dùng duy trì đăng nhập), refresh token, API key, khóa mã hóa, dữ liệu cá nhân (PII) như tên và email, và các bản ghi nghiệp vụ được cache để dùng ngoại tuyến (đơn hàng, vé, ghi chú khách hàng).
Dưới đây là các chế độ thất bại thực tế thường gặp:
- Thiết bị bị mất hoặc bị đánh cắp, token bị sao chép để mạo danh người dùng.
- Malware hoặc một app "hỗ trợ" đọc file cục bộ trên thiết bị đã root hoặc qua mánh lới truy cập trợ năng.
- Bản sao lưu tự động chuyển dữ liệu app của bạn đến nơi bạn không mong muốn.
- Bản dựng debug ghi token vào log, gửi vào báo cáo sự cố, hoặc tắt các kiểm tra bảo mật.
Đó là lý do vì sao "chỉ lưu vào SharedPreferences" không ổn cho bất cứ thứ gì cấp quyền truy cập (token) hoặc có thể gây hại cho người dùng và công ty (PII). Plain SharedPreferences giống như viết bí mật lên một mảnh giấy nhớ trong app: tiện lợi nhưng dễ đọc nếu ai đó có cơ hội.
Điểm bắt đầu hữu ích nhất là đặt tên cho từng mục lưu và hỏi hai câu: nó có mở khóa thứ gì không, và có vấn đề gì nếu nó bị công khai? Phần còn lại (Keystore, encrypted preferences, cơ sở dữ liệu mã hóa) sẽ theo sau.
Phân loại dữ liệu: token, khóa và PII
Lưu trữ an toàn dễ hơn khi bạn ngừng coi tất cả "dữ liệu nhạy cảm" như nhau. Bắt đầu bằng việc liệt kê những gì app lưu và điều gì xảy ra nếu nó bị lộ.
Tokens không giống mật khẩu. Access token và refresh token được lưu để người dùng giữ phiên đăng nhập, nhưng chúng vẫn là bí mật giá trị cao. Mật khẩu không nên lưu lại. Nếu cần đăng nhập, chỉ lưu những gì cần để duy trì phiên (thường là token) và dựa vào server để kiểm tra mật khẩu.
Khóa là một lớp khác. API key, khóa ký và khóa mã hóa có thể mở cả hệ thống, không chỉ một tài khoản người dùng. Nếu ai đó trích xuất chúng từ thiết bị, họ có thể tự động hóa lạm dụng ở quy mô lớn. Một quy tắc tốt: nếu một giá trị có thể được dùng bên ngoài app để mạo danh app hoặc giải mã dữ liệu, hãy coi nó rủi ro cao hơn token người dùng.
PII là bất cứ thứ gì có thể xác định một người: email, điện thoại, địa chỉ nhà, ghi chú khách hàng, giấy tờ tuỳ thân, dữ liệu liên quan đến sức khỏe. Ngay cả các trường trông vô hại cũng trở nên nhạy cảm khi kết hợp với nhau.
Một hệ thống gắn nhãn nhanh hoạt động tốt:
- Session secrets: access token, refresh token, session cookie
- App secrets: API keys, signing keys, encryption keys (cố gắng tránh đặt những thứ này trên thiết bị khi có thể)
- User data (PII): thông tin hồ sơ, định danh, tài liệu, thông tin y tế hoặc tài chính
- Device and analytics IDs: advertising ID, device ID, install ID (vẫn nhạy cảm theo nhiều chính sách)
Android Keystore: khi nào dùng
Android Keystore phù hợp khi bạn cần bảo vệ những bí mật không bao giờ được rời thiết bị ở dạng thuần. Nó là két an toàn cho khóa mật mã, không phải cơ sở dữ liệu cho dữ liệu thực tế của bạn.
Nó tốt ở chỗ: tạo và giữ khóa dùng cho mã hóa, giải mã, ký hoặc xác minh. Thường bạn mã hóa token hoặc dữ liệu ngoại tuyến ở chỗ khác, và một khóa từ Keystore là thứ mở khoá cho nó.
Khóa có hỗ trợ phần cứng: thực chất là gì
Trên nhiều thiết bị, khóa trong Keystore có thể được hỗ trợ bằng phần cứng. Điều đó có nghĩa thao tác khóa diễn ra trong môi trường được bảo vệ và vật liệu khóa không thể bị trích xuất. Nó giảm rủi ro từ malware có thể đọc file app.
Hỗ trợ phần cứng không được đảm bảo trên mọi thiết bị, và hành vi khác nhau theo model và phiên bản Android. Hãy xây dựng như thể thao tác khóa có thể thất bại.
Rào cản xác thực người dùng
Keystore có thể yêu cầu sự hiện diện của người dùng trước khi khóa được sử dụng. Đó là cách bạn liên kết truy cập với sinh trắc học hoặc chứng chỉ thiết bị. Ví dụ, bạn có thể mã hóa một token xuất khẩu và chỉ giải mã nó sau khi người dùng xác nhận bằng vân tay hoặc PIN.
Keystore phù hợp mạnh khi bạn muốn khóa không thể xuất, khi bạn muốn sự chấp thuận bằng sinh trắc học hoặc chứng chỉ thiết bị cho các hành động nhạy cảm, và khi bạn muốn bí mật riêng từng thiết bị không đồng bộ hoặc không đi theo bản sao lưu.
Lên kế hoạch cho các cạm bẫy: khóa có thể bị vô hiệu hóa sau khi thay đổi màn hình khóa, thay đổi sinh trắc học, hoặc các sự kiện bảo mật. Dự kiến lỗi và triển khai phương án khôi phục sạch: phát hiện khóa không hợp lệ, xoá blob mã hóa, và yêu cầu người dùng đăng nhập lại.
EncryptedSharedPreferences: khi nào đủ
EncryptedSharedPreferences là lựa chọn mặc định tốt cho một vài bí mật dạng key-value nhỏ. Nó là "SharedPreferences, nhưng được mã hóa" nên người ta không thể chỉ mở file và đọc giá trị.
Ở bên trong, nó dùng một master key để mã hóa và giải mã giá trị. Master key đó được bảo vệ bởi Android Keystore, nên app của bạn không lưu khóa mã hóa thô ở dạng plaintext.
Nó thường đủ cho vài mục nhỏ bạn đọc thường xuyên, như access và refresh token, session ID, device ID, cờ môi trường, hoặc các mẩu trạng thái nhỏ như thời gian đồng bộ cuối. Nó cũng ổn cho mảnh dữ liệu người dùng rất nhỏ nếu bạn thực sự phải lưu, nhưng không nên biến nó thành nơi chứa PII.
Nó không phù hợp cho bất cứ thứ gì lớn hoặc có cấu trúc. Nếu bạn cần danh sách ngoại tuyến, tìm kiếm, hoặc truy vấn theo trường (khách hàng, vé, đơn hàng), EncryptedSharedPreferences sẽ chậm và vụn. Đó là lúc bạn cần cơ sở dữ liệu được mã hóa.
Một quy tắc đơn giản: nếu bạn có thể liệt kê mọi khóa lưu trên một màn hình, EncryptedSharedPreferences có thể ổn. Nếu bạn cần hàng và truy vấn, chuyển sang giải pháp khác.
Mã hóa cơ sở dữ liệu: khi nào cần
Mã hóa cơ sở dữ liệu quan trọng khi bạn lưu nhiều hơn vài cài đặt nhỏ. Nếu app giữ dữ liệu nghiệp vụ trên thiết bị, hãy giả định nó có thể bị trích xuất từ điện thoại bị mất trừ khi bạn bảo vệ nó.
Cơ sở dữ liệu là hợp lý khi bạn cần truy cập ngoại tuyến tới bản ghi, cache cục bộ để tăng hiệu suất, lịch sử/nhật ký, hoặc lưu ghi chú/đính kèm dài.
Hai cách tiếp cận mã hóa phổ biến
Mã hóa toàn bộ cơ sở dữ liệu (thường kiểu SQLCipher) mã hóa toàn bộ file ở trạng thái nghỉ. App của bạn mở nó bằng một khóa. Điều này dễ lý giải vì bạn không phải nhớ cột nào được bảo vệ.
Mã hóa ở lớp ứng dụng theo trường chỉ mã hóa những trường nhất định trước khi ghi, rồi giải mã sau khi đọc. Điều này có thể ổn nếu phần lớn bản ghi không nhạy cảm, hoặc bạn muốn giữ cấu trúc cơ sở dữ liệu hiện tại mà không đổi định dạng file.
Đánh đổi: bảo mật thông tin vs tìm kiếm và sắp xếp
Mã hóa toàn bộ che giấu mọi thứ trên đĩa, nhưng một khi cơ sở dữ liệu được mở khoá, app có thể truy vấn bình thường.
Mã hóa theo trường bảo vệ các cột cụ thể, nhưng bạn mất khả năng tìm kiếm và sắp xếp dễ dàng trên các giá trị đã mã hóa. Sắp xếp theo họ mã hóa không hoạt động đáng tin cậy, và tìm kiếm trở nên hoặc là "tìm sau khi giải mã" (chậm) hoặc "lưu chỉ mục phụ" (phức tạp hơn và có thể rò rỉ).
Cơ bản về quản lý khóa
Khóa cơ sở dữ liệu không bao giờ nên được hardcode hoặc đóng gói trong app. Một mẫu phổ biến là tạo một khóa cơ sở dữ liệu ngẫu nhiên, sau đó lưu nó ở dạng bọc (được mã hóa) bằng một khóa giữ trong Android Keystore. Khi logout, bạn có thể xóa khóa bọc và coi cơ sở dữ liệu cục bộ như có thể bị huỷ, hoặc giữ lại nếu app phải hoạt động ngoại tuyến giữa các phiên.
Cách chọn: so sánh thực tế
Bạn không chọn "tùy chọn an toàn nhất" nói chung. Bạn chọn phương án an toàn nhất phù hợp với cách app sử dụng dữ liệu.
Những câu hỏi thực sự quyết định lựa chọn đúng:
- Dữ liệu được đọc thường xuyên đến mức nào (mỗi lần khởi động hay hiếm)?
- Dữ liệu lớn bao nhiêu (vài byte hay hàng nghìn bản ghi)?
- Nếu nó bị lộ sẽ thế nào (khó chịu, tốn kém, phải báo cáo theo luật)?
- Bạn có cần truy cập ngoại tuyến, tìm kiếm, hay sắp xếp không?
- Bạn có yêu cầu tuân thủ (retention, audit, quy tắc mã hóa) không?
Một ánh xạ làm việc:
- Tokens (OAuth access và refresh token) thường thuộc về EncryptedSharedPreferences vì chúng nhỏ và đọc thường xuyên.
- Vật liệu khóa nên sống trong Android Keystore bất cứ khi nào có thể để giảm cơ hội bị sao chép khỏi thiết bị.
- PII và dữ liệu nghiệp vụ ngoại tuyến thường cần mã hóa cơ sở dữ liệu khi bạn lưu nhiều hơn vài trường hoặc cần danh sách ngoại tuyến và lọc.
Dữ liệu hỗn hợp là bình thường trong app doanh nghiệp. Một mẫu thực tế là tạo một Data Encryption Key (DEK) ngẫu nhiên cho cơ sở dữ liệu hoặc file cục bộ, chỉ lưu DEK đã bọc bằng một khóa Keystore, và quay vòng khi cần.
Nếu bạn không chắc, chọn con đường an toàn đơn giản hơn: lưu ít lại. Tránh PII ngoại tuyến nếu không thực sự cần, và giữ khóa trong Keystore.
Bước từng bước: triển khai lưu trữ an toàn trong app Kotlin
Bắt đầu bằng cách viết ra mọi giá trị bạn định lưu trên thiết bị và lý do chính xác nó phải ở đó. Đây là cách nhanh nhất để ngăn "lỡ lưu cho chắc".
Trước khi viết code, quyết định quy tắc của bạn: mỗi mục sống bao lâu, khi nào nên thay, và "logout" nghĩa là gì. Một access token có thể hết hạn trong 15 phút, refresh token có thể lâu hơn, và PII ngoại tuyến có thể cần quy tắc "xóa sau 30 ngày".
Triển khai để dễ bảo trì:
- Tạo một wrapper duy nhất "SecureStorage" để phần còn lại của app không chạm trực tiếp vào SharedPreferences, Keystore, hoặc cơ sở dữ liệu.
- Đặt từng mục ở nơi phù hợp: token vào EncryptedSharedPreferences, khóa mã hóa được bảo vệ bởi Android Keystore, và dataset ngoại tuyến lớn hơn vào cơ sở dữ liệu mã hóa.
- Xử lý lỗi có chủ ý. Nếu lưu trữ an toàn thất bại, đóng kín (fail closed). Đừng âm thầm chuyển sang lưu dưới dạng plaintext.
- Thêm chẩn đoán mà không làm rò rỉ dữ liệu: log kiểu sự kiện và mã lỗi, không bao giờ log token, khóa, hoặc chi tiết người dùng.
- Kết nối đường dẫn xóa: logout, xoá tài khoản, và "clear app data" nên chảy vào cùng một routine wipe.
Sau đó kiểm thử các trường hợp buồn tẻ phá hỏng lưu trữ an toàn trong sản xuất: khôi phục từ bản sao lưu, nâng cấp từ phiên bản app cũ, thay đổi cài đặt màn hình khóa, di chuyển sang điện thoại mới. Đảm bảo người dùng không bị mắc kẹt trong vòng lặp khi dữ liệu lưu không thể giải mã nhưng app cứ thử lại.
Cuối cùng, ghi ra các quyết định trên một trang để cả đội cùng theo: gì được lưu, ở đâu, thời hạn lưu, và điều gì xảy ra khi giải mã thất bại.
Sai lầm phổ biến làm phá vỡ lưu trữ an toàn
Hầu hết thất bại không phải vì chọn thư viện sai. Chúng xảy ra khi một lối tắt nhỏ âm thầm sao chép bí mật vào nơi bạn không định lưu.
Cờ đỏ lớn nhất là một refresh token (hoặc token phiên dài hạn) được lưu ở plaintext bất kỳ đâu: SharedPreferences, file, cache "tạm thời", hoặc cột database cục bộ. Nếu ai đó có bản sao lưu, dump thiết bị đã root, hoặc artefact bản dựng debug, token đó có thể sống lâu hơn mật khẩu.
Bí mật cũng rò rỉ qua hiển thị, không chỉ lưu trữ. Ghi toàn bộ header yêu cầu vào log, in token khi gỡ lỗi, hoặc đính kèm ngữ cảnh "hữu ích" vào báo cáo sự cố và analytics có thể lộ thông tin ngoài thiết bị. Đối xử logs như công khai.
Xử lý khóa là một khoảng trống phổ biến khác. Dùng một khóa cho mọi thứ làm tăng phạm vi ảnh hưởng. Không quay vòng khóa có nghĩa là các lỗ hổng cũ vẫn còn hiệu lực. Bao gồm kế hoạch cho versioning khóa, quay vòng, và điều gì xảy ra với dữ liệu mã hóa cũ.
Đừng quên các đường dẫn "bên ngoài két"
Mã hóa không ngăn được sao lưu đám mây sao chép dữ liệu app cục bộ. Nó không ngăn được chụp màn hình hay quay màn hình lộ PII. Nó không ngăn bản dựng debug với các thiết lập lỏng lẻo, hoặc tính năng xuất (CSV/share sheets) làm lộ trường nhạy cảm. Clipboard cũng có thể làm lộ mã một lần hoặc số tài khoản.
Ngoài ra, mã hóa không sửa lỗi phân quyền. Nếu app hiển thị PII sau khi người dùng logout, hoặc giữ cache có thể truy cập mà không cần xác thực lại, đó là lỗi kiểm soát truy cập. Khoá giao diện, xoá cache nhạy cảm khi logout, và kiểm tra lại quyền trước khi hiển thị dữ liệu bảo vệ.
Chi tiết vận hành: vòng đời, logout và các trường hợp biên
Lưu trữ an toàn không chỉ là nơi bạn đặt bí mật. Là cách chúng hành xử theo thời gian: khi app ngủ, khi người dùng logout, và khi thiết bị bị khóa.
Với token, lập kế hoạch vòng đời đầy đủ. Access token nên ngắn hạn. Refresh token nên được xử lý như mật khẩu. Nếu token hết hạn, làm mới nó im lặng. Nếu refresh thất bại (bị thu hồi, mật khẩu đổi, thiết bị bị gỡ), dừng vòng lặp thử và bắt sign-in sạch. Hỗ trợ thu hồi phía server. Lưu trữ cục bộ hoàn hảo không giúp nếu bạn không vô hiệu hoá credential bị đánh cắp.
Dùng sinh trắc để xác thực lại, không phải cho mọi thứ. Hỏi khi hành động có rủi ro thực sự (xem PII, xuất dữ liệu, thay đổi thông tin thanh toán, hiển thị khoá một lần). Đừng hỏi mỗi lần mở app.
Khi logout, hãy nghiêm ngặt và có thể đoán:
- Xóa bản sao trong bộ nhớ trước (token cache trong singletons, interceptors, hoặc ViewModels).
- Xoá token lưu và trạng thái phiên (bao gồm refresh token).
- Xóa hoặc vô hiệu hóa khóa mã hóa cục bộ nếu thiết kế của bạn hỗ trợ.
- Xoá PII ngoại tuyến và cache phản hồi API.
- Vô hiệu hoá job nền có thể lấy lại dữ liệu.
Các trường hợp biên quan trọng trong app doanh nghiệp: nhiều tài khoản trên một thiết bị, work profiles, backup/restore, chuyển thiết bị, và logout một phần (chuyển workspace thay vì đăng xuất hoàn toàn). Kiểm thử force stop, nâng cấp OS, và thay đổi thời gian vì sai lệch thời gian có thể phá logic hết hạn.
Phát hiện can thiệp là một đánh đổi. Kiểm tra cơ bản (bản dựng có debuggable, cờ emulator, tín hiệu root đơn giản, verdicts Play Integrity) có thể giảm lạm dụng thông thường, nhưng kẻ tấn công quyết tâm vẫn có thể vượt qua. Xử lý tín hiệu tamper như input rủi ro: giới hạn truy cập ngoại tuyến, yêu cầu xác thực lại, và log sự kiện.
Danh sách kiểm tra nhanh trước khi phát hành
Dùng danh sách này trước khi ra mắt. Nó nhắm vào những chỗ mà lưu trữ an toàn thường thất bại trong các app doanh nghiệp.
- Giả định thiết bị có thể thù địch. Nếu kẻ tấn công có thiết bị đã root hoặc ảnh đầy đủ của thiết bị, họ có thể đọc token, khóa, hoặc PII từ file app, preferences, logs, hoặc ảnh chụp màn hình? Nếu câu trả lời là "có thể", chuyển bí mật sang bảo vệ bằng Keystore và giữ payload được mã hóa.
- Kiểm tra sao lưu và chuyển thiết bị. Giữ file nhạy cảm ngoài Android Auto Backup, sao lưu đám mây, và chuyển thiết bị. Nếu mất khóa khi khôi phục sẽ phá vỡ giải mã, lên kế hoạch luồng khôi phục (xác thực lại và tải lại thay vì cố giải mã).
- Tìm plaintext vô tình trên đĩa. Tìm file tạm, cache HTTP, báo cáo sự cố, sự kiện analytics, và cache ảnh có thể chứa PII hoặc token. Kiểm tra logging debug và JSON dumps.
- Hết hạn và quay vòng. Access token nên ngắn hạn, refresh token được bảo vệ, và session phía server có thể bị thu hồi. Định nghĩa quay vòng khóa và app làm gì khi token bị từ chối (xóa, xác thực lại, thử lại một lần).
- Hành vi khi cài lại và đổi thiết bị. Kiểm thử gỡ cài đặt rồi cài lại, mở offline. Nếu khóa Keystore biến mất, app nên thất bại an toàn (xóa dữ liệu mã hóa, hiển thị sign-in, tránh đọc một phần làm hỏng trạng thái).
Một xác thực nhanh là bài kiểm tra "ngày xấu": người dùng đăng xuất, đổi mật khẩu, khôi phục bản sao lưu sang điện thoại mới, và mở app khi ở trên máy bay. Kết quả nên có thể đoán: hoặc dữ liệu giải mã cho đúng người dùng, hoặc nó bị xóa và tải lại sau khi đăng nhập.
Ví dụ tình huống: app doanh nghiệp lưu PII ngoại tuyến
Hãy tưởng tượng app bán hàng hiện trường dùng ở nơi sóng yếu. Nhân viên đăng nhập một lần vào buổi sáng, duyệt khách hàng được phân công ngoại tuyến, thêm ghi chú cuộc gặp, rồi đồng bộ sau. Đây nơi danh sách kiểm tra lưu trữ có tác dụng thực sự.
Phân chia thực tế:
- Access token: giữ ngắn hạn và lưu trong EncryptedSharedPreferences.
- Refresh token: bảo vệ chặt hơn và hạn chế truy cập qua Android Keystore.
- PII khách hàng (tên, điện thoại, địa chỉ): lưu trong cơ sở dữ liệu cục bộ được mã hóa.
- Ghi chú ngoại tuyến và đính kèm: lưu trong cơ sở dữ liệu mã hóa, cẩn trọng thêm với chức năng xuất và chia sẻ.
Bây giờ thêm hai tính năng và rủi ro thay đổi.
Nếu bạn thêm "remember me", refresh token trở thành cửa chính để vào lại tài khoản. Hãy coi nó như mật khẩu. Tuỳ người dùng, bạn có thể yêu cầu mở khoá thiết bị (PIN/mẫu/sinh trắc) trước khi giải mã.
Nếu bạn thêm chế độ ngoại tuyến, bạn không chỉ bảo vệ phiên nữa. Bạn bảo vệ một danh sách khách hàng đầy đủ có giá trị riêng. Điều đó thường đẩy bạn đến mã hóa cơ sở dữ liệu cộng với quy tắc logout rõ ràng: xóa PII cục bộ, giữ chỉ những gì cần cho lần đăng nhập tiếp theo, và huỷ đồng bộ nền.
Kiểm thử trên thiết bị thật, không chỉ emulator. Ít nhất, xác minh hành vi khoá/mở khoá, cài lại, sao lưu/khôi phục, và tách profile người dùng hoặc work profile.
Bước tiếp theo: biến nó thành thói quen lặp lại của đội
Lưu trữ an toàn chỉ hiệu quả khi nó là thói quen. Viết một chính sách lưu trữ ngắn cho đội: gì vào đâu (Keystore, EncryptedSharedPreferences, cơ sở dữ liệu mã hóa), gì không bao giờ được lưu, và gì phải bị xóa khi logout.
Làm cho nó thành một phần của quy trình giao hàng hàng ngày: definition of done, code review, và kiểm tra phát hành.
Một checklist reviewer nhẹ:
- Mỗi mục lưu được gắn nhãn (token, vật liệu khóa, hoặc PII).
- Lý do chọn nơi lưu được giải thích trong comment code.
- Logout và đổi tài khoản xóa đúng dữ liệu (và chỉ dữ liệu đó).
- Lỗi và logs không bao giờ in bí mật hoặc PII đầy đủ.
- Có người chịu trách nhiệm cho chính sách và giữ nó cập nhật.
Nếu đội bạn dùng AppMaster (appmaster.io) để xây dựng app doanh nghiệp và xuất mã Kotlin cho client Android, giữ cùng cách tiếp cận SecureStorage để mã sinh và mã tuỳ chỉnh theo cùng một chính sách nhất quán.
Bắt đầu với một minh chứng nhỏ
Xây một POC nhỏ lưu một auth token và một bản ghi PII (ví dụ: số điện thoại khách hàng cần ngoại tuyến). Sau đó kiểm thử cài mới, nâng cấp, logout, thay đổi màn hình khóa, và clear app data. Mở rộng chỉ khi hành vi wipe đúng và có thể lặp lại.
Câu hỏi thường gặp
Bắt đầu bằng cách liệt kê chính xác những gì bạn lưu và lý do. Đưa các bí mật phiên nhỏ như access và refresh token vào EncryptedSharedPreferences, giữ khóa mật mã trong Android Keystore, và sử dụng cơ sở dữ liệu được mã hóa cho bản ghi nghiệp vụ ngoại tuyến và PII khi bạn có nhiều trường hoặc cần truy vấn.
Plain SharedPreferences lưu giá trị trong một file mà thường có thể bị đọc từ sao lưu thiết bị, truy cập file trên thiết bị đã root, hoặc các artefact gỡ lỗi. Nếu giá trị là token hoặc bất kỳ PII nào, coi nó như một cài đặt bình thường sẽ làm nó dễ bị sao chép và tái sử dụng ngoài ứng dụng.
Dùng Android Keystore để tạo và giữ các khóa mật mã mà không nên bị trích xuất. Bạn thường dùng những khóa này để mã hóa dữ liệu khác (token, khóa cơ sở dữ liệu, file), và có thể yêu cầu xác thực người dùng (sinh trắc học hoặc chứng chỉ thiết bị) trước khi khóa được sử dụng.
Nó có nghĩa là các thao tác khóa có thể xảy ra trong phần cứng bảo vệ nên vật liệu khóa khó bị trích xuất, ngay cả khi kẻ tấn công có thể đọc file ứng dụng. Đừng giả định nó luôn có sẵn hoặc luôn hành xử giống nhau; thiết kế cho trường hợp thất bại và có luồng khôi phục khi khóa không khả dụng.
Nó thường đủ cho một tập nhỏ các bí mật key-value được đọc thường xuyên như access/refresh token, session ID và các mảnh trạng thái nhỏ. Nó không phù hợp cho dữ liệu lớn, bản ghi ngoại tuyến có cấu trúc, hoặc bất cứ thứ gì bạn cần truy vấn và lọc như danh sách khách hàng, vé, hoặc đơn hàng.
Chọn cơ sở dữ liệu mã hóa khi bạn lưu dữ liệu nghiệp vụ ngoại tuyến hoặc PII ở quy mô, cần truy vấn/tìm kiếm/sắp xếp, hoặc giữ lịch sử để dùng ngoại tuyến. Nó giảm nguy cơ thiết bị bị mất làm lộ toàn bộ danh sách khách hàng hoặc ghi chú, đồng thời vẫn cho phép ứng dụng hoạt động ngoại tuyến với chiến lược quản lý khóa rõ ràng.
Mã hóa toàn bộ cơ sở dữ liệu bảo vệ toàn bộ file ở trạng thái nghỉ và dễ lý giải vì bạn không phải nhớ cột nào được bảo vệ. Mã hóa theo trường có thể phù hợp cho vài cột nhưng làm mất khả năng tìm kiếm và sắp xếp, và dễ vô tình làm lộ dữ liệu qua chỉ mục hoặc các trường dẫn xuất.
Tạo một khóa cơ sở dữ liệu ngẫu nhiên, rồi lưu nó chỉ ở dạng được bọc (được mã hóa) bằng một khóa được bảo vệ bởi Keystore. Không bao giờ hardcode khóa hoặc đóng gói chúng trong app, và quyết định điều gì xảy ra khi logout hoặc khóa bị vô hiệu hóa (thường là: xóa khóa bọc và coi dữ liệu cục bộ là có thể loại bỏ).
Khóa có thể bị vô hiệu hóa bởi thay đổi màn hình khóa, thay đổi sinh trắc học, sự kiện bảo mật của OS, hoặc kịch bản khôi phục/di chuyển. Xử lý rõ ràng: phát hiện lỗi giải mã, xóa an toàn các blob mã hóa hoặc cơ sở dữ liệu cục bộ, và yêu cầu người dùng đăng nhập lại thay vì lặp lại hoặc quay về lưu trữ không mã hóa.
Hầu hết rò rỉ xảy ra “bên ngoài két”: logs, crash reports, analytics events, in ra khi gỡ lỗi, cache HTTP, ảnh chụp màn hình, clipboard và đường dẫn sao lưu/khôi phục. Xem logs như công khai, không bao giờ ghi token hay PII đầy đủ, tắt đường dẫn xuất khẩu vô tình, và đảm bảo logout xóa cả dữ liệu lưu và bản sao trong bộ nhớ.


