« ^ »

Emacsで取扱注意のデータを安全に扱うためにplstoreを使う

所要時間: 約 5分

今回はEmacsで取扱注意のデータを安全に扱う、保管する方法について考える事にする。自前実装を辞め、 plstore を使う方法について調べる。

取扱注意のデータの扱いは悩ましい

ソフトウェアの開発の作業をしていると、取り扱いに注意が必要なデータに触れる事がある。

個人情報となるような氏名や住所もあれば、WebサービスのIDやパスワード、会社の内部情報など、一言で 取り扱いに注意が必要なデータ と言っても、様々な種類や危険度の違いがあり、どのように取り扱うべきか悩む事も多い。

最近、OpenAIのクライアントをEmacs Lispで新しく実装したため、OpenAI APIのAPIキーの管理についても考えなおす事にした。つまり 取り扱いに注意が必要なデータEmacs でどのように取り扱うかについて考える。

今までやっていたかなり無理矢理なデータの秘匿方法

今までは手動で発行したAPIキーをelファイルに記述し、そのelファイルごとGnuPGで暗号化していた。読み込む時にはこんな感じにしていた。

(with-current-buffer (find-file "/PATH/TO/FILE.el.gpg")
  (eval-buffer)
  (kill-buffer))

拡張子がgpgになっているから、 find-file で開くとGnuPGによって複合化した状態でバッファに表示される。それを (eval-buffer) すれば、秘密のデータをEmacsに反映できる。

絶対ダメな方法という訳ではないけれど、かなり無理矢理だし正直イケていない。この方法のイケていない所の1つは、ファイル全体が暗号化されてしまうと言う点だ。例えば復号化後のデータが100行のテキストだったとする。その1行を更新した場合、ファイル全体が書き変わる。これでは差分が取れず、どの値が書き変わったのかを確認するためには、平文のデータを比較しなければならない。

折角、できるだけ平文をストレージに書き込まないようにしているのに、比較のために変更前と変更後の平文のファイルをストレージに書き込みたくはない。

また、秘密の情報を保存するファイルとは言え、その中には別に秘密ではない情報も書き込みたくなる。関連する情報は、できるかぎり近くに記述したいからだ。しかしその場合、秘密でもなんでもない情報も合わせて暗号化する事になる。

例えば、秘密の情報を設定する変数の近くに、その変数を説明するコメントを記述していたとする。そして、このコメントは別に秘密でもなんでもないとする。暗号化して暫く運用した後、そのコメントにタイポを発見した。そうすると、そのタイポを修正するためだけに、ファイル全体が書き変える事になる。

見れば見るほど、この方法はイケていないように感じる。実際、イケてはいないけれど。Emacsが提供している、他のデータの保存方法はないのだろうか。

plstoreを使う

Emacsは plstore(secure plist store) というライブラリを梱包している。これはplistを部分的に暗号化して永続化する。正に探していたもののように思える。ここでは plstore を使ってみる事にする。

plstore はEmacsに梱包されているため package-install などでインストールする必要はないが、暗号化及び復号化には、GnuPGを使用する。そのためGnuPGをインストールする必要がある。簡単な使い方を見て行く事にする。

plstore-open 関数で、データを保存するファイルを指定する。

(require 'plstore)

(setq mystore (plstore-open "testing.plist" ))

plstore-get で保存されている値を取得する。値がない場合はnilが返される。

(plstore-get mystore "foo") ;; => nil ; 値がない場合はnil

plstore-put で保存されている値を登録する。登録する値は keyssecret-keys の2種類がある。 keys は暗号化されずそのまま保存される。 secret-keys は暗号化されて保存される。

(plstore-put mystore "foo" '(:name "testing") '(:age "123"))

この例の場合、 '(:name "testing") はそのまま、 '(:age "123") は暗号化されて保存される事になる。

登録されたデータは plstore-get で取得できる。

(plstore-get mystore "foo") ;; => ("foo" :age "123" :name "testing")

データを変更したら plstore-save を使ってファイルに出力する。

(plstore-save mystore)

用が済んだら plstore-close で、バッファを削除する。

(plstore-close mystore)

保存するファイルの形式

Emacsでplstore-modeで開くと、暗号化された値は復号化された状態でバッファに表示される。これはEmacsが復号化してplistになるようにマージした状態にする。

;;; You are looking at the decoded form of the plstore file.
;;; To see the original form content, do C-c C-c

(("foo" :secret-age t :name "testing"))

実際のファイルとして次のような形式となる。

;;; public entries -*- mode: plstore -*- 
(("foo" :secret-age t :name "testing"))
;;; secret entries
"-----BEGIN PGP MESSAGE-----

jA0ECQMIloel/8zQmKDo0koBCuKPqfdS98j5Xo/7M51XAd7icFE7dLIhQXx9g4Gm
6ONBgIZkswcvh3cMRa82rcUH4toM9eHEmIK6559DziH3Y8FKza+grh5UAg==
=kHXr
-----END PGP MESSAGE-----
"

使われる暗号方式

plstoreは plstore-save 関数の中で呼ばれている、 plstore--insert-buffer で行われる。 plstore-encrypt-to にnilを設定している場合、対称暗号が使用される。どの対称暗号アルゴリズムが使われるかは、GnuPGのバージョンやgpg.confの設定によって異なるが、動作としては gpg --symmetric を実行する事になる。

plstore-encrypt-to を指定している場合、その受信者の鍵のアルゴリズムが使用される。この場合 gpg --encrypt を実行する事になる。

まとめ

今回は、Emacsで取扱注意のデータを安全に扱うための方法について考えた。今までの無理矢理な方法のイケてない所を確認し、それらを解決するために plstore を使う方法と、内部的な動きについても確認した。=plstore= を使うと、秘匿したい情報と秘匿する必要のない情報を混在したままデータを保存でき、ファイルに永続化する事ができた。これからは plstore を使っていこうと思う。