flutter_secure_storageを使ってみた
📕Overview
Flutter Secure Storage は、データを安全なストレージに保存するための API を提供します。iOS ではキーチェーンが使用され、Android ではキーストア ベースのソリューションが使用されます。
flutter_secure_storage
Note: usage of encryptedSharedPreference
When using the encryptedSharedPreferences parameter on Android, make sure to pass the option to the constructor instead of the function like so:
注意:encryptedSharedPreferenceの使い方
AndroidでencryptedSharedPreferencesパラメータを使用する場合は、関数ではなくコンストラクタにオプションを渡すようにしてください:
AndroidOptions _getAndroidOptions() => const AndroidOptions(
encryptedSharedPreferences: true,
);
final storage = FlutterSecureStorage(aOptions: _getAndroidOptions());
This will prevent errors due to mixed usage of encryptedSharedPreferences. For more info, see this issue.
これにより、encryptedSharedPreferencesの混合使用によるエラーを防ぐことができる。詳細については、この問題を参照してください。
Info
A Flutter plugin to store data in secure storage:
Keychain is used for iOS
AES encryption is used for Android. AES secret key is encrypted with RSA and RSA key is stored in KeyStore
With V5.0.0 we can use EncryptedSharedPreferences on Android by enabling it in the Android Options like so:
情報
データを安全なストレージに保存するためのFlutterプラグイン:
iOSではKeychainを使用
AndroidではAES暗号が使われます。AES秘密鍵はRSAで暗号化され、RSA鍵はKeyStoreに保存される。
V5.0.0では、Android OptionsでEncryptedSharedPreferencesを有効にすることで、AndroidでもEncryptedSharedPreferencesを使うことができます:
AndroidOptions _getAndroidOptions() => const AndroidOptions(
encryptedSharedPreferences: true,
);
For more information see the example app.
libsecret is used for Linux.
Note KeyStore was introduced in Android 4.3 (API level 18). The plugin wouldn't work for earlier versions.
詳細はサンプルアプリを参照のこと。
Linuxではlibsecretを使用しています。
注 KeyStoreはAndroid 4.3(APIレベル18)で導入されました。それ以前のバージョンではプラグインは動作しません。
Getting Started
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
// Create storage
final storage = new FlutterSecureStorage();
// Read value
String value = await storage.read(key: key);
// Read all values
Map<String, String> allValues = await storage.readAll();
// Delete value
await storage.delete(key: key);
// Delete all
await storage.deleteAll();
// Write value
await storage.write(key: key, value: value);
This allows us to be able to fetch secure values while the app is backgrounded, by specifying first_unlock or first_unlock_this_device. The default if not specified is unlocked. An example:
これにより、first_unlockまたはfirst_unlock_this_deviceを指定することで、アプリがバックグラウンドになっている間に安全な値をフェッチできるようになる。指定しない場合のデフォルトはunlockedです。例
final options = IOSOptions(accessibility: KeychainAccessibility.first_unlock);
await storage.write(key: key, value: value, iOptions: options);
Configure Android version
In [project]/android/app/build.gradle set minSdkVersion to >= 18.
android {
...
defaultConfig {
...
minSdkVersion 18
...
}
}
Note By default Android backups data on Google Drive. It can cause exception java.security.InvalidKeyException:Failed to unwrap key. You need to
disable autobackup, details
exclude sharedprefs FlutterSecureStorage used by the plugin, details
注意 Androidのデフォルトでは、データはGoogle Driveにバックアップされます。この場合、例外java.security.InvalidKeyException:Failed to unwrap keyが発生することがあります。その場合は
自動バックアップを無効にする。
プラグインが使用するsharedprefs FlutterSecureStorageを除外する。
🧷summary
長い説明になりましたね😅
海外の動画を参考に使ってみましょう。
flutter_secure_storageを使用するサービスクラスを定義。保存・取得・削除の機能を実装。
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
// FlutterSecureStorage Service Class
class SecureStorageService {
// FlutterSecureStorage Instance
final FlutterSecureStorage _flutterSecureStorage = const FlutterSecureStorage();
// get data
Future<String?> getValue(String key) {
return _flutterSecureStorage.read(key: key);
}
// get all data
Future<Map<String, String?>> getAllValue() {
return _flutterSecureStorage.readAll();
}
// set data
Future<void> setValue(String key, String value) async {
await _flutterSecureStorage.write(key: key, value: value);
}
// delete data
Future<void> deleteValue(String key) async {
await _flutterSecureStorage.delete(key: key);
}
// delete all data
Future<void> deleteAll() async {
await _flutterSecureStorage.deleteAll();
}
}
入力フォームから保存をした後に、データの取得と削除する機能をmain.dart
に追加しましょう。
mainのコード
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:secure_storage_demo/secure_storage_service.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final TextEditingController _controller = TextEditingController();
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: const Text("Secure Storage"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(20.0),
child: TextField(
controller: _controller,
decoration: const InputDecoration(
border: OutlineInputBorder()
),
),
),
// 追加
ElevatedButton(onPressed: () {
SecureStorageService().setValue('test', _controller.text);
}, child: const Text('Save')),
// keyがtestのデータを取得
ElevatedButton(onPressed: () async {
final result = await SecureStorageService().getValue('test');
log(result ?? '');
}, child: const Text('Get')),
// 全てのデータを取得
ElevatedButton(onPressed: () async {
final result = await SecureStorageService().getAllValue();
log('All Value:$result');
}, child: const Text('All Get')),
// keyがtestのデータを削除
ElevatedButton(onPressed: () async {
await SecureStorageService().deleteValue('test');
final result = await SecureStorageService().getValue('test');
log(result ?? '');
}, child: const Text('Delete')),
// 全てのデータを削除
ElevatedButton(onPressed: () async {
await SecureStorageService().deleteAll();
final result = await SecureStorageService().getValue('test');
log(result ?? '');
}, child: const Text('All Delete')),
],
),
),
);
}
}
🧑🎓thoughts
使ってみた感想ですが、暗号化されたデータが保存されると思ったがそうでもないみたい?
ログに表示されている。
安全なストレージに保存するのが目的なので、iPhoneを使っているときに、指紋認証でロックを解除してデータを読み込むことがあるのですが、ここでキーチェーンを使いますね。保存されている場所が安全な場所なんでしょうね。
Discussion