AWS帳號充值優惠 如何限制指定 IP 訪問 AWS S3
第一章:先想清楚,你要限制的是什麼
很多人看到「限制指定 IP 訪問 AWS S3」時,第一反應是用 Bucket Policy 擋住不該來的人。但現實是:你能控制的粒度,取決於「訪問路徑」與「你要防的攻擊型態」。
在 S3 的世界裡,常見的訪問方式大致分三類:
- 直接存取 S3:使用公開 S3 endpoint(例如
https://bucket-name.s3.amazonaws.com),由用戶端 IP 直接連到 AWS。 - 透過中介層存取:例如 CloudFront、API Gateway、或其他代理服務。
- 透過 VPC Endpoint 存取:你的資源在 VPC 裡,使用 PrivateLink(S3 Gateway/Interface Endpoint)走私有路徑。
你要限制「指定 IP」時,答案通常是:先決定訪問是走哪條路。若是直接公開 endpoint,那 Bucket Policy 是核心;若是走 CloudFront 或 VPC Endpoint,還需要配合其他層的條件,才能做到真正可控且可驗證。
第二章:Bucket Policy 能不能依 IP 限制?能,但要理解條件
AWS S3 的 Bucket Policy 支援用 Condition 判斷請求來源 IP。常見的條件鍵是:
aws:SourceIp:依請求的來源 IP 判斷。NotIpAddress或IpAddress:配合允許/拒絕邏輯。
概念很簡單:你可以在 Bucket Policy 裡寫「只有特定 IP(或網段)可以執行某些動作,例如 s3:GetObject、s3:PutObject」。
但要注意兩件事:
- 來源 IP 以你能看到的那個為準:如果你是從 CloudFront 進來,來源 IP 可能是 CloudFront 的地址,而不是使用者的真實 IP。這時直接用
aws:SourceIp可能達不到你想像的效果。 - 不是所有請求都會同樣帶來可用的 IP:例如某些代理、特定 AWS 服務或特殊路徑,條件判斷的結果可能不是你期待的來源。
因此,做之前你應該先決定:你要限制的「IP」是用戶端的真實 IP,還是你實際能控制/可觀測的 IP(例如公司出口 NAT 的 IP)?多數公司情境下,限制公司出口 NAT 的 IP 最實際,因為你穩定掌握、也能驗證。
第三章:最常用的做法——在 Bucket Policy 以 IP 白名單限制
下面提供一個典型模板:只允許特定 IP 網段讀取某個 prefix 下的物件。你可以依需求改成允許寫入、或針對特定動作。
3.1 只允許指定 IP 下載物件(GetObject)
假設你的 Bucket 名稱是 my-bucket,你要限制的物件範圍是 public/ 目錄,允許的來源網段是:
203.0.113.10/32198.51.100.0/24
你可以把 Bucket Policy 設定成類似以下內容(把資源 ARN 與動作改成你的實際需求):
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowGetObjectFromAllowedIps",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-bucket/public/*",
"Condition": {
"IpAddress": {
"aws:SourceIp": [
"203.0.113.10/32",
"198.51.100.0/24"
]
}
}
}
]
}
說明:這個政策表示:只有符合 IP 條件的請求才能進行 s3:GetObject。其他 IP 的讀取請求會因為缺乏 Allow 而被拒絕(前提是你的 Bucket 沒有其他更寬鬆的政策放行)。
如果你的 Bucket 目前可能已有允許公開讀取的 Statement,請先清掉或調整,否則「允許」會覆蓋你設定的限制邏輯。
3.2 只允許指定 IP 上傳物件(PutObject)
AWS帳號充值優惠 若你要限制上傳:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowPutObjectFromAllowedIps",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::my-bucket/upload/*",
"Condition": {
"IpAddress": {
"aws:SourceIp": [
"203.0.113.10/32",
"198.51.100.0/24"
]
}
}
}
]
}
提醒:上傳通常還會受到 IAM Identity policy、以及是否使用 SSE、是否需要特定 ACL 或 Bucket 所有權設定等影響。Bucket Policy 只是其中一層;你要確保整體授權沒有互相打架。
第四章:更安全的策略——先拒絕其他 IP,再允許白名單
有些團隊喜歡用「先明確拒絕不在名單的來源」的方式,因為更容易看出意圖,也更不容易被其他政策意外放行。AWS 的評估規則通常採用顯式 Deny 優先於 Allow。
4.1 使用 Deny NotIpAddress 把非白名單擋掉
例如:拒絕任何不在允許清單內的 IP 進行讀取:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyGetObjectFromNonAllowedIps",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-bucket/public/*",
"Condition": {
"NotIpAddress": {
"aws:SourceIp": [
"203.0.113.10/32",
"198.51.100.0/24"
]
}
}
}
]
}
這樣做的邏輯是:只要來源 IP 不在白名單,就直接 Deny。白名單內是否允許,仍取決於你是否提供了 Allow(或是否存在其他 Allow)。
實務上常見的做法是配合 IAM 使用:Bucket Policy 的 Allow 只開最必要的動作,並讓 Deny 作為「最終保險」。但如果你直接用 Deny 來「硬擋」,仍要確保你沒有其他會放行的公開政策。
4.2 Deny 與 Allow 並用的完整範例
假設目標是:白名單 IP 可以讀取;非白名單一律拒絕。你可以用:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyGetObjectFromNonAllowedIps",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-bucket/public/*",
"Condition": {
"NotIpAddress": {
"aws:SourceIp": [
"203.0.113.10/32",
"198.51.100.0/24"
]
}
}
},
{
"Sid": "AllowGetObjectFromAllowedIps",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-bucket/public/*",
"Condition": {
"IpAddress": {
"aws:SourceIp": [
"203.0.113.10/32",
"198.51.100.0/24"
]
}
}
}
]
}
AWS帳號充值優惠 這會把意圖寫得更明確:不在名單的直接拒絕,在名單的才允許。
AWS帳號充值優惠 第五章:常見坑位與排查方法
「貼了 Bucket Policy 但還是能存取」或「看起來擋了,但內網也被擋」是最常見的狀況。通常不是政策語法問題,而是你對條件判斷的前提不一致。
5.1 你以為限制的是用戶 IP,其實是出口 NAT 或代理 IP
最常見的狀況是:你的公司用戶在瀏覽器發起請求,但流量經過 NAT/防火牆,AWS 看到的來源 IP 是 NAT 的出口 IP。此時你應該把允許清單設成「公司出口 IP」,而不是用戶自己的內網地址。
AWS帳號充值優惠 如何確認?你可以在被允許/被拒絕時比較:
- 用你的實際客戶端測試(從公司網路 vs 家用網路)
- 看 AWS CloudTrail 或 S3 server access logs(如啟用)中顯示的請求來源(有些情境下會有線索)
- 在測試時臨時允許你的 NAT 出口 IP,確認策略成立後再收斂
5.2 有其他 Bucket Policy 或 ACL 放行了
如果你的 Bucket 原本設定了「公開讀取」,那麼加一條白名單 Allow 不一定能達到你想要的效果。公開讀取可能是另一條 Statement 在允許。AWS 的政策評估中,Deny 會優先於 Allow,但如果你只用了 Allow,而且又存在其他 Allow,就會發生「看起來限制失效」。
解法:
- 先檢查 Bucket Policy 的整份內容是否還有公開讀取、特定服務放行等。
- 必要時改用
Deny+NotIpAddress作最終保險。
5.3 忘了處理 ListBucket(列出物件)
很多人只限制了 s3:GetObject,但前端或程式實際使用的是:
s3:ListBucket(例如列出 prefix 下的檔案)- 然後再
s3:GetObject
結果就是:你可以下載某個檔案,但列目錄失敗。若你要全面限制,應該把需要的動作一起管理,例如:
- 限制
arn:aws:s3:::my-bucket(ListBucket 通常是 Bucket ARN,不是 object ARN) - 限制
arn:aws:s3:::my-bucket/prefix/*(GetObject/PutObject 通常是 object ARN)
5.4 分區端點、地區與 ARN 寫錯
Policy 的 ARN 如果寫錯(例如 bucket 名稱或 prefix 目標),會導致條件根本沒有匹配到你想管的資源。建議你在套用政策前,先把這兩件事核對:
- Resource 的 ARN 是否正確:
arn:aws:s3:::bucketvsarn:aws:s3:::bucket/* - prefix 的路徑是否符合你實際物件存放位置
第六章:如果你用的是 CloudFront,IP 限制要換思路
在很多網站架構中,S3 不會直接暴露給瀏覽器,而是由 CloudFront 轉發。這時「指定 IP 訪問 S3」的直覺就會失效:CloudFront 到 S3 的來源 IP 可能是 CloudFront 的網段,而不是使用者的真實 IP。
AWS帳號充值優惠 此時你要做的通常是:
- 把 IP 限制放在 CloudFront 層(例如搭配 WAF),或
- 使用可以判斷真實來源的機制(需看你如何設定原始請求頭與 WAF 規則)。
換句話說:當訪問路徑被中介層包住,你就不要硬在 S3 用 aws:SourceIp 期待看到用戶端 IP。要在最靠近真實來源的層做控管。
第七章:若你的服務在 VPC,優先用 VPC Endpoint 把路徑鎖死
有另一種更乾淨的安全策略:不是讓流量到公網 endpoint,再靠 IP 條件判斷,而是讓請求根本只能走你的私有路徑。
對 S3 來說,你可以使用 VPC Gateway Endpoint(或在特定情境使用 Interface Endpoint 的方案)。當流量走 VPC Endpoint,你可以搭配端點的路由與安全規則,把外部來源大幅排除。
這樣做的優點是:
- 你控制的是「網路可達性」,不只是「授權判斷」。
- 可降低你對 SourceIp 觀測誤差的依賴。
當然,若你仍希望精準限制「哪些來源主機可以用端點存取」,你可能還需要搭配 IAM 條件(例如要求特定角色、或在端點政策中限制)。
第八章:實際落地步驟(建議流程)
以下是一套可操作、也比較不容易翻車的流程。你不需要照單全抄,但邏輯值得沿用。
8.1 建立目標清單:動作、物件範圍、允許來源
把需求拆成三欄:
- 動作:
s3:GetObject、s3:PutObject、s3:ListBucket、或其他。 - 物件範圍:例如
public/*或upload/*。 - 允許來源 IP:單一 IP 用 /32,網段用 /24、/20 等。
很多失敗不是因為策略寫得不夠厲害,而是你其實漏了某個動作或路徑。
8.2 先用只允許(Allow)測試,再進階到 Deny 保險
建議從最小變更開始:
- 先確定你的 Allow 能命中目標資源與動作
- 再加入 Deny(NotIpAddress)讓效果更穩定
尤其是你不確定 SourceIp 會不會是你想像的 IP 時,先用 Allow + 實測會更快找到差距。
8.3 用同一條路徑驗證:兩邊都測(允許與拒絕)
驗證不要只測「能不能存取」。你也要測「不能存取」是否真被拒絕。
測試方式可用:
- 從允許網路環境請求(例如公司網路)下載一個物件
- 從非允許環境請求(例如家用網路、手機熱點)下載同一個物件
如果兩邊都能存取,通常代表你仍有公開 Allow 或其他政策在放行。
AWS帳號充值優惠 8.4 開啟存取紀錄與告警(可選但很有價值)
要讓限制真正可維運,你需要知道「有人試了什麼」與「被拒的原因是什麼」。
你可以考慮:
- 啟用 S3 access logs 或用 CloudTrail 配合審計
- 把常見拒絕模式納入監控(例如短時間大量 403)
當你往後調整 IP 清單或重構架構時,這些訊號能幫你快速定位是政策問題還是來源 IP 變動。
第九章:範例整合——限制整個 Bucket 的讀寫(依你的情境調整)
AWS帳號充值優惠 如果你需要更「一口氣」的限制,例如:整個 Bucket 的 object 只允許特定 IP 讀取或寫入。以下提供示意,但你仍應根據你 Bucket 的現況調整(尤其是是否已有公開策略)。
9.1 只允許指定 IP 做讀取(GetObject),其他動作不放行
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyGetObjectFromNonAllowedIps",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-bucket/*",
"Condition": {
"NotIpAddress": {
"aws:SourceIp": [
"203.0.113.10/32",
"198.51.100.0/24"
]
}
}
},
{
"Sid": "AllowGetObjectFromAllowedIps",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-bucket/*",
"Condition": {
"IpAddress": {
"aws:SourceIp": [
"203.0.113.10/32",
"198.51.100.0/24"
]
}
}
}
]
}
9.2 只允許指定 IP 做讀寫(GetObject/PutObject),同時限制 ListBucket
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyListBucketFromNonAllowedIps",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::my-bucket",
"Condition": {
"NotIpAddress": {
"aws:SourceIp": [
"203.0.113.10/32",
"198.51.100.0/24"
]
}
}
},
{
"Sid": "AllowListBucketFromAllowedIps",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::my-bucket",
"Condition": {
"IpAddress": {
"aws:SourceIp": [
"203.0.113.10/32",
"198.51.100.0/24"
]
}
}
},
{
"Sid": "DenyGetPutFromNonAllowedIps",
"Effect": "Deny",
"Principal": "*",
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": "arn:aws:s3:::my-bucket/*",
"Condition": {
"NotIpAddress": {
"aws:SourceIp": [
"203.0.113.10/32",
"198.51.100.0/24"
]
}
}
},
{
"Sid": "AllowGetPutFromAllowedIps",
"Effect": "Allow",
"Principal": "*",
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": "arn:aws:s3:::my-bucket/*",
"Condition": {
"IpAddress": {
"aws:SourceIp": [
"203.0.113.10/32",
"198.51.100.0/24"
]
}
}
}
]
}
再次提醒:如果你還允許匿名訪問、或使用預簽 URL(Presigned URL),就要重新評估限制策略能否符合你的目的。
第十章:最後確認——你想要的是「安全」還是「可用性」
IP 限制能有效降低風險,但它不是魔法。它的強度來自「你掌握來源網段的穩定性」以及「你的訪問路徑不會把 SourceIp 換掉」。
如果你的團隊常見情況是:辦公室網路固定、公司出口 IP 可控,那用 Bucket Policy 做白名單/黑名單是合理且經濟的。
若你的場景牽涉多地辦公、臨時網路、或透過 CloudFront 等中介層,單純在 S3 用 aws:SourceIp 可能會讓你反覆調參。這時應該把控管放到更接近真實來源的層(例如 WAF),或改走 VPC Endpoint 讓網路可達性先被鎖住。
做完策略後,最重要的不是把政策寫得多漂亮,而是能否在真實環境中穩定運作:允許的人能用、禁止的人真的被擋、而且你能在半年後仍然看懂為什麼要這樣設。
把目標講清楚、把測試做完整、把例外處理好,限制指定 IP 訪問 S3 就會變成一個可靠的安全機制,而不是一段需要猜的防火牆。


