AWS Learning
Storage

S3 Security

Access Control, Encryption, Block Public Access, Pre-signed URLs, VPC Endpoints

Tổng quan

S3 Security dựa trên nguyên tắc Defense in Depth - nhiều lớp bảo vệ chồng lên nhau:

┌─────────────────────────────────────────────────────────────────┐
│                    S3 SECURITY LAYERS                           │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  Layer 1: Network                                               │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │ VPC Endpoints, S3 Access Points, Block Public Access    │    │
│  └─────────────────────────────────────────────────────────┘    │
│                             │                                   │
│  Layer 2: Identity & Access                                     │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │ IAM Policies, Bucket Policies, ACLs, Pre-signed URLs    │    │
│  └─────────────────────────────────────────────────────────┘    │
│                             │                                   │
│  Layer 3: Data Protection                                       │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │ Encryption (SSE-S3, SSE-KMS, SSE-C), Versioning         │    │
│  └─────────────────────────────────────────────────────────┘    │
│                             │                                   │
│  Layer 4: Compliance & Audit                                    │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │ Object Lock, Access Logs, CloudTrail, Macie             │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Mặc định: Buckets và objects là private - chỉ owner có quyền access.


Access Control

So sánh các phương pháp

MethodScopeAttach vàoUse Case
IAM PoliciesUser/RoleIAM User, Group, RoleInternal users, services trong cùng account
Bucket PoliciesBucketS3 BucketCross-account, public access, IP restrictions
Access PointsNamed endpointS3 BucketMulti-team access, simplified permissions
ACLsObject/BucketObject hoặc Bucket⚠️ Legacy, không khuyến khích

IAM Policies vs Bucket Policies

┌─────────────────────────────────────────────────────────────────┐
│                    POLICY EVALUATION                            │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  Request đến S3:                                                │
│                                                                 │
│  1. Check IAM Policy (caller có permission không?)              │
│  2. Check Bucket Policy (bucket cho phép caller không?)         │
│  3. Check ACL (nếu enabled)                                     │
│                                                                 │
│  → Kết quả = UNION của Allow - Explicit Deny wins               │
│                                                                 │
│  Ví dụ:                                                         │
│  - IAM: Allow s3:GetObject                                      │
│  - Bucket Policy: Allow Principal: *                            │
│  - Có Explicit Deny? → DENY                                     │
│  - Không có Deny? → ALLOW                                       │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Bucket Policy Examples

