本文へ移動
松葉 / Matsuba ・ 開発過程資料

全社の監査ログ/ Company-wide Audit

システム管理者が、生のまま引く

3 つの読み口のうち 1 つ目です。生のデータをそのまま引ける唯一の経路で、接続元の IP や端末情報も伏せません。伏せると調査の役に立たないためです。その代わりに、閲覧できる相手を絞り、取り出しに理由を要求し、閲覧そのものを毎回記録しています。

01

何ができるかCapabilities

窓口は 8 つです。すべてシステム管理者に限定し、2 段の保護を掛けています。1 段目でログインを確かめ、2 段目で全社の管理権限を確かめます。権限が無い相手には 404 を返し、そういう窓口があること自体を伏せます。

Eight Endpoints
窓口できること上限・条件
一覧9 条件で絞り込み、新しい順に並べて取る。続きは不透明な印で辿る1 ページ 50 件
集計期間内の件数を、時間単位または日単位のまとまりで返す。多い操作の上位も返す期間は必須・上限あり
CSV の取り出し絞り込んだ範囲を CSV にする。その場で流すか、バッチへ渡すかを内部で決める理由 10〜256 文字必須
1 時間 5 回
依頼の一覧バッチへ渡した取り出しの依頼を新しい順に見る90 日分・50 件
依頼 1 件の状態進み具合を見る。成功していればダウンロード用の URL を都度発行するURL は 15 分
子テーブルの一覧現役・切り離し待ち・退避済みの 3 つのリストを 1 回で返す—
退避分の取得2 年を超えて外に出た月のファイルへ、ダウンロード用の URL を発行する理由 10〜256 文字必須
URL は 15 分
管理画面の閲覧記録管理画面を開いたこと自体を記録する—
02

絞り込みと続きの取り方Filter & Paging

調査で使う操作は「期間を決めて、対象を絞って、新しい順に辿る」の 3 つに集約されます。上段が業務としての流れ、下段がそれをどう実現しているかです。

図 04 · 絞り込みと続きの取り方 — 業務の流れと、その実現方法
業務としての流れ① 期間を決める「先月の 1 か月」「退職日の前後 1 週間」② 対象を絞るBox・操作者・操作種別対象の種類と識別子③ 新しい順に見る1 ページ 50 件操作者名・対象名も一緒に④ 続きを読む「印」を渡して次の 50 件同時刻の行も取りこぼさないそれをどう実現しているか開始以上・終了未満同じ行が隣り合う 2 期間の両方に現れない範囲外の月は走査から丸ごと落ちる9 条件を掛け合わせる期間・Box・操作者・行為者の種類・操作種別・対象 2 種・リクエストの識別子操作種別は上限あり組み立ては 1 か所一覧・その場で流す CSV・バッチの取り出しの 3 経路が同じ組み立てを通る対象名は条件つきの結合 3 本複合キーで続きを取る(発生時刻, 連番)を不透明な印にして返す壊れた印は 400 にせず先頭ページへ戻す

続きの位置に「発生時刻」だけを使うと、同じ時刻の行が 50 件目と 51 件目に分かれたときに取りこぼしか重複が起きます。連番を組み合わせて必ず一意な順序にすることで、これを防いでいます。並び順は主キーがそのまま供給するので、並べ替えのための追加処理は発生しません。

配列を運ぶときの落とし穴: 操作種別は複数指定できます。URL 上の形は同じキーを繰り返す方式(?actions=a&actions=b)で、カンマ連結は使いません。ここを素直に「配列」として検証すると、1 個だけ選んだときに拒否され、2 個以上なら通るという左右非対称な故障になります(1 個のときは配列ではなく単一の値として届くため)。共通のヘルパを必ず通す規律にして、URL の往復を検査する自動テストで固定しています。
03

閲覧そのものを記録するAuditing the Auditor

一覧を引くと、そのリクエストごとに必ず 1 行が記録に増えます。目的は、システム管理者が取引先のデータを必要以上に見ていないかを、あとから検知できるようにすることです。

記録する中身は、絞り込みの要約と「新規の検索か、続きの読み込みか」の手掛かりだけです。返した行の中身は入れません(同じ内容を二重に持つことになり、量も増えるため)。

記録に条件を付けない

ここは実装の途中で 2 回考え直した箇所です。当初は「無駄な行を増やさない」ために、記録する条件を置いていました。しかし条件の材料が続きの位置を示す印である以上、その印は監査される本人が握っている値です。つまり条件は原理的に操作できます。実際に 2 通りとも外側から破れました。

Why the Condition Was Removed
置いた条件破れ方
続きの印が付いていたら記録しない空の印や壊れた印を渡すと、仕組みは先頭のページを返します(古い URL や並行アクセスで 500 にしないための安全弁)。データは返るのに記録されない
続きの印を実際に使ったときだけ記録しない印は形だけを確かめており、値の範囲は見ていません。ありえない未来の時刻を詰めた印を渡すと、条件が全行に当てはまるので先頭のページがそのまま返り、しかも「印を使った」と判定される。細工をしなくても、一度得た印を別の絞り込みで使い回すだけで成立しうる

