Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. Security
Code

Khoá, Thông tin xác thực và Lưu trữ trong Android

by
Difficulty:IntermediateLength:LongLanguages:

Vietnamese (Tiếng Việt) translation by Dai Phong (you can also view the original English article)

Trong bài viết trước về bảo mật dữ liệu người dùng Android, chúng ta đã tìm hiểu về mã hóa dữ liệu thông qua passcode do người dùng cung cấp. Hướng dẫn này sẽ chuyển tập trung vào thông tin xác thực và khoá lưu trữ. Tôi sẽ bắt đầu bằng cách giới thiệu thông tin tài khoản và kết thúc bằng một ví dụ về bảo vệ dữ liệu bằng cách sử dụng KeyStore.

Thông thường, khi làm việc với một dịch vụ của bên thứ ba, sẽ có một số hình thức xác thực được yêu cầu. Nó có thể đơn giản như một andpoint /login chấp nhận tên người dùng và mật khẩu.

Thoạt nhìn nó có vẻ là một giải pháp đơn giản là xây dựng một giao diện người dùng yêu cầu người dùng đăng nhập, sau đó ghi nhận và lưu trữ thông tin xác thực của họ. Tuy nhiên, đây không phải là phương pháp hay nhất vì ứng dụng của chúng ta không cần phải biết thông tin xác thực cho tài khoản của bên thứ ba. Thay vào đó, chúng ta có thể sử dụng Account Manager, ủy quyền để xử lý thông tin nhạy cảm đó cho chúng ta.

Account Manager

Account Manager là trình trợ giúp tập trung cho thông tin đăng nhập tài khoản của người dùng để ứng dụng của bạn không phải xử lý trực tiếp mật khẩu. Nó thường cung cấp một token thay cho tên người dùng và mật khẩu thật sự có thể được sử dụng để thực hiện các yêu cầu được chứng thực cho một dịch vụ. Một ví dụ là khi yêu cầu một token từ OAuth2.

Đôi khi, tất cả thông tin bắt buộc đã được lưu trữ trên thiết bị và vào những lần khác, Account Manager sẽ cần phải gọi một máy chủ để có một token mới. Bạn có thể đã thấy phần Accounts trong phần Cài đặt của thiết bị cho các ứng dụng khác nhau. Chúng ta có thể lấy danh sách các tài khoản có sẵn như thế này:

Code sẽ yêu cầu quyền android.permission.GET_ACCOUNTS. Nếu bạn đang tìm kiếm một tài khoản cụ thể nào đó, bạn có thể tìm thấy tài khoản đó như sau:

Một khi bạn có tài khoản, bạn có thể truy xuất token cho tài khoản đó bằng cách gọi phương thức getAuthToken(Account, String, Bundle, Activity, AccountManagerCallback, Handler). Token sau đó có thể được sử dụng để thực hiện các yêu cầu API được xác thực cho một dịch vụ. Đây có thể là một RESTful API, nơi bạn truyền vào một tham số token trong một yêu cầu HTTPS, mà không bao giờ phải biết chi tiết tài khoản cá nhân của người dùng.

Bởi vì mỗi dịch vụ sẽ có một cách khác nhau để xác thực và lưu trữ thông tin đăng nhập, nên Account Manager cung cấp các mô đun xác thực cho dịch vụ của bên thứ ba để triển khai. Mặc dù Android có nhiều triển khai cho nhiều dịch vụ phổ biến, điều đó có nghĩa là bạn có thể viết trình xác thực của riêng mình để xử lý xác thực tài khoản của ứng dụng và lưu trữ thông tin xác thực của ứng dụng. Điều này cho phép bạn đảm bảo thông tin đăng nhập được mã hóa. Xin lưu ý, điều này cũng có nghĩa là thông tin xác thực trong Account Manager được sử dụng bởi các dịch vụ khác có thể được lưu trữ ở dạng văn bản rõ ràng, hiện hữu đối với mọi người đã root thiết bị của họ.

Thay vì thông tin đăng nhập đơn giản, có những lúc bạn cần xử lý khóa hoặc chứng chỉ cho cá nhân hoặc tổ chức — ví dụ, khi bên thứ ba gửi cho bạn tập tin chứng chỉ mà bạn cần lưu giữ. Kịch bản phổ biến nhất là khi một ứng dụng cần xác thực với máy chủ của một tổ chức riêng.

Trong hướng dẫn tiếp theo, chúng ta sẽ tìm hiểu việc sử dụng chứng chỉ để xác thực và giao tiếp an toàn, nhưng tôi vẫn muốn giải quyết cách lưu trữ các mục này trong lúc chờ đợi. Keychain API ban đầu được xây dựng cho mục đích sử dụng rất cụ thể đó — cài đặt một khóa riêng hoặc cặp chứng chỉ từ tập tin PKCS#12.

