ユーザーデータの保護
背景
Akaza はユーザーの変換履歴を学習データとして保存する。具体的には、ユーザーが変換を確定するたびに以下の統計データが記録される:
- unigram: 確定した単語の頻度(例:
互換/ごかん 3) - bigram: 隣接する単語ペアの頻度(例:
互換/ごかん\t性/せい 2) - skip-bigram: 1語飛ばしの単語ペアの頻度
これらのデータにはユーザーが「何を変換したか」が記録されるため、プライバシー上のリスクがある。ファイルを閲覧できる第三者がユーザーの入力内容を推測できる可能性がある。
一方、ユーザー辞書 (SKK-JISYO.user) は SKK 辞書形式のテキストファイルであり、ユーザーが手動で編集する用途もあるため、保護の対象外とする。
方針
Mozc のデータ保護設計 を参考に、以下の方針でユーザー統計データを保護する。
保護対象
| ファイル | 内容 | 保護 |
|---|---|---|
unigram.v2.bin | 単語の使用頻度 | AES256 CBC で難読化 |
bigram.v2.bin | 隣接語ペアの頻度 | AES256 CBC で難読化 |
skip_bigram.v2.bin | 1語飛ばしペアの頻度 | AES256 CBC で難読化 |
SKK-JISYO.user | ユーザー辞書 | 保護なし(平文テキスト) |
保護しない対象
- ユーザー辞書 (
SKK-JISYO.user): ユーザーが手動編集する用途があるため - システム辞書・言語モデル: 公開データから生成されるため
設計
レイヤー構成
鍵管理と暗号化ロジックを分離する。
┌─────────────────────────────────────────┐
│ フロントエンド (鍵の管理) │
│ ibus-akaza / mac-akaza: │
│ encryption.key ファイル (0600) │
│ 将来: macOS Keychain 等 │
├─────────────────────────────────────────┤
│ libakaza (暗号化・復号ロジック + 鍵管理) │
│ encryption_key モジュールで鍵生成・読込 │
│ user_stats_utils で AES256 CBC 暗号化 │
└─────────────────────────────────────────┘
libakaza は暗号化・復号のロジック、バイナリ形式の読み書き、および鍵ファイルの生成・読み込みを担当する。
各フロントエンド は libakaza の load_or_create_default_encryption_key() を呼び出し、取得した鍵を UserData に渡す:
- ibus-akaza (Linux):
~/.local/share/akaza/encryption.keyを0600パーミッションで保存 - mac-akaza (macOS): 同上。将来的に Keychain への移行が可能
この分離により、プラットフォームごとに鍵の保護レベルを独立して向上させることができる。
難読化の仕組み
Mozc と同様に AES256 CBC を使用してユーザー統計データを難読化する。
- 初回起動時にランダムな 32 バイト鍵を生成し、鍵ファイル (
encryption.key) に保存する - 統計データの保存時に AES256 CBC で暗号化する
- 統計データの読み込み時に復号する
鍵はマシンごとに異なる値が生成される。プロダクト共通のマスターキーは使用しない。
ファイル形式
統計データはバイナリ形式で保存する。テキストエディタで開いても内容を読むことはできない。
[magic: 4 bytes "AKZ\x01"] // フォーマット識別 + バージョン
[iv: 16 bytes] // AES256 CBC の IV(保存ごとにランダム生成)
[encrypted payload] // bincode(HashMap<String, u32>) を暗号化したもの
- シリアライズ: bincode(Rust との親和性が高く高速)
- 暗号化: AES256 CBC(IV は保存のたびにランダム生成)
- 書き出し方式: 3秒間隔で
HashMap全体をシリアライズ → 暗号化 → 書き出し。一時ファイル + rename によるアトミック書き込み - 追記方式は採用しない: レコード単位の暗号化はオーバーヘッドが大きく、コンパクション等の複雑さに見合わない。ユーザーの語彙数(数千〜数万エントリ)であれば全体書き出しで十分高速
ファイルパーミッション
全てのユーザーデータファイルは 0600(所有者のみ読み書き可能)で保存する。これは現行の実装と同様である。
セキュリティ上の注意
Mozc の設計文書にも明記されているとおり、この難読化は同一ユーザー権限で動作するプロセスからの保護を意図していない。信頼境界はユーザーアカウントのレベルに設定する。
保護の目的は以下のとおり:
- ファイルを
cat等で開いたときに変換履歴が即座に読めない状態にすること - カジュアルな覗き見を防止すること
同一ユーザー権限を持つ攻撃者に対する完全な保護は、このレイヤーでは提供しない。
マイグレーション
旧形式(v1、テキスト)と新形式(v2、暗号化バイナリ)の共存期間を設け、段階的に移行する。
ロード時の挙動
- 新形式ファイル(
unigram.v2.bin等)が存在すれば、それを読み込む - 新形式が存在せず旧形式(
unigram.v1.txt等)が存在すれば、旧形式を読み込む - どちらも存在しなければ初期状態から開始(初回起動時)
保存時の挙動
- 常に新形式で保存する
- 旧形式ファイルは削除しない(ダウングレード時の安全策として残す)
将来の旧形式サポート削除
十分な移行期間を経た後、旧形式の読み込みサポートを削除する。削除時期はリリースノートで事前に告知する。
デバッグ支援
暗号化によりデバッグが困難になることを防ぐため、akaza-data コマンドに復号表示機能を提供する。
# ユーザー統計データの内容を復号して表示
akaza-data dump-user-stats --key-file ~/.local/share/akaza/encryption.key