パスワードや API キーなどの機密情報を安全に保存するために、Tauri の plugin-stronghold を使用して暗号化ストレージを導入・利用する方法を解説します。
Stronghold は IOTA 財団が開発したセキュアなデータベースエンジンを使用しており、データはファイルシステム上で暗号化されて保存されます。
1. セットアップ
インストール手順
Rust 側と JavaScript 側の両方に依存関係を追加し、プラグインを初期化します。
1. コマンドで追加
Tauri の CLI を使用して追加します。
npm run tauri add stronghold
補足: このレシピのコード例ではargon2クレートを使用しています。src-tauriディレクトリでcargo add argon2を実行して依存関係を追加してください。
2. プラグインの初期化 (Rust)
src-tauri/src/lib.rs でハッシュ化ロジックを含めてプラグインを登録します。
以下は rust-argon2 クレートを使用してパスワードのハッシュ値を生成する公式サンプルの実装例です。
// src-tauri/src/lib.rs
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.plugin(
tauri_plugin_stronghold::Builder::new(|password| {
// Argon2 v0.5+ を使用したハッシュ化の実装例
// Cargo.toml に argon2 = "0.5" (またはそれ以降) が必要です
use argon2::{
password_hash::{PasswordHasher, SaltString},
Argon2,
};
// 本番環境では、アプリごとに一意で固定のソルトを使用する必要があります。
// ランダムに生成してしまうと、再起動後に復号できなくなります。
// ここでは例として固定のバイト列からソルトを生成します。
let salt = SaltString::encode_b64(b"random_salt_value_16_bytes").expect("Failed to create salt");
let argon2 = Argon2::default();
let password_hash = argon2
.hash_password(password.as_bytes(), &salt)
.expect("Failed to hash password");
// 生のハッシュ値 (32バイト) を取り出して返す
password_hash.hash.expect("Hash calculation failed").as_bytes().to_vec()
})
.build(),
)
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
権限設定 (Capabilities)
src-tauri/capabilities/default.json で Stronghold へのアクセスを許可します。
{
"permissions": [
...,
"stronghold:default"
]
}
2. データの操作
Stronghold の操作は基本的に「ロード」→「クライアント作成」→「ストア操作」→「保存」の流れになります。
コード例 (TypeScript)
以下は、公式ドキュメントに基づいたデータの保存と読み出しの実装例です。
import { Client, Stronghold } from '@tauri-apps/plugin-stronghold';
import { appDataDir } from '@tauri-apps/api/path';
// Stronghold の初期化とクライアント取得
const initStronghold = async () => {
const vaultPath = `${await appDataDir()}/vault.hold`;
const vaultPassword = 'vault password'; // 本番ではユーザー入力などを使用してください
const stronghold = await Stronghold.load(vaultPath, vaultPassword);
let client: Client;
const clientName = 'name your client';
try {
client = await stronghold.loadClient(clientName);
} catch {
client = await stronghold.createClient(clientName);
}
return {
stronghold,
client,
};
};
// 「store」にレコードを挿入
async function insertRecord(store: any, key: string, value: string) {
const data = Array.from(new TextEncoder().encode(value));
await store.insert(key, data);
}
// 「ストア」からレコードを読み取り
async function getRecord(store: any, key: string): Promise<string> {
const data = await store.get(key);
return new TextDecoder().decode(new Uint8Array(data));
}
// 使用例
async function main() {
const { stronghold, client } = await initStronghold();
const store = client.getStore();
const key = 'my_key';
// 「store」にレコードを挿入
await insertRecord(store, key, 'secret value');
// 「ストア」からレコードを読み取り
const value = await getRecord(store, key);
console.log(value); // 'secret value'
// 更新内容を保存
await stronghold.save();
// 必要であれば削除
// await store.remove(key);
}
3. 使い分けのヒント
Stronghold はセキュリティ強度が高い反面、初期化手順やパスワード管理などが複雑です。
* ウィンドウサイズやテーマ設定など → plugin-store (JSON)
* アクセストークンや秘密鍵など → plugin-stronghold (暗号化)
というように使い分けるのが賢明です。