Keychain

Được giới thiệu trong Android 4.0 (API Level 14), Keychain API đề cập đến quản lý khóa. Cụ thể, nó hoạt động với các đối tượng PrivateKeyX509Certificate và cung cấp một kho chứa an toàn hơn so với việc sử dụng data storage trong ứng dụng của bạn. Đó là vì quyền cho các khóa riêng chỉ cho phép ứng dụng của riêng bạn truy cập vào các khóa và chỉ sau khi người dùng uỷ quyền. Điều này có nghĩa là bạn phải thiết lập màn hình khóa trên thiết bị trước khi có thể sử dụng credential storage. Ngoài ra, các đối tượng trong keychain có thể bị ràng buộc để bảo mật phần cứng, nếu có.

Code để cài đặt chứng chỉ như sau:

Người dùng sẽ được nhắc nhập mật khẩu để truy cập khóa riêng và một tùy chọn đặt tên chứng chỉ. Để lấy khóa, code sau đưa ra một UI cho phép người dùng chọn từ danh sách các khóa đã cài đặt.

Sau khi lựa chọn được thực hiện, một chuỗi tên bí danh được trả về trong callback alias(final String alias) nơi bạn có thể truy cập trực tiếp vào khóa riêng hoặc certificate chain.

Với hiểu biết đó, bây giờ hãy xem cách chúng ta có thể sử dụng bộ nhớ thông tin xác thực để lưu dữ liệu nhạy cảm của riêng bạn.

KeyStore

Trong hướng dẫn trước, chúng ta đã tìm hiểu về bảo vệ dữ liệu thông qua passcode do người dùng cung cấp. Kiểu thiết lập này thì ổn, nhưng yêu cầu ứng dụng thường tránh xa việc có người dùng đăng nhập mỗi lần và ghi nhớ thêm passcode.

Đó là nơi mà KeyStore API có thể được sử dụng. Kể từ API 1, KeyStore đã được hệ thống sử dụng để lưu trữ thông tin đăng nhập WiFi và VPN. Kể từ 4.3 (API 18), nó cho phép bạn làm việc với các khóa không đối xứng cụ thể cho ứng dụng của bạn và trong Android M (API 23), nó có thể lưu trữ khóa đối xứng AES. Vì vậy, mặc dù API không cho phép lưu trữ các chuỗi nhạy cảm một cách trực tiếp, các khóa này có thể được lưu trữ và sau đó được sử dụng để mã hóa chuỗi.

Lợi ích của việc lưu trữ một khóa trong KeyStore là nó cho phép các key được vận hành mà không để lộ nội dung bí mật của khóa đó; dữ liệu khoá không nhập vào không gian ứng dụng. Hãy nhớ rằng các khoá được bảo vệ bởi các quyền để chỉ ứng dụng của bạn có thể truy cập chúng và chúng cũng có thể được bảo mật bằng phần cứng nếu thiết bị có khả năng. Điều này tạo ra một kho chứa khiến việc trích xuất khóa từ thiết bị trở nên khó khăn hơn.

Tạo khóa ngẫu nhiên mới

Đối với ví dụ này, thay vì tạo khóa AES từ passcode do người dùng cung cấp, chúng ta có thể tự động tạo một khóa ngẫu nhiên sẽ được bảo vệ trong KeyStore. Chúng ta có thể làm điều này bằng cách tạo một đối tượng KeyGenerator, thiết lập cho "AndroidKeyStore".

Phần quan trọng cần xem xét ở đây là đặt điểm của .setUserAuthenticationRequired(true).setUserAuthenticationValidityDurationSeconds(120) Điều này yêu cầu cần phải cài đặt màn hình khóa và được khóa cho đến khi người dùng đã xác thực.

Nhìn vào tài liệu hướng dẫn cho .setUserAuthenticationValidityDurationSeconds(), bạn sẽ thấy rằng nó có nghĩa là khóa chỉ sẵn có ở một số giây nhất định từ xác thực mật khẩu, và truyền vào -1 yêu cầu xác thực vân tay mỗi khi bạn muốn truy cập khóa. Cho phép yêu cầu xác thực cũng có tác dụng thu hồi khóa khi người dùng xóa hoặc thay đổi màn hình khóa.

Bởi vì lưu trữ một khóa không được bảo vệ cùng với dữ liệu được mã hóa giống như đặt một chìa khóa của ngôi nhà dưới tấm thảm chùi chân, các tùy chọn này cố gắng bảo vệ khóa ở trạng thái nghỉ trong trường hợp thiết bị bị xâm nhập. Một ví dụ có thể là một kết xuất dữ liệu ngoại tuyến của thiết bị. Nếu không có mật khẩu cho thiết bị, dữ liệu đó sẽ trở nên vô dụng.