対処として、条件そのものを撤廃しました。「印の検証を厳しくする」「印に署名を付ける」という方向は採っていません。厳しくした条件の外側には必ずまた「返るのに残らない」経路ができるうえ、壊れた印を先頭に戻す挙動はほかの画面と共通で、この 1 か所だけ違う挙動にしたくなかったためです。

行が増えることは、過剰なアクセスの検知という目的にとっては情報が増えることです。ページ送りも 1 行として積まれるので、「何ページ目まで辿ったか」まで読めます。記録に入れている「新規か続きか」の手掛かりは、あくまで読む側の目安で、判定には使いません(本人が操作できる値だからです)。

この決めごとを守る自動テスト: 「どんな印を渡しても記録が 1 行増える」ことを、5 通り(未指定・空文字・復号できない値・正しい値・ありえない未来を詰めた値)で表にして固定しています。同時に「そのとき何が返ったか」も固定しているので、何も返らないケースを合格にしてしまうことがありません。
04

集計Aggregation

一覧の前に「いつ、どのくらい、どんな操作があったか」を掴むための窓口です。期間の指定は必須で、上限があります。

  • まとまりの単位は自動で決まります。期間が 24 時間以内なら時間単位、それより長ければ日単位です。
  • 区切りは日本時間です。世界標準時で区切ると、日本の 9 時前の操作が前日に入ってしまい、業務の「日」と合いません。
  • 返すのは、まとまりごとの件数と、その期間に多かった操作の上位です。
  • 絞り込みの条件は一覧と同じものが使えます(期間・Box・操作者・行為者の種類・操作種別)。

集計・子テーブルの一覧・依頼の一覧については、個別の閲覧記録を付けていません。これらはどれも一覧を引く前後に必ず通る窓口で、一覧の記録に含まれると判断したためです。

05

CSV の取り出しCSV Export

画面で辿るのではなく、まとめて手元に落として調べたい場面のための窓口です。期間の長さと件数の見込みで、その場で流すかバッチへ渡すかを内部で決めます。要求する側はどちらになるかを指定しません。

図 05 · CSV の取り出しの分岐 — その場で流すか、バッチへ渡すか
受付理由を 10〜256 文字で必須にする / 同じ利用者は 1 時間に 5 回まで(管理者側と Box 側で共用)判定 — 2 つの条件をどちらも満たすか① 期間が 31 日以下 / ② 件数の見込みが 10 万行以下見込みは 10 万 1 件で打ち切って数える(全件を数えると数十秒かかりうる)満たす満たさないその場で流すデータベースの取り出し機能の出力を、そのまま応答へ分割して流す接続はプール経由ではなく直結を使う(プール経由は実行時間の上限で切られる)記録は完走したときだけ残すバッチへ渡す依頼を台帳へ積み、受付を記録するバッチを起動し、202 で即座に応答する(起動はトランザクションの外で行う)完了すると 24 時間有効な URL をメールで届ける共通の後段 — 置き場と寿命出力先は依頼ごとの専用の場所。オブジェクトは 7 日で自動削除され、台帳の行は 90 日で消える8 日目以降の依頼は「期限切れ」として扱い、ダウンロード用の URL を発行しない

「その場で流す」側で直結の接続を使っているのは、実測にもとづく判断です。プールを経由すると全体に実行時間の上限が掛かっており、数百万行の取り出しは確実に途中で切られます。直結側では上限を長めに明示して開いています。

CSV は 19 列。データベースに作らせる

列は次の 19 です。並びは古い順(画面の新しい順とは逆)にしています。時系列で読み下すほうが調査に向くためです。

19 Columns of the Admin CSV
区分列
いつ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 万行で破綻します。
(画面の画像は掲載していません)
19 列がすべて並んだ状態。左から時刻・行為者・操作・対象と続き、右端の自由項目には操作ごとの中身が JSON のまま入ります。
取り出しの中身をログとメールに出さない: CSV の本文は、メールにも実行時のログにも一切出しません。個人情報と操作の詳細がそのまま複製されるためです。メールに載るのはダウンロード用の URL と有効期限だけです。
06

大きい取り出しの裏側Background Export

バッチへ渡す側の全体を、時系列で追います。要点は 3 つです。取り合いを防ぐ(同じ依頼が二重に走らない)、途中の状態を外に見せない(書き終わるまでファイルが現れない)、失敗を黙って終わらせない(台帳に残し、重大なアラートを上げる)。

