今回は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
で保存されている値を登録する。登録する値は keys
と secret-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
を使っていこうと思う。