全社の監査ログ/ Company-wide Audit
システム管理者が、生のまま引く
3 つの読み口のうち 1 つ目です。生のデータをそのまま引ける唯一の経路で、接続元の IP や端末情報も伏せません。伏せると調査の役に立たないためです。その代わりに、閲覧できる相手を絞り、取り出しに理由を要求し、閲覧そのものを毎回記録しています。
何ができるかCapabilities
窓口は 8 つです。すべてシステム管理者に限定し、2 段の保護を掛けています。1 段目でログインを確かめ、2 段目で全社の管理権限を確かめます。権限が無い相手には 404 を返し、そういう窓口があること自体を伏せます。
| 窓口 | できること | 上限・条件 |
|---|---|---|
| 一覧 | 9 条件で絞り込み、新しい順に並べて取る。続きは不透明な印で辿る | 1 ページ 50 件 |
| 集計 | 期間内の件数を、時間単位または日単位のまとまりで返す。多い操作の上位も返す | 期間は必須・上限あり |
| CSV の取り出し | 絞り込んだ範囲を CSV にする。その場で流すか、バッチへ渡すかを内部で決める | 理由 10〜256 文字必須 1 時間 5 回 |
| 依頼の一覧 | バッチへ渡した取り出しの依頼を新しい順に見る | 90 日分・50 件 |
| 依頼 1 件の状態 | 進み具合を見る。成功していればダウンロード用の URL を都度発行する | URL は 15 分 |
| 子テーブルの一覧 | 現役・切り離し待ち・退避済みの 3 つのリストを 1 回で返す | — |
| 退避分の取得 | 2 年を超えて外に出た月のファイルへ、ダウンロード用の URL を発行する | 理由 10〜256 文字必須 URL は 15 分 |
| 管理画面の閲覧記録 | 管理画面を開いたこと自体を記録する | — |
絞り込みと続きの取り方Filter & Paging
調査で使う操作は「期間を決めて、対象を絞って、新しい順に辿る」の 3 つに集約されます。上段が業務としての流れ、下段がそれをどう実現しているかです。
続きの位置に「発生時刻」だけを使うと、同じ時刻の行が 50 件目と 51 件目に分かれたときに取りこぼしか重複が起きます。連番を組み合わせて必ず一意な順序にすることで、これを防いでいます。並び順は主キーがそのまま供給するので、並べ替えのための追加処理は発生しません。
?actions=a&actions=b)で、カンマ連結は使いません。ここを素直に「配列」として検証すると、1 個だけ選んだときに拒否され、2 個以上なら通るという左右非対称な故障になります(1 個のときは配列ではなく単一の値として届くため)。共通のヘルパを必ず通す規律にして、URL の往復を検査する自動テストで固定しています。
閲覧そのものを記録するAuditing the Auditor
一覧を引くと、そのリクエストごとに必ず 1 行が記録に増えます。目的は、システム管理者が取引先のデータを必要以上に見ていないかを、あとから検知できるようにすることです。
記録する中身は、絞り込みの要約と「新規の検索か、続きの読み込みか」の手掛かりだけです。返した行の中身は入れません(同じ内容を二重に持つことになり、量も増えるため)。
記録に条件を付けない
ここは実装の途中で 2 回考え直した箇所です。当初は「無駄な行を増やさない」ために、記録する条件を置いていました。しかし条件の材料が続きの位置を示す印である以上、その印は監査される本人が握っている値です。つまり条件は原理的に操作できます。実際に 2 通りとも外側から破れました。
| 置いた条件 | 破れ方 |
|---|---|
| 続きの印が付いていたら記録しない | 空の印や壊れた印を渡すと、仕組みは先頭のページを返します(古い URL や並行アクセスで 500 にしないための安全弁)。データは返るのに記録されない |
| 続きの印を実際に使ったときだけ記録しない | 印は形だけを確かめており、値の範囲は見ていません。ありえない未来の時刻を詰めた印を渡すと、条件が全行に当てはまるので先頭のページがそのまま返り、しかも「印を使った」と判定される。細工をしなくても、一度得た印を別の絞り込みで使い回すだけで成立しうる |
対処として、条件そのものを撤廃しました。「印の検証を厳しくする」「印に署名を付ける」という方向は採っていません。厳しくした条件の外側には必ずまた「返るのに残らない」経路ができるうえ、壊れた印を先頭に戻す挙動はほかの画面と共通で、この 1 か所だけ違う挙動にしたくなかったためです。
行が増えることは、過剰なアクセスの検知という目的にとっては情報が増えることです。ページ送りも 1 行として積まれるので、「何ページ目まで辿ったか」まで読めます。記録に入れている「新規か続きか」の手掛かりは、あくまで読む側の目安で、判定には使いません(本人が操作できる値だからです)。
集計Aggregation
一覧の前に「いつ、どのくらい、どんな操作があったか」を掴むための窓口です。期間の指定は必須で、上限があります。
- まとまりの単位は自動で決まります。期間が 24 時間以内なら時間単位、それより長ければ日単位です。
- 区切りは日本時間です。世界標準時で区切ると、日本の 9 時前の操作が前日に入ってしまい、業務の「日」と合いません。
- 返すのは、まとまりごとの件数と、その期間に多かった操作の上位です。
- 絞り込みの条件は一覧と同じものが使えます(期間・Box・操作者・行為者の種類・操作種別)。
集計・子テーブルの一覧・依頼の一覧については、個別の閲覧記録を付けていません。これらはどれも一覧を引く前後に必ず通る窓口で、一覧の記録に含まれると判断したためです。
CSV の取り出しCSV Export
画面で辿るのではなく、まとめて手元に落として調べたい場面のための窓口です。期間の長さと件数の見込みで、その場で流すかバッチへ渡すかを内部で決めます。要求する側はどちらになるかを指定しません。
「その場で流す」側で直結の接続を使っているのは、実測にもとづく判断です。プールを経由すると全体に実行時間の上限が掛かっており、数百万行の取り出しは確実に途中で切られます。直結側では上限を長めに明示して開いています。
CSV は 19 列。データベースに作らせる
列は次の 19 です。並びは古い順(画面の新しい順とは逆)にしています。時系列で読み下すほうが調査に向くためです。
| 区分 | 列 |
|---|---|
| いつ | id / occurred_at |
| 誰が | actor_type / actor_id / actor_email / actor_display_name / actor_share_link_id / actor_file_request_id |
| 何を | action |
| どこで・何に | box_id / box_name / target_type / target_id / target_label |
| 付随情報 | request_id / reason / ip / user_agent / metadata_json |
CSV の組み立てはデータベース側に作らせています。文字のくくり方、値が空のときの表し方、時刻の書式は、どれも自前で書くと取りこぼしが出る種類の処理です。データベースの取り出し機能に任せて、その出力をそのまま応答へ流しています。
- 時刻は世界標準時。文字コードの目印(BOM)は付けず、圧縮したファイルとして返します。この CSV は「機械で読み直す監査の成果物」という位置づけで、表計算で開く用途は Box 側の CSV が担います。
- 自由項目は JSON の文字列としてそのまま 1 マスに入ります。改行が含まれることがありますが、くくり方が正しいので表計算でも崩れません。
- 対象の名前(ファイル名など)は、種類ごとの条件つきの結合 3 本で取っています。1 行ごとに問い合わせる書き方にすると 100 万行で破綻します。
大きい取り出しの裏側Background Export
バッチへ渡す側の全体を、時系列で追います。要点は 3 つです。取り合いを防ぐ(同じ依頼が二重に走らない)、途中の状態を外に見せない(書き終わるまでファイルが現れない)、失敗を黙って終わらせない(台帳に残し、重大なアラートを上げる)。
実線は処理の依頼、破線は応答です。⑦ の「待機中のときだけ実行中へ」は、更新できた行数で判定しています。0 行なら二重の起動か再試行と判断して、何もせず正常に終了します(バッチの再試行が無害になる)。⑧ で条件をもう一度検証しているのは、台帳の条件の列が利用者が書ける列だからです。読み戻した値を組み立ての材料として信用しません。
失敗したときに何が起きるか
- 台帳を失敗にしてから、例外をそのまま投げ直します。バッチが異常終了するので、重大なアラートが上がります。
- 要求した人への失敗のメールは送りません。画面の一覧に失敗として出るうえ、管理者にはアラートが届くため、そこに 1 通足す価値が薄いという判断です。
- 起動そのものに失敗したときは、台帳の欄に定型文だけを書きます。生の例外文をそのまま入れると、クラウドのプロジェクト識別子や権限の名前が混じり、その欄は要求した人の画面にそのまま出るためです。詳しい情報は実行時のログにだけ残します。
- 長時間「実行中」のまま止まっている依頼は、月次の点検が失敗へ倒します。処理が強制終了した場合、再試行しても取り合い防止の判定に引っかかって何もせず終わるため、そのままだと永久に「実行中」で残ります。
4 つの歯止めGuardrails
生のデータを引ける窓口なので、権限だけでは足りません。次の 4 つを重ねています。
| 歯止め | 内容 | そう決めた理由 |
|---|---|---|
| 理由の必須化 | CSV の取り出しと、退避分の取得は、10〜256 文字の理由が必須。専用の列に保存する | あとから「なぜ取り出したか」を追える。長さの下限は「とりあえず 1 文字」を防ぐため |
| 回数の制限 | 取り出しは 1 利用者あたり 1 時間 5 回。カウンタが壊れているときは通す側に倒す | 機械的な連打だけを弾く。カウンタの障害で監査の点検の導線を塞ぐほうが困る |
| 閲覧の記録 | 一覧を引くたびに 1 行。条件は付けない(§03) | 過剰なアクセスを、あとから検知できるようにする |
| 出力の寿命 | 取り出したファイルは 7 日で自動削除。台帳の行は 90 日。8 日目以降は URL を出さない | 取り出した CSV が置き場に残り続けるほうが危ない。消えたあとに使える URL を出さないことも同じ理由 |
退避分の取得には回数の制限を掛けていません。掛けると通常の取り出しと枠を共有してしまい、片方が枯れるためです。こちらはシステム管理者に限定されており、発行がすべて記録される点で担保しています。
現時点の到達範囲Current Scope
この章で説明した窓口は 8 つとも実装済みで、本番で稼働しています。一方で、これを操作する画面はまだありません。
| 状態 | 対象 |
|---|---|
| 実装済み | 8 つの窓口(絞り込み・集計・CSV の取り出し・依頼の一覧と状態・子テーブルの一覧・退避分の取得・管理画面の閲覧記録)/取り出しのバッチ/月次の退避のバッチ/閲覧の記録/理由と回数の制限/出力の寿命の管理 |
| 今後実装 | これらを操作する管理画面。絞り込みの UI、時系列の一覧、集計のグラフ、取り出しのダイアログ、子テーブルの一覧のサイドパネル |
現状の確認と取り出しは、認証付きの直接の呼び出しで行っています。画面の側で使う部品(期間の選択・操作種別の絞り込み・操作者の絞り込み)は、Box のアクティビティで先に作り込んであり、そのうち操作種別の絞り込みはそのまま流用できる形になっています。残る 2 つは Box に依存した作りなので、全社向けにするには手を入れる必要があります。