Skip to content
Alex Chih

← 文章 aws · 2026年8月10日 · 3 min 閱讀

用 Athena 查 S3 access logs,零基礎設施成本

S3 存取紀錄開了就該查得動:date-based partitioning、partition projection,加上幾條實際在用的 SQL。


S3 access log 常常開了就放著:合規要求要留,於是留了,然後沒有人查過一次。要查存取紀錄,不必架 SIEM、不必開 OpenSearch;log 已經躺在 bucket 裡,Athena 直接對它下 SQL 就夠了。這篇是我實際在用的最小配置。

前置:讓 log 自帶日期路徑

S3 的 server access logging 預設把所有 log 物件丟在同一個 prefix 底下,沒有任何日期結構——這種平鋪結構 Athena 查起來只能全量掃描。先到來源 bucket 的 logging 設定裡打開 date-based partitioning,之後的 log 會照這個結構落地:

s3://my-log-bucket/logs/123456789012/ap-northeast-1/my-data-bucket/2026/08/10/

日期可以選事件時間或送達時間,查安全事件用事件時間。注意這個設定只影響之後的新 log;舊 log 還是平的,兩批要嘛分開建表,要嘛忍痛只查新的。

建表: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}'
);

欄位與 regex 是官方文件的標準版,照抄即可;要改的只有 LOCATION 路徑和 projection.dt.range 的起始日。之後每條查詢都帶 dt 條件,Athena 就只讀那幾天的檔案。

我平常先查這三件事

誰動過敏感 prefix——資安檢視的第一條:

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 對照,從沒被 GET 過的 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 按掃描量計價(每 TB 5 美元)。partition 和日期條件都有設好時,查一個月的 access log 通常只要幾分錢;真正貴的是忘記帶 dt 條件的那一次全量掃描,所以把常用查詢存成 saved query,條件寫死在裡面。

邊界先講清楚

  • Server access log 是 best-effort:偶爾漏、偶爾重複,拿來做趨勢與調查夠用,拿來當稽核證據不行——那是 CloudTrail data events 的工作(另外計費)。
  • Log 送達有延遲,通常是幾小時等級,不是即時告警的料。
  • Log bucket 自己要設 lifecycle,不然它會長成帳單上最肥的一項;也不要把 log bucket 的 access log 指回自己,會遞迴。

Alex Chih

Security consultant & instructor · AWS / Azure

與我合作