1. Public Read (cho static website):

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "PublicReadGetObject",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::my-bucket/*"
    }
  ]
}

2. Cross-Account Access:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "CrossAccountAccess",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::123456789012:root"
      },
      "Action": ["s3:GetObject", "s3:PutObject"],
      "Resource": "arn:aws:s3:::my-bucket/*"
    }
  ]
}

3. Restrict by IP (chỉ cho phép từ office IP):

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "IPRestriction",
      "Effect": "Deny",
      "Principal": "*",
      "Action": "s3:*",
      "Resource": [
        "arn:aws:s3:::my-bucket",
        "arn:aws:s3:::my-bucket/*"
      ],
      "Condition": {
        "NotIpAddress": {
          "aws:SourceIp": ["203.0.113.0/24", "198.51.100.0/24"]
        }
      }
    }
  ]
}

4. Force HTTPS (deny HTTP):

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ForceHTTPS",
      "Effect": "Deny",
      "Principal": "*",
      "Action": "s3:*",
      "Resource": "arn:aws:s3:::my-bucket/*",
      "Condition": {
        "Bool": {
          "aws:SecureTransport": "false"
        }
      }
    }
  ]
}

S3 Access Points

Simplify access management cho shared datasets - tạo nhiều "cổng vào" riêng cho cùng 1 bucket.

Vấn đề: 1 Bucket Policy cho nhiều teams

Khi nhiều teams cùng dùng 1 bucket, bạn phải viết 1 bucket policy rất dài và phức tạp:

// Bucket policy KHỔNG LỒ - khó maintain, giới hạn 20KB!
{
  "Statement": [
    { "Sid": "DataScienceRead", "Principal": {"AWS": "role/data-science"}, "Resource": ".../ml/*" },
    { "Sid": "DataScienceWrite", "Principal": {"AWS": "role/data-science"}, "Resource": ".../ml/*" },
    { "Sid": "AnalyticsRead", "Principal": {"AWS": "role/analytics"}, "Resource": ".../analytics/*" },
    { "Sid": "MarketingRead", "Principal": {"AWS": "role/marketing"}, "Resource": ".../mkt/*" }
    // ... 50 statements nữa cho các team khác
  ]
}

Giải pháp: Mỗi team có Access Point riêng

┌─────────────────────────────────────────────────────────────────┐
│                    S3 ACCESS POINTS                             │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  Không có Access Points:                                        │
│  ═══════════════════════                                        │
│    Team A ──┐                                                   │
│    Team B ──┼──► [ 1 cửa vào + 1 policy dài ] ──► Bucket        │
│    Team C ──┘                                                   │
│                                                                 │
│  Có Access Points:                                              │
│  ═════════════════                                              │
│    Team A ──► [ Cửa A + policy A ] ──┐                          │
│    Team B ──► [ Cửa B + policy B ] ──┼──► Bucket                │
│    Team C ──► [ Cửa C + policy C ] ──┘                          │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Ví dụ cụ thể

Bucket company-data chứa 3 folders: /ml/, /analytics/, /mkt/

Access Point cho team Data Science - chỉ được đọc/ghi /ml/:

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": {"AWS": "arn:aws:iam::123456789012:role/data-science"},
    "Action": ["s3:GetObject", "s3:PutObject"],
    "Resource": "arn:aws:s3:us-east-1:123456789012:accesspoint/data-science/object/ml/*"
  }]
}

Access Point cho team Marketing - chỉ được đọc /mkt/:

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": {"AWS": "arn:aws:iam::123456789012:role/marketing"},
    "Action": "s3:GetObject",
    "Resource": "arn:aws:s3:us-east-1:123456789012:accesspoint/marketing/object/mkt/*"
  }]
}

Cách team sử dụng

Mỗi team gọi qua access point của mình thay vì bucket trực tiếp:

# Trước: tất cả team dùng chung endpoint
aws s3 cp s3://company-data/ml/model.pkl .
 
# Sau: mỗi team dùng access point riêng
aws s3 cp s3://arn:aws:s3:us-east-1:123456789012:accesspoint/data-science/ml/model.pkl .

Endpoint format: https://<access-point-name>-<account-id>.s3-accesspoint.<region>.amazonaws.com

So sánh có và không có Access Points

Không có Access PointsCó Access Points
1 bucket policy cho tất cảMỗi team có policy riêng
Policy dài, phức tạpMỗi policy ngắn, đơn giản
Giới hạn 20KBKhông lo giới hạn
Thay đổi ảnh hưởng tất cảThay đổi chỉ ảnh hưởng 1 team
Khó debug khi có lỗiDễ xác định lỗi ở team nào

💡 Hiểu đơn giản: Access Point giống như tạo nhiều "cổng vào" cho cùng 1 kho hàng, mỗi cổng có bảo vệ riêng, quy định riêng về ai được vào và lấy gì.


Encryption

⚡ Default Encryption (Từ Jan 2023)

Quan trọng: Từ tháng 1/2023, AWS đã bật SSE-S3 encryption mặc định cho TẤT CẢ objects mới upload lên S3. Bạn không cần làm gì!

┌─────────────────────────────────────────────────────────────────┐
│              S3 DEFAULT ENCRYPTION (Jan 2023+)                  │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  Trước Jan 2023:              Sau Jan 2023 (hiện tại):          │
│  ─────────────────            ────────────────────────          │
│  Upload object                Upload object                     │
│       │                           │                             │
│       ▼                            ▼                            │
│  Không encrypt                Tự động encrypt với SSE-S3        │
│  (trừ khi bạn specify)        (AES-256, AWS managed keys)       │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
AspectDefault Behavior
Encryption typeSSE-S3 (AES-256)
Key managementAWS quản lý hoàn toàn
CostFree
Bạn cần làm gì?Không cần làm gì!
Có thể đổi?Có - đổi sang SSE-KMS nếu cần audit

Server-Side Encryption (SSE)

S3 tự động encrypt data khi lưu và decrypt khi GET:

TypeKey quản lýAuditKhi nào dùng
SSE-S3AWS (tự động)Default, đơn giản nhất
SSE-KMSBạn (KMS)✅ CloudTrailCompliance, audit trail
SSE-CBạn (gửi mỗi request)Full control
DSSE-KMSKMS, dual-layerExtra security
┌──────────────────────────────────────────────────────────────────┐
│                    ENCRYPTION COMPARISON                         │
├──────────────────────────────────────────────────────────────────┤
│                                                                  │
│  SSE-S3 (Default từ Jan 2023)                                    │
│  ┌─────────────────────────────────────────────────────────┐     │
│  │ PUT object → S3 tự encrypt với AES-256                  │     │
│  │ GET object → S3 tự decrypt                              │     │
│  │ Key: AWS quản lý hoàn toàn, bạn không thấy              │     │
│  └─────────────────────────────────────────────────────────┘     │
│                                                                  │
│  SSE-KMS                                                         │
│  ┌─────────────────────────────────────────────────────────┐     │
│  │ PUT object → S3 gọi KMS để lấy Data Key → encrypt       │     │
│  │ GET object → S3 gọi KMS để decrypt Data Key → decrypt   │     │
│  │ Key: Bạn quản lý trong KMS, có audit log                │     │
│  │ ⚠️ KMS có request limits (5,500-30,000 req/s per Region)│     │
│  └─────────────────────────────────────────────────────────┘     │
│                                                                  │
│  SSE-C                                                           │
│  ┌─────────────────────────────────────────────────────────┐     │
│  │ PUT: Gửi object + encryption key trong header           │     │
│  │ GET: Gửi encryption key trong header để decrypt         │     │
│  │ Key: Bạn quản lý hoàn toàn, AWS không lưu               │     │
│  │ ⚠️ HTTPS bắt buộc (để bảo vệ key trong transit)         │     │
│  └─────────────────────────────────────────────────────────┘     │
│                                                                  │
└──────────────────────────────────────────────────────────────────┘

Client-Side Encryption

  • Encrypt trước khi upload
  • Decrypt sau khi download
  • AWS không bao giờ thấy plaintext data
Client-Side Encryption:
┌────────────┐   encrypt   ┌────────────┐   upload   ┌────────────┐
│ Plaintext  │ ──────────► │ Ciphertext │ ─────────► │     S3     │
└────────────┘             └────────────┘            └────────────┘
                                                     (stores cipher)

Bucket Default Encryption

Bật encryption mặc định cho bucket (tất cả objects mới sẽ được encrypt):

Bucket Properties → Default encryption:
- SSE-S3 (default)
- SSE-KMS (chọn KMS key)

→ Objects upload không specify encryption sẽ dùng default

Force Encryption with Bucket Policy

Tại sao cần Force khi đã có Default?

Default encryption chỉ áp dụng khi client không specify encryption. Client vẫn có thể override bằng header:

# Bucket default: SSE-S3
# Nhưng client có thể override:
aws s3api put-object \
  --bucket my-bucket \
  --key myfile.txt \
  --body myfile.txt \
  --server-side-encryption aws:kms    # ← Override sang KMS
┌─────────────────────────────────────────────────────────────────┐
│              DEFAULT vs FORCE ENCRYPTION                        │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  DEFAULT ENCRYPTION:                                            │
│  - Upload không specify → S3 tự động áp dụng default            │
│  - Upload có specify → Dùng encryption client chọn (override)   │
│                                                                 │
│  FORCE ENCRYPTION (Bucket Policy):                              │
│  - Upload PHẢI dùng encryption được chỉ định                    │
│  - Nếu không đúng → DENY! (kể cả không specify)                 │
│  - Đảm bảo 100% compliance, không ai bypass được                │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Bucket Policy để Force SSE-KMS:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyNonKMSEncryption",
      "Effect": "Deny",
      "Principal": "*",
      "Action": "s3:PutObject",
      "Resource": "arn:aws:s3:::my-bucket/*",
      "Condition": {
        "StringNotEquals": {
          "s3:x-amz-server-side-encryption": "aws:kms"
        }
      }
    }
  ]
}

Kết quả:

  • Upload với SSE-S3 → ❌ DENIED
  • Upload với SSE-KMS → ✅ ALLOWED
  • Upload không specify → ❌ DENIED

Khi nào cần Force Encryption?

ScenarioCần Force?
Dữ liệu thông thường❌ Default SSE-S3 đủ
Cần audit log (ai đọc/ghi)✅ Force SSE-KMS
Compliance (HIPAA, PCI, SOC2)✅ Force SSE-KMS
Dùng customer-managed key✅ Force SSE-KMS với specific key

S3 Block Public Access

Safety net để ngăn accidentally expose data ra public:

┌──────────────────────────────────────────────────────────────────┐
│           S3 BLOCK PUBLIC ACCESS (4 settings)                    │
├──────────────────────────────────────────────────────────────────┤
│                                                                  │
│  ☑️ BlockPublicAcls                                              │
│     → Block PUT bucket/object ACL nếu grants public access       │
│                                                                  │
│  ☑️ IgnorePublicAcls                                             │
│     → Ignore existing public ACLs (treat như không có)           │
│                                                                  │
│  ☑️ BlockPublicPolicy                                            │
│     → Block PUT bucket policy nếu grants public access           │
│                                                                  │
│  ☑️ RestrictPublicBuckets                                        │
│     → Restrict access to bucket với public policy chỉ cho        │
│        AWS services và authorized users trong account            │
│                                                                  │
└──────────────────────────────────────────────────────────────────┘

Có thể apply ở 2 levels:

  • Account level - apply cho tất cả buckets trong account
  • Bucket level - apply cho specific bucket

💡 Best Practice: Enable tất cả 4 settings ở Account level, chỉ disable ở bucket level khi thực sự cần public access.


CORS

CORS (Cross-Origin Resource Sharing) - cơ chế cho phép web browser gọi đến S3 bucket từ domain khác.

Vấn đề: Same-Origin Policy

Browser có Same-Origin Policy - chặn requests đến domain khác vì lý do bảo mật:

┌─────────────────────────────────────────────────────────────────┐
│                    SAME-ORIGIN POLICY                           │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  Website: https://myapp.com                                     │
│                                                                 │
│  ✅ Fetch from https://myapp.com/api      (same origin)         │
│  ❌ Fetch from https://my-bucket.s3...    (cross-origin!)       │
│  ❌ Fetch from https://api.other.com      (cross-origin!)       │
│                                                                 │
│  Origin = protocol + domain + port                              │
│  → Nếu khác bất kỳ thành phần nào → Cross-Origin                │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Làm thế nào để Browser kết nối được S3?

2 cách chính:

Cách 1: Cấu hình CORS trên S3 bucket (Browser gọi trực tiếp)

Website (myapp.com)     →     S3 Bucket (có CORS enabled)

1. JS: fetch("https://bucket.s3.amazonaws.com/file")


2. Browser: "Cross-origin → gửi preflight OPTIONS trước"


3. S3: Check CORS config → có allow myapp.com → OK!
       Trả về: Access-Control-Allow-Origin: https://myapp.com


4. Browser: "S3 cho phép" → Gửi request thật → Nhận data ✅

Cách 2: Đi qua Backend (Proxy) - không cần CORS

Website (myapp.com)  →  Backend (myapp.com/api)  →  S3

1. JS: fetch("/api/get-file")      (same origin!) ✅


2. Backend: aws sdk → S3 (server-to-server)


3. Backend trả data về browser

→ Browser không biết S3 tồn tại
→ Server-to-server KHÔNG bị Same-Origin Policy
Phương phápCần CORS?Khi nào dùng
Browser → S3 trực tiếp✅ CầnUpload/download file trực tiếp, static assets
Browser → Backend → S3❌ KhôngCần xử lý logic, bảo mật sensitive operations

💡 Key insight: Same-Origin Policy là browser chặn, không phải S3. CORS là cách S3 nói với browser "tao cho phép origin này gọi tao".

CORS giải quyết thế nào?

┌─────────────────────────────────────────────────────────────────┐
│                    CORS FLOW                                    │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  1. Browser gửi Preflight request (OPTIONS)                     │
│     Origin: https://myapp.com                                   │
│         │                                                       │
│          ▼                                                      │
│  2. S3 check CORS config → có allow myapp.com?                  │
│         │                                                       │
│          ▼                                                      │
│  3. S3 trả về CORS headers                                      │
│     Access-Control-Allow-Origin: https://myapp.com              │
│     Access-Control-Allow-Methods: GET, PUT                      │
│         │                                                       │
│          ▼                                                      │
│  4. Browser nhận → OK, cho phép request thật                    │
│         │                                                       │
│          ▼                                                      │
│  5. Browser gửi actual request (GET/PUT...)                     │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Cấu hình CORS trên S3

Bucket → Permissions → Cross-origin resource sharing (CORS):

[
  {
    "AllowedOrigins": [
      "https://myapp.com",
      "https://www.myapp.com"
    ],
    "AllowedMethods": [
      "GET",
      "PUT",
      "POST",
      "DELETE"
    ],
    "AllowedHeaders": [
      "*"
    ],
    "ExposeHeaders": [
      "ETag",
      "x-amz-meta-custom-header"
    ],
    "MaxAgeSeconds": 3600
  }
]

Giải thích các fields

FieldMô tảVí dụ
AllowedOriginsDomains được phép gọi["https://myapp.com"] hoặc ["*"] (all)
AllowedMethodsHTTP methods được phép["GET", "PUT", "POST", "DELETE"]
AllowedHeadersHeaders client được gửi["*"] hoặc ["Content-Type", "Authorization"]
ExposeHeadersHeaders S3 trả về mà JS có thể đọc["ETag", "x-amz-meta-*"]
MaxAgeSecondsCache preflight response (giây)3600 (1 hour)

Use Cases phổ biến

┌─────────────────────────────────────────────────────────────────┐
│                    CORS USE CASES                               │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  1. Static Website + S3 Assets                                  │
│     Website ở CloudFront/Vercel, images/fonts ở S3              │
│                                                                 │
│  2. Direct Upload từ Browser                                    │
│     User upload file trực tiếp lên S3 (với pre-signed URL)      │
│                                                                 │
│  3. SPA (React/Vue) + S3 API                                    │
│     Frontend gọi S3 để lấy JSON/data files                      │
│                                                                 │
│  4. Web Fonts                                                   │
│     Font files (.woff2) hosted trên S3                          │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Ví dụ: Allow all origins (Development)

[
  {
    "AllowedOrigins": ["*"],
    "AllowedMethods": ["GET", "HEAD"],
    "AllowedHeaders": ["*"],
    "MaxAgeSeconds": 3000
  }
]

⚠️ Cảnh báo: Không dùng "*" cho production với write operations!

Troubleshooting CORS Errors

❌ Error: "No 'Access-Control-Allow-Origin' header is present"

Checklist:
1. ✅ CORS config đã được thêm vào bucket?
2. ✅ AllowedOrigins có chứa domain của bạn?
3. ✅ AllowedMethods có chứa method bạn dùng?
4. ✅ Bucket policy có block request không?
5. ✅ CloudFront có forward Origin header không?

MFA Delete

MFA Delete - yêu cầu MFA (Multi-Factor Authentication) để thực hiện các thao tác xóa quan trọng trên S3.

Tại sao cần MFA Delete?

┌─────────────────────────────────────────────────────────────────┐
│                    MFA DELETE PROTECTION                        │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  Scenario nguy hiểm:                                            │
│                                                                 │
│  1. Hacker có được AWS credentials (access key bị lộ)           │
│  2. Hacker xóa tất cả objects + versions                        │
│  3. Hacker tắt versioning                                       │
│  4. Data mất vĩnh viễn! 💀                                      │
│                                                                 │
│  Với MFA Delete enabled:                                        │
│                                                                 │
│  1. Hacker có credentials                                       │
│  2. Hacker DELETE object version → ❌ DENIED (cần MFA code)     │
│  3. Hacker tắt versioning → ❌ DENIED (cần MFA code)            │
│  4. Data vẫn an toàn! ✅                                        │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

MFA Delete bảo vệ những gì?

ActionCần MFA?
Delete object version (permanently delete)✅ Cần MFA
Suspend/Disable versioning✅ Cần MFA
Delete object (tạo delete marker)❌ Không cần
Enable versioning❌ Không cần
List/Get objects❌ Không cần

💡 Lưu ý: MFA Delete chỉ bảo vệ permanent delete (xóa version cụ thể), không phải soft delete (tạo delete marker).

Yêu cầu để enable MFA Delete

┌─────────────────────────────────────────────────────────────────┐
│                    MFA DELETE REQUIREMENTS                      │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  1. ✅ Bucket phải có Versioning ENABLED                        │
│                                                                 │
│  2. ✅ Chỉ ROOT ACCOUNT owner mới có thể enable/disable         │
│        (IAM users KHÔNG THỂ, kể cả có full admin permissions)   │
│                                                                 │
│  3. ✅ Phải dùng AWS CLI (không thể dùng Console!)              │
│                                                                 │
│  4. ✅ Root account phải có MFA device configured               │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Cách enable MFA Delete

# Chỉ root account mới chạy được!
# Phải dùng CLI, không có option trên Console
 
aws s3api put-bucket-versioning \
  --bucket my-bucket \
  --versioning-configuration Status=Enabled,MFADelete=Enabled \
  --mfa "arn:aws:iam::123456789012:mfa/root-account-mfa-device 123456"
                                                            ^
                                                    MFA code từ device

Khi delete cần MFA

# Delete một version cụ thể (permanent delete)
aws s3api delete-object \
  --bucket my-bucket \
  --key myfile.txt \
  --version-id "abc123" \
  --mfa "arn:aws:iam::123456789012:mfa/root-account-mfa-device 654321"

MFA Delete vs Object Lock

FeatureMFA DeleteObject Lock
Bảo vệCần MFA để deleteKhông ai có thể delete
Linh hoạtVẫn có thể delete (với MFA)Hoàn toàn không thể delete
Use caseChống unauthorized deleteCompliance (WORM)
Enable bởiRoot account onlyKhi tạo bucket

Khi nào dùng MFA Delete?

ScenarioRecommendation
Critical backups✅ Enable MFA Delete
Compliance data (WORM required)✅ Use Object Lock thay vì MFA Delete
Shared AWS account✅ Enable MFA Delete
Data có thể recreate❌ Không cần

⚠️ Lưu ý quan trọng: MFA Delete chỉ có thể enable/disable bởi root account. Nếu root account bị mất access, bạn không thể tắt MFA Delete!


Pre-signed URLs

Tạo temporary URLs để grant access không cần AWS credentials:

┌─────────────────────────────────────────────────────────────────┐
│                    PRE-SIGNED URLs                              │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  Use case: Cho user download private object qua browser         │
│                                                                 │
│  1. User request download                                       │
│  2. Backend generate pre-signed URL (có thời hạn)               │
│  3. Return URL cho user                                         │
│  4. User download trực tiếp từ S3                               │
│                                                                 │
│  ┌────────┐  request   ┌────────┐  generate   ┌────────┐        │
│  │  User  │ ─────────► │ Backend│ ──────────► │   S3   │        │
│  └────────┘            └───┬────┘             └────────┘        │
│       ▲                    │                       ▲            │
│       │    pre-signed URL  │                      │             │
│       └────────────────────┘         download     │             │
│       │────────────────────────────────────────────┘            │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

AWS CLI:

# Generate pre-signed URL (default 1 hour)
aws s3 presign s3://my-bucket/my-object
 
# Specify expiration (max 7 days = 604800 seconds)
aws s3 presign s3://my-bucket/my-object --expires-in 3600

Pre-signed URL format:

https://my-bucket.s3.amazonaws.com/my-object
  ?X-Amz-Algorithm=AWS4-HMAC-SHA256
  &X-Amz-Credential=...
  &X-Amz-Date=...
  &X-Amz-Expires=3600
  &X-Amz-SignedHeaders=host
  &X-Amz-Signature=...

Use cases:

  • Allow downloads without AWS credentials
  • Allow uploads to specific path
  • Temporary access for external users

VPC Endpoints

Truy cập S3 qua private network thay vì internet:

┌─────────────────────────────────────────────────────────────────┐
│                    VPC ENDPOINTS FOR S3                         │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  Without VPC Endpoint:                                          │
│  EC2 ──► NAT Gateway ──► Internet Gateway ──► Internet ──► S3   │
│                           (tốn NAT cost, slow, less secure)     │
│                                                                 │
│  With Gateway Endpoint (FREE):                                  │
│  EC2 ──────────────────► VPC Endpoint ──────────────────► S3    │
│                           (private, fast, no NAT cost)          │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

2 loại VPC Endpoints cho S3:

TypeNetworkCostUse Case
Gateway EndpointRoute table entryFreeDefault choice
Interface EndpointENI with private IP~$7/monthOn-premises access, specific compliance

Bucket Policy restrict to VPC Endpoint:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "VPCEndpointOnly",
      "Effect": "Deny",
      "Principal": "*",
      "Action": "s3:*",
      "Resource": [
        "arn:aws:s3:::my-bucket",
        "arn:aws:s3:::my-bucket/*"
      ],
      "Condition": {
        "StringNotEquals": {
          "aws:sourceVpce": "vpce-1234567890abcdef0"
        }
      }
    }
  ]
}

S3 Access Logs

S3 Access Logs ghi lại mọi requests đến bucket - ai truy cập, làm gì, khi nào.

Cách hoạt động

┌─────────────────────────────────────────────────────────────────┐
│                    S3 ACCESS LOGGING                            │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ┌──────────────┐                    ┌──────────────────────┐   │
│  │ Source Bucket│  ───► Logs ───►    │ Target Bucket (logs) │   │
│  │ (my-data)    │                    │ (my-data-logs)       │   │
│  └──────────────┘                    └──────────────────────┘   │
│                                           │                     │
│                                            ▼                    │
│                               2024-01-15-00-45-32-ABC123.log    │
│                               2024-01-15-01-12-45-DEF456.log    │
│                               ...                               │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Log format (các fields quan trọng)

79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be  ← Bucket Owner ID
my-bucket                                                          ← Bucket Name  
[15/Jan/2024:09:47:23 +0000]                                       ← Time
192.168.1.100                                                      ← Requester IP
arn:aws:iam::123456789012:user/john                                ← Requester ARN
ABC123XYZ                                                          ← Request ID
REST.GET.OBJECT                                                    ← Operation
myfile.txt                                                         ← Key
"GET /my-bucket/myfile.txt HTTP/1.1"                               ← Request-URI
200                                                                ← HTTP Status
-                                                                  ← Error Code
1024                                                               ← Bytes Sent
234                                                                ← Object Size
50                                                                 ← Total Time (ms)
12                                                                 ← Turn-Around Time (ms)
"-"                                                                ← Referrer
"aws-sdk-java/1.11.123"                                            ← User-Agent

Cách enable S3 Access Logs

Console: Bucket → Properties → Server access logging → Edit → Enable

CLI:

# Tạo target bucket cho logs
aws s3 mb s3://my-bucket-logs
 
# Enable logging
aws s3api put-bucket-logging \
  --bucket my-bucket \
  --bucket-logging-status '{
    "LoggingEnabled": {
      "TargetBucket": "my-bucket-logs",
      "TargetPrefix": "logs/my-bucket/"
    }
  }'

Target Bucket Policy

Target bucket cần có policy cho phép S3 log delivery:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "S3LogDeliveryWrite",
      "Effect": "Allow",
      "Principal": {
        "Service": "logging.s3.amazonaws.com"
      },
      "Action": "s3:PutObject",
      "Resource": "arn:aws:s3:::my-bucket-logs/*",
      "Condition": {
        "StringEquals": {
          "aws:SourceAccount": "123456789012"
        }
      }
    }
  ]
}

Use Cases

Use CaseMô tả
Security auditAi đã truy cập sensitive files?
Access patternsFiles nào được access nhiều nhất?
Cost analysisRequests đến từ đâu, bao nhiêu?
TroubleshootingTại sao request failed? (error codes)
ComplianceChứng minh access history

Lưu ý quan trọng

⚠️  KHÔNG bật logging vào chính bucket đó!

    Source Bucket ──► logs ──► Source Bucket (SAME!)


                              Infinite loop!
                              Log ghi → tạo log mới → ghi → ...
                              → Bucket đầy, tốn tiền!

✅  ĐÚNG: Dùng bucket riêng cho logs

    Source Bucket ──► logs ──► Target Bucket (KHÁC!)
Điểm cần lưu ýChi tiết
Best-effort deliveryKhông đảm bảo 100% logs được ghi
DelayLogs có thể delay vài phút
CostChỉ tốn phí storage cho log files
FormatText files, space-delimited

S3 Access Logs vs CloudTrail

FeatureS3 Access LogsCloudTrail
ScopeRequests đến bucketAWS API calls
Chi tiếtRất chi tiết (bytes, latency)Ít chi tiết hơn
DeliveryBest-effortNear real-time
CostStorage onlyPer event (data events)
AnalysisAthena, custom scriptsCloudWatch, Athena
Use caseDeep analysisAudit, compliance

💡 Recommendation: Dùng cả hai cho comprehensive monitoring. CloudTrail cho real-time audit, Access Logs cho detailed analysis.


Logging & Monitoring

CloudTrail

Log management events (bucket creation, policy changes) và data events (GetObject, PutObject):

  • Management events: mặc định đã log
  • Data events: phải bật riêng (tốn phí)

Amazon Macie

Tự động discover sensitive data (PII, credentials) trong S3:

  • Scan buckets tìm sensitive data
  • Alert về public buckets, unencrypted data
  • Compliance reports

Best Practices

Checklist bảo mật S3

CategoryActionPriority
AccessEnable S3 Block Public Access ở account level🔴 Critical
AccessDisable ACLs (dùng Bucket owner enforced)🟠 High
AccessSử dụng least privilege IAM policies🟠 High
EncryptionEnable default encryption (SSE-S3 hoặc SSE-KMS)🟠 High
EncryptionForce HTTPS với bucket policy🟡 Medium
Data ProtectionEnable versioning cho critical data🟠 High
Data ProtectionEnable MFA Delete cho critical buckets🟡 Medium
NetworkSử dụng VPC Endpoints🟡 Medium
MonitoringEnable S3 Access Logs🟡 Medium
MonitoringEnable CloudTrail data events🟡 Medium
ComplianceSử dụng Object Lock cho WORM requirements🟡 As needed

Common Mistakes

❌ Bucket policy với Principal: "*" mà không có Condition
   → Public bucket!

❌ ACLs enabled (legacy) có thể vô tình grant public access
   → Dùng "Bucket owner enforced" để disable ACLs

❌ Quên enable versioning trước khi bật Object Lock
   → Object Lock yêu cầu versioning

❌ SSE-C qua HTTP
   → Keys bị exposed! Phải dùng HTTPS

Liên kết

  • S3 - Main S3 documentation
  • AWS KMS - Key Management Service
  • VPC Endpoints - Private access to AWS services
  • IAM - Identity and Access Management
  • Amazon Macie - Sensitive data discovery

Tài liệu tham khảo