Tuỳ chọn .setRandomizedEncryptionRequired(true) cho phép yêu cầu có đủ ngẫu nhiên (một IV ngẫu nhiên mới mỗi lần) để nếu cùng một dữ liệu được mã hóa lần thứ hai, đầu ra được mã hóa đó sẽ vẫn khác nhau. Điều này ngăn cản kẻ tấn công lấy được manh mối về bản mã dựa trên việc cung cấp dữ liệu tương tự.

Một tùy chọn khác cần lưu ý là setUserAuthenticationValidWhileOnBody(boolean remainsValid), nó khóa cái khoá một khi thiết bị đã phát hiện ra nó không còn trên người.

Mã hoá Dữ liệu

Bây giờ khóa được lưu trữ trong KeyStore, chúng ta có thể tạo ra một phương thức mã hóa dữ liệu bằng cách sử dụng đối tượng Cipher, được cung cấp SecretKey. Nó sẽ trả về một HashMap chứa dữ liệu được mã hóa và một IV ngẫu nhiên cần thiết để giải mã dữ liệu. Dữ liệu được mã hóa, cùng với IV, sau đó có thể được lưu vào một tập tin hoặc vào shared preferences.

Giải mã thành một Mảng Byte

Để giải mã, đảo ngược được áp dụng. Đối tượng Cipher được khởi tạo bằng hằng số DECRYPT_MODE và mảng byte[] đã giải mã được trả về.

Kiểm tra Ví dụ

Bây giờ chúng ta có thể kiểm tra ví dụ!

Sử dụng các khóa không đối xứng RSA cho các thiết bị cũ hơn

Đây là một giải pháp tốt để lưu trữ dữ liệu cho các phiên bản M và cao hơn, nhưng nếu ứng dụng của bạn hỗ trợ các phiên bản trước đó thì sao? Mặc dù các khóa đối xứng AES không được hỗ trợ trong các phiên bản dưới M, nhưng khoá không đối xứng RSA thì có. Điều đó có nghĩa là chúng ta có thể sử dụng khóa RSA và mã hóa để thực hiện điều tương tự.

Sự khác biệt chính ở đây là một cặp khóa bất đối xứng chứa hai khóa, một khóa riêng và một khóa công khai, nơi khóa công khai mã hóa dữ liệu và khóa riêng giải mã nó. Một KeyPairGeneratorSpec được truyền vào KeyPairGenerator được khởi tạo với KEY_ALGORITHM_RSA và provider "AndroidKeyStore".

Để mã hóa, chúng ta lấy RSAPublicKey từ cặp khóa và sử dụng nó với đối tượng Cipher.

Giải mã được thực hiện bằng cách sử dụng đối tượng RSAPrivateKey.

Một điều về RSA là mã hóa chậm hơn so với AES. Điều này thường ổn đối với một lượng nhỏ thông tin, chẳng hạn như khi bạn đang bảo mật các chuỗi shared preference. Tuy nhiên, nếu bạn thấy có vấn đề về hiệu suất mã hóa một lượng lớn dữ liệu, bạn có thể sử dụng ví dụ này để mã hóa và chỉ lưu trữ một khóa AES. Sau đó, sử dụng mã hóa AES nhanh hơn đã được thảo luận trong hướng dẫn trước cho phần còn lại của dữ liệu. Bạn có thể tạo khóa AES mới và chuyển đổi nó thành mảng byte[] tương thích với ví dụ này.

Để lấy lại khóa từ các byte, hãy thực hiện như sau:

Có rất nhiều code! Để giữ cho tất cả các ví dụ đơn giản, tôi đã bỏ qua việc xử lý ngoại lệ. Nhưng hãy nhớ rằng đối với code thành phẩm, phải bắt tất cả các trường hợp Throwable trong một catch.

Tóm tắt

Phần này kết thúc hướng dẫn về làm việc với thông tin đăng nhập và khóa. Phần lớn sự nhầm lẫn xoay quanh khóa và bộ nhớ phải làm liên quan đến sự phát triển của hệ điều hành Android, nhưng bạn có thể chọn giải pháp để sử dụng theo level API mà ứng dụng của bạn hỗ trợ.

Bây giờ chúng ta đã đề cập đến các phương pháp tốt nhất để bảo mật dữ liệu đang ở trạng thái nghỉ ngơi, hướng dẫn tiếp theo sẽ tập trung vào việc bảo mật dữ liệu khi truyền dẫn.

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.