図 06 · 大きい取り出しの裏側 — 受付から、メールでの受け取りまで
管理者のブラウザmatsuba-apiPostgreSQLmatsuba-batchaudit-log-export監査エクスポートバケット通知基盤 /Resend① 絞り込みと理由を添えて依頼する② 件数の見込みを尋ねる(10 万 1 件で打ち切り)③ 10 万行を超える④ 依頼を台帳へ積み、受付を記録する(同じ区切り)⑤ バッチを起動する(区切りの外で)⑥ 202 受け付けました⑦ 待機中のときだけ実行中へ(取り合いを防ぐ)⑧ 絞り込み条件をもう一度検証する(台帳の値を信用しない)⑨ データベースから取り出す⑩ 圧縮しながら書く(書き終わるまで現れない)⑪ 検査値と容量を返す⑫ 24 時間有効な URL を発行する⑬ 台帳を成功へ・記録を残す・通知を積む(同じ区切り)⑭ 積まれた通知を払い出す⑮ URL 入りのメールが届く⑯ 状態を尋ねる⑰ 15 分の URL を都度発行(7 日超は期限切れ)

実線は処理の依頼、破線は応答です。⑦ の「待機中のときだけ実行中へ」は、更新できた行数で判定しています。0 行なら二重の起動か再試行と判断して、何もせず正常に終了します(バッチの再試行が無害になる)。⑧ で条件をもう一度検証しているのは、台帳の条件の列が利用者が書ける列だからです。読み戻した値を組み立ての材料として信用しません。

失敗したときに何が起きるか

  • 台帳を失敗にしてから、例外をそのまま投げ直します。バッチが異常終了するので、重大なアラートが上がります。
  • 要求した人への失敗のメールは送りません。画面の一覧に失敗として出るうえ、管理者にはアラートが届くため、そこに 1 通足す価値が薄いという判断です。
  • 起動そのものに失敗したときは、台帳の欄に定型文だけを書きます。生の例外文をそのまま入れると、クラウドのプロジェクト識別子や権限の名前が混じり、その欄は要求した人の画面にそのまま出るためです。詳しい情報は実行時のログにだけ残します。
  • 長時間「実行中」のまま止まっている依頼は、月次の点検が失敗へ倒します。処理が強制終了した場合、再試行しても取り合い防止の判定に引っかかって何もせず終わるため、そのままだと永久に「実行中」で残ります。
07

4 つの歯止めGuardrails

生のデータを引ける窓口なので、権限だけでは足りません。次の 4 つを重ねています。

Four Guardrails
歯止め内容そう決めた理由
理由の必須化CSV の取り出しと、退避分の取得は、10〜256 文字の理由が必須。専用の列に保存するあとから「なぜ取り出したか」を追える。長さの下限は「とりあえず 1 文字」を防ぐため
回数の制限取り出しは 1 利用者あたり 1 時間 5 回。カウンタが壊れているときは通す側に倒す機械的な連打だけを弾く。カウンタの障害で監査の点検の導線を塞ぐほうが困る
閲覧の記録一覧を引くたびに 1 行。条件は付けない(§03)過剰なアクセスを、あとから検知できるようにする
出力の寿命取り出したファイルは 7 日で自動削除。台帳の行は 90 日。8 日目以降は URL を出さない取り出した CSV が置き場に残り続けるほうが危ない。消えたあとに使える URL を出さないことも同じ理由

退避分の取得には回数の制限を掛けていません。掛けると通常の取り出しと枠を共有してしまい、片方が枯れるためです。こちらはシステム管理者に限定されており、発行がすべて記録される点で担保しています。

08

現時点の到達範囲Current Scope

この章で説明した窓口は 8 つとも実装済みで、本番で稼働しています。一方で、これを操作する画面はまだありません。

Implemented / Not Yet
状態対象
実装済み8 つの窓口(絞り込み・集計・CSV の取り出し・依頼の一覧と状態・子テーブルの一覧・退避分の取得・管理画面の閲覧記録)/取り出しのバッチ/月次の退避のバッチ/閲覧の記録/理由と回数の制限/出力の寿命の管理
今後実装これらを操作する管理画面。絞り込みの UI、時系列の一覧、集計のグラフ、取り出しのダイアログ、子テーブルの一覧のサイドパネル

現状の確認と取り出しは、認証付きの直接の呼び出しで行っています。画面の側で使う部品(期間の選択・操作種別の絞り込み・操作者の絞り込み)は、Box のアクティビティで先に作り込んであり、そのうち操作種別の絞り込みはそのまま流用できる形になっています。残る 2 つは Box に依存した作りなので、全社向けにするには手を入れる必要があります。

なぜ API を先に作ったか: 画面が無くても、記録はその日から溜まり続けます。取り出す手段が無いまま溜めると、いざ調査が必要になった日に手も足も出ません。窓口と長期保管を先に完成させたのは、そのためです。画面はあとから足せますが、記録されていなかった過去は足せません。