TT Lab
はじめる
学ぶ 学習パス コース

閉域網の現場 — 防衛ドメイン

消せば解析が死ぬ — 置き換えて外へ出す技術

TT Labで続きを見る

一言でいうと

ログを外に出さなければならないのに、その中に人と組織が紛れ込んでいます。消せば分析ができず、そのまま出せば持ち出しの審査を通りません。その間にあるのが、決定的な仮名化です。同じ値がいつも同じ仮名になるので相関分析は生き残り、元に戻すための鍵は内側にだけ残ります。

なぜ必要なのか

エアギャップ環境の現場で解けない障害が起きると、結局ログを外に送ることになります。本社の開発チームでもメーカーでも、そのコードを知っている人は、ネットワークの外にいるからです。ところが、運用ログには、社員番号、アカウント、内部アドレス、端末識別子、そして人が手で書いた備考が、そのまま入っています。持ち出し審査は、それらを根拠に差し戻します。

最初の反応は、たいていマスキングです。社員番号をすべてアスタリスクに変え、アドレスを切り落とします。すると審査は通りますが、分析が通りません。障害分析で最もよく使う事実は、「同じユーザーが3分の間に同じリクエストを5回失敗した」のようなもので、その事実は、値そのものではなく、値が同じだという関係から出ます。すべて同じアスタリスクにすれば、5行は互いに赤の他人になり、すべて別の乱数にすれば、5人が1回ずつ失敗したように見えます。どちらでも、原因は見つけられません。

そこで必要になるのが、同じ値は同じに、違う値は違うように、そして元に戻せないように変える関数です。この3つの条件を同時に満たす標準的な道具が、鍵付きハッシュ、つまりHMACです。構造はRFC 2104が定義し、米国の連邦標準はFIPS 198-1で同じものを固定しています。Pythonでは、標準ライブラリのhmac1行で済みます。

どう動くのか

1つ目に、マスキングと仮名化を分けます。マスキングは値をなくすことで、仮名化は値を置き換えることです。なくすべきなのは、何が入っているか数えられない場所です。人が自由に書いた備考欄には、電話番号も名前も入りえて、どんな正規表現を組んでも、次の人が新しい形で書きます。そうした欄は、まるごと消すほうが誠実です。反対に、機械が決まった型で出力するフィールドは、何が入るかがわかるので、仮名化します。

2つ目に、仮名は鍵に結びつけなければなりません。単にsha256(사번)(プレースホルダーは社員番号です)で作ってはいけません。社員番号は形式が狭く、可能な値が数万個しかないので、受け取る側がすべてをハッシュして表を作れば、数秒で元に戻ってきます。電話番号も住民番号の区切りも同じです。鍵を混ぜれば、鍵を知らない人は、その表を作れません。鍵はデータと一緒に出てはならず、鍵が変われば仮名もまるごと変わるので、いつどの鍵を使ったかは、内側に記録しておきます。

3つ目に、正規表現が見逃す場所を別に数えます。emp=E24-0101のようにラベルが付いた場所は簡単です。難しいのは3つあります。ログメッセージの本文の中に、文章のように埋め込まれた値、自由記述欄に人が書き込んだ値、そしてbase64のようなエンコードの裏に隠れた値です。最初の2つは、フィールド単位の走査では絶対に出てこず、3つ目は、どんな正規表現でも出てきません。先にデコードしてから探さなければなりません。この3つを数えずに「正規表現ですべて処理した」と言った瞬間に、持ち出し物が漏れます。

4つ目に、消したあとにも残るリスクを見ます。直接識別子をすべて変えても、部署と等級とアクセス時刻を一緒に見ると、1人に絞り込まれる行が残ります。こうした列を準識別子といい、同じ組み合わせを持つ行がk個未満の状態をリスクとみなします。減らす方法は、消すことではなく、粗くすることです。分単位の時刻を時単位に切り捨てると、同じ組み合わせの行がまとまって、唯一の行の数が減ります。失うのは精度で、得るのは持ち出しの可能性なので、この2つの交換を数字で書いておくことが、このステップの成果物です。米国の標準機関の非識別化についての整理文書NIST IR 8053とNIST SP 800-188が、この交換を詳しく扱っています。

5つ目に、マッピング表は持ち出し物に入れません。仮名を元に戻す表は、調査にどうしても必要ですが、それがデータと一緒に出れば、仮名化をしていないのと同じです。表は内側の金庫に残し、外には仮名だけを出します。ログ管理の一般論は、NIST SP 800-92にまとめられています。

現場での姿

あるとき、持ち出し審査を2回差し戻されたことがあります。1回目は予想した理由でした。社員番号がそのまま残っていたのです。直して出し直したところ、2回目に差し戻されて、理由は備考欄でした。担当者が「通話済み」のあとに連絡先を書いた行が1行あり、その行は、私たちが作ったどの正規表現にも引っかかりませんでした。それ以来、自由記述欄はまるごと消すことを原則にしました。分析に本当に必要なら、人がもう一度読んで、必要な文だけを手で書き写すようにしました。

もう1つ記憶に残っているのは、エンコードです。認証失敗ログにトークンの断片がbase64で載っていたのですが、デコードしてみると、アカウントのアドレスでした。持ち出し物でアカウント文字列を探す検査は、すべて通過しました。探す側が、原文だけで探したからです。自己検証を作るときに、「元にあった値が持ち出し物にないか」だけを見るのではなく、「エンコードされた形でもないか」も一緒に見なければならない理由です。

次のラボですること

アクセスログ96行とアプリログ16行、そして18人の名簿を作って、どのフィールドが直接識別子で、どれが準識別子かを分けます。正規表現で見つけた値と見逃した値を別々に数え、鍵に結びついた決定的な仮名関数を実装して、同じ値が同じ仮名になるか、鍵を変えると変わるかを、テストで示します。そのあと、仮名化とマスキングを分けて適用し、準識別子の組み合わせで唯一になる行を数えて、一般化で減らしたうえで、持ち出しパッケージを機械で自己検証して出します。