Ngôn ngữ hiển thị:
Tháng trước mình review hệ thống của một startup và phát hiện bucket S3 chứa toàn bộ hợp đồng khách hàng đang để Public — ai có link là đọc được, không cần đăng nhập. Lý do? Dev mới cấu hình sai Block Public Access vì nghĩ "chỉ cần set ACL là xong". S3 có nhiều lớp kiểm soát quyền truy cập chồng lên nhau, và nếu không hiểu rõ từng lớp, sai lầm xảy ra rất tự nhiên.
Hãy hình dung S3 như một tòa nhà văn phòng: Bucket là cả tòa nhà, Object là từng phòng bên trong. Bạn có thể kiểm soát ai được vào tòa nhà (Bucket Policy), ai được mở từng phòng (Object ACL), và có thể khóa toàn bộ cửa chính không cho ai vào dù có chìa khóa (Block Public Access).
AWS S3 có 4 lớp kiểm soát quyền truy cập:
Hiểu 4 lớp này là chìa khóa để không bao giờ cấu hình sai quyền S3 nữa.
Trước khi upload, cần xác định rõ mục đích sử dụng file:
Bước 1 — Tạo bucket với Block Public Access bật mặc định
Khi tạo bucket mới, AWS bật Block Public Access theo mặc định từ 2023. Đừng tắt nó trừ khi bạn thực sự cần serve file public trực tiếp (hiếm gặp). Kiểm tra lại bằng AWS CLI:
aws s3api get-public-access-block --bucket ten-bucket-cua-banOutput cần thấy tất cả 4 trường đều là true.
Bước 2 — Upload file với AWS CLI
Upload file đơn giản:
aws s3 cp ./invoice.pdf s3://my-private-bucket/invoices/invoice_001.pdfUpload cả thư mục (ví dụ: build React app):
aws s3 sync ./dist s3://my-website-bucket/ --deleteTùy chọn --delete xóa file trên S3 không còn tồn tại local — hữu ích khi deploy website tĩnh.
Bước 3 — Bucket Policy cho website tĩnh (Public qua CloudFront)
Thay vì public bucket trực tiếp, tạo CloudFront Origin Access Control (OAC) và chỉ cho CloudFront đọc bucket:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowCloudFrontOAC",
"Effect": "Allow",
"Principal": {
"Service": "cloudfront.amazonaws.com"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-website-bucket/*",
"Condition": {
"StringEquals": {
"AWS:SourceArn": "arn:aws:cloudfront::123456789:distribution/EXXXXX"
}
}
}
]
}Policy này chỉ cho phép đúng CloudFront distribution của bạn đọc file — không ai khác, dù biết URL S3 gốc cũng không truy cập được.
Bước 4 — Tạo Presigned URL cho file private
Khi user cần tải hóa đơn PDF riêng của họ, backend tạo presigned URL thay vì expose file trực tiếp:
import boto3
from datetime import datetime
s3 = boto3.client('s3', region_name='ap-southeast-1')
presigned_url = s3.generate_presigned_url(
'get_object',
Params={
'Bucket': 'my-private-bucket',
'Key': f'invoices/{user_id}/invoice_001.pdf'
},
ExpiresIn=3600 # Hết hạn sau 1 giờ
)
# Trả URL này về cho frontend — không expose AWS credentialsMẹo: Đặt ExpiresIn ngắn nhất có thể cho use case của bạn. Hóa đơn tải về một lần → 15 phút là đủ. Tránh dùng 7 ngày vì nếu link bị lộ, kẻ tấn công có cả tuần.
1. Tắt Block Public Access "thử xem" — rồi quên bật lại. Giải pháp: dùng AWS Config Rule s3-bucket-public-read-prohibited để tự động cảnh báo khi bucket bị public.
2. Dùng ACL public-read cho từng object — thay vì Bucket Policy. ACL đang bị deprecate và khó audit. Chuyển sang Bucket Policy + CloudFront OAC cho mọi trường hợp public.
3. IAM User có quyền s3:* trên mọi bucket — vi phạm least privilege. Tạo IAM Role riêng cho từng service, chỉ cấp quyền đúng bucket và đúng action (s3:GetObject, s3:PutObject).
Q: Dùng ACL hay Bucket Policy — cái nào tốt hơn?A: Bucket Policy. AWS đã disable Object ACL mặc định từ April 2023 cho bucket mới. Bucket Policy linh hoạt hơn, dễ audit hơn, và hỗ trợ điều kiện phức tạp (IP whitelist, MFA, VPC). Chỉ dùng ACL khi làm việc với bucket cũ chưa migrate.
Q: Presigned URL có an toàn không nếu bị lộ?A: An toàn hơn link public nhưng không phải 100%. Ai có URL đó trong thời hạn ExpiresIn đều truy cập được. Giải pháp: đặt ExpiresIn ngắn, log access với S3 Server Access Logging, và khi cần bảo mật cao hơn thì kết hợp CloudFront Signed URL với trusted key group.
Q: Upload file lớn (5GB+) có cần cách khác không?A: Cần dùng Multipart Upload — S3 tự động dùng khi bạn upload qua AWS CLI hoặc SDK với file trên 8MB. Với file >5GB, đây là bắt buộc. Nếu upload trực tiếp từ browser, dùng presigned URL multipart để không expose credentials phía client.
Phần lớn sự cố rò rỉ dữ liệu S3 không phải do lỗ hổng AWS — mà do cấu hình sai của người dùng. Nguyên tắc đơn giản: mọi bucket đều private mặc định, chỉ mở public qua CloudFront khi thực sự cần, và dùng Presigned URL cho mọi file cần chia sẻ tạm thời. Tuần tới mình sẽ đào sâu vào EBS — ổ cứng SSD ảo cho EC2 — và tại sao đây là lựa chọn tốt hơn hẳn so với lưu file thẳng trên instance.