← 記事 aws · 2026年8月10日 · 3 min 読む
追加インフラなしで S3 アクセスログを Athena で調べる
S3 アクセスログをちゃんと調べられる状態にする。date-based partitioning、partition projection、そして実際に使っている SQL。
S3 のアクセスログは、有効化したまま放置されがちです。コンプライアンス要件で残してはいるものの、一度もクエリされたことがない——そんなバケットは珍しくありません。調べるために SIEM や OpenSearch クラスターを立てる必要はありません。ログはすでにバケットに置いてあり、Athena で直接クエリできます。これは私が実際に使っている最小構成です。
前準備:ログに日付パスを持たせる
S3 の server access logging は、デフォルトではすべてのログを日付構造のない一つのプレフィックスに書き込みます。この平坦な構造では、Athena のクエリは毎回フルスキャンになります。まずソースバケットのログ設定で date-based partitioning を有効にしてください。以降のログは次の構造で配置されます。
s3://my-log-bucket/logs/123456789012/ap-northeast-1/my-data-bucket/2026/08/10/
日付はイベント時刻か配信時刻を選べます。セキュリティ調査ならイベント時刻です。この設定は有効化後の新しいログにしか効かない点に注意してください。古いログは平坦なままなので、別テーブルを作るか、新しいログだけを対象にするか、どちらかです。
テーブル定義:partition projection、Glue crawler なし
日付がパスに入っていれば、partition projection で Athena がパスから直接パーティションを導出します。crawler も MSCK REPAIR も不要です。
CREATE EXTERNAL TABLE s3_access_logs (
bucketowner STRING, bucket_name STRING, requestdatetime STRING,
remoteip STRING, requester STRING, requestid STRING, operation STRING,
key STRING, request_uri STRING, httpstatus STRING, errorcode STRING,
bytessent BIGINT, objectsize BIGINT, totaltime STRING,
turnaroundtime STRING, referrer STRING, useragent STRING,
versionid STRING, hostid STRING, sigv STRING, ciphersuite STRING,
authtype STRING, endpoint STRING, tlsversion STRING
)
PARTITIONED BY (`dt` STRING)
ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.RegexSerDe'
WITH SERDEPROPERTIES (
'input.regex' = '([^ ]*) ([^ ]*) \\[(.*?)\\] ([^ ]*) ([^ ]*) ([^ ]*) ([^ ]*) ([^ ]*) ("[^"]*"|-) (-|[0-9]*) ([^ ]*) ([^ ]*) ([^ ]*) ([^ ]*) ([^ ]*) ([^ ]*) ("[^"]*"|-) ([^ ]*)(?: ([^ ]*) ([^ ]*) ([^ ]*) ([^ ]*) ([^ ]*) ([^ ]*))?.*$'
)
LOCATION 's3://my-log-bucket/logs/123456789012/ap-northeast-1/my-data-bucket/'
TBLPROPERTIES (
'projection.enabled' = 'true',
'projection.dt.type' = 'date',
'projection.dt.format' = 'yyyy/MM/dd',
'projection.dt.interval' = '1',
'projection.dt.interval.unit' = 'DAYS',
'projection.dt.range' = '2026/01/01,NOW',
'storage.location.template' = 's3://my-log-bucket/logs/123456789012/ap-northeast-1/my-data-bucket/${dt}'
);
カラムと正規表現は公式ドキュメントの標準版なので、そのまま使えます。書き換えるのは LOCATION のパスと projection.dt.range の開始日だけ。以降のクエリはすべて dt 条件を付ければ、Athena はその日数分のファイルしか読みません。
最初に見る三つのこと
機密プレフィックスに誰が触れたか——セキュリティレビューの最初の一問です。
SELECT requestdatetime, requester, remoteip, key
FROM s3_access_logs
WHERE dt BETWEEN '2026/08/01' AND '2026/08/10'
AND key LIKE 'confidential/%'
AND operation LIKE 'REST.GET%'
ORDER BY requestdatetime;
4xx がどこかの時間帯で急増していないか——スキャンや総当たりの兆候です。まず集計して、それから requester と remoteip を追います。
SELECT dt, httpstatus, count(*) AS hits
FROM s3_access_logs
WHERE dt >= '2026/08/01' AND httpstatus LIKE '4%'
GROUP BY dt, httpstatus
ORDER BY hits DESC;
各オブジェクトが最後に読まれたのはいつか——S3 Inventory と突き合わせて、ここに一度も現れない key は lifecycle policy の候補です。
SELECT key, max(requestdatetime) AS last_read
FROM s3_access_logs
WHERE dt >= '2026/01/01' AND operation LIKE 'REST.GET%'
GROUP BY key;
コスト
Athena の課金はスキャン量ベースで、1TB あたり 5 USD。パーティションと日付条件が効いていれば、一か月分のアクセスログのクエリは数セントで済みます。高くつくのは dt 条件を忘れた一回のフルスキャンです。よく使うクエリは条件を書き込んだ状態で saved query にしておきましょう。
限界も先に
- Server access log はベストエフォートです。まれに欠け、まれに重複します。傾向分析と調査には十分ですが、監査証跡としては使えません——それは CloudTrail data events の仕事です(別課金)。
- 配信には数時間の遅延があります。リアルタイムアラートの入力には向きません。
- ログバケット自体に lifecycle を設定しないと、請求書でいちばん太い行に育ちます。ログバケットのアクセスログを自分自身に向けるのも避けてください。再帰します。
Alex Chih
Security consultant & instructor · AWS / Azure