Quay lại bài viết
16 thg 6, 2026
16 min read

Amazon S3 Security: Mã hóa, Object Lock, Access Points và những gì cần nhớ cho kỳ thi SAA

Bạn vừa đưa một hệ thống lưu trữ hồ sơ y tế và chứng từ tài chính lên Amazon S3. Mọi thứ chạy mượt — cho đến khi đội compliance gõ cửa với một bảng câu hỏi khiến bạn toát mồ hôi:

  • Dữ liệu trong bucket có được mã hóa không? Ai đang giữ khóa — AWS, hay bạn?
  • Nếu một nhân viên (hoặc kẻ tấn công có credential) cố xóa hồ sơ, ta có chặn được không? Có chứng minh được với kiểm toán viên là hồ sơ không thể bị sửa trong 7 năm không?
  • Ai đã truy cập object nào, lúc nào?
  • Làm sao cho đối tác tải đúng một file trong 10 phút, mà không phải mở bucket ra public?
  • Năm team khác nhau cần truy cập năm thư mục khác nhau trong cùng một bucket — quản lý quyền kiểu gì cho khỏi loạn?
  • Khi đội phân tích đọc dữ liệu, làm sao tự động che (redact) số CMND/thẻ tín dụng mà không phải tạo một bản sao đã xử lý?

Điều thú vị: mỗi câu hỏi ở trên ứng với đúng một tính năng bảo mật của S3. S3 không chỉ là “kho lưu file giá rẻ” — nó là một tập hợp các lớp phòng thủ, mỗi lớp sinh ra để trả lời một câu hỏi cụ thể. Bài viết này sắp xếp toàn bộ những tính năng đó thành bốn lớp dễ nhớ, giải thích mỗi tính năng giải quyết vấn đề gì, dùng khi nào, và chốt lại những điểm hay ra trong kỳ thi SAA.


1. Bức tranh tổng thể: bốn lớp phòng thủ

Thay vì học 13 tính năng rời rạc, hãy gắn mỗi tính năng với câu hỏi bảo mật mà nó trả lời. Tất cả gom lại thành bốn lớp:

Câu hỏi compliance hỏiLớp phòng thủTính năng S3
”Dữ liệu có mã hóa không? Ai giữ khóa?”EncryptionSSE-S3, SSE-KMS, SSE-C, DSSE-KMS, Client-Side, TLS in-transit
”Có chống sửa/xóa được không?”Integrity & RetentionVersioning, S3 Object Lock, Glacier Vault Lock, MFA Delete
”Ai được làm gì, cấp quyền ra sao?”Access ControlBucket Policy/IAM, Pre-signed URLs, Access Points, CORS, Object Lambda
”Ai đã truy cập cái gì, lúc nào?”ObservabilityS3 Server Access Logs, CloudTrail

Hai ý xuyên suốt cần ghim trước khi đi vào chi tiết:

  • Mã hóa và kiểm soát truy cập là hai chuyện khác nhau. Mã hóa trả lời “nếu dữ liệu bị lấy ra thì kẻ trộm có đọc được không”, kiểm soát truy cập trả lời “ai được chạm vào dữ liệu”. Một bucket mã hóa hoàn hảo vẫn có thể bị rò nếu policy mở toang, và ngược lại.
  • Versioning là nền móng. Hai tính năng chống xóa quan trọng nhất — Object Lock và MFA Delete — đều yêu cầu bật Versioning trước. Versioning nghĩa là S3 giữ lại mọi phiên bản của object, nên “xóa” không đồng nghĩa với “mất”.

2. Mã hóa at-rest: bốn cách giữ khóa

Đây là phần dày nhất của Section 14, và cũng hay ra thi nhất. “Mã hóa at-rest” (encryption at rest) nghĩa là dữ liệu được mã hóa khi nằm yên trên đĩa, để nếu ai đó lấy được phần cứng vật lý cũng không đọc được nội dung. Câu hỏi cốt lõi phân biệt các phương án không phải “có mã hóa hay không”, mà là: mã hóa diễn ra ở đâu, và ai giữ khóa.

2.1. SSE-S3 — để AWS lo hết

SSE-S3 là kiểu mã hóa mặc định và đơn giản nhất, được AWS quản lý hoàn toàn. Cách hoạt động cũng giống với SSE-KMS, chỉ khác ở chỗ master key lúc này được S3 quản lý, không đụng tới KMS nên cũng gặp không các vấn đề về throttling.

Mỗi object được encrypt với 1 data key khác nhau — sinh ra từ master key.

Enable thông qua header x-amz-server-side-encryption: AES256.

2.2. SSE-KMS — kiểm soát và audit khóa

SSE-KMS vẫn để S3 mã hóa, nhưng khóa nằm trong AWS KMS — dịch vụ quản lý khóa của AWS. Khác biệt lớn so với SSE-S3 nằm ở hai chữ: kiểm soát.

Bạn tự tạo khóa, đặt policy ai được dùng khóa, bật xoay vòng khóa, và quan trọng nhất là mỗi lần truy cập khoá đều được lưu lại trong CloudTrail.

Mỗi lần upload object, S3 gọi KMS API GenerateDataKey, mỗi lần download, S3 gọi Decrypt. Các lời gọi này tính vào quota KMS theo region (mặc định vài nghìn request/giây). Với bucket lưu lượng cực cao, bạn có thể bị throttling (KMS từ chối bớt request khi vượt quota).

Để giải quyết vấn đề throttling khi sử dụng SSE-KMS, S3 Bucket Keys ra đời — S3 nhờ KMS tạo một khóa cho bucket — gọi là bucket key, bucket sẽ dùng bucket key để sinh data key cho từng object thay vì gọi KMS mỗi lần, giảm số lời gọi KMS tới khoảng 99% (giảm cả chi phí lẫn nguy cơ throttling).

Bucket key không phải master key. Bản chất nó chỉ là một tầng khóa trung gian được chèn thêm vào, biến envelope encryption từ hai cấp thành ba cấp. Master key vẫn an toàn trong KMS service.

Nhược điểm của S3 Bucket Keys chính là bán kính ảnh hưởng (blast radius — phạm vi thiệt hại nếu một khóa bị lộ) của bucket key rộng hơn data key một chút: nếu một bucket key bị lộ, các object nằm trong khung thời gian nó phục vụ đều bị ảnh hưởng, thay vì chỉ một object.

Chi tiết về cách KMS sinh khóa, envelope encryption và các loại khóa nằm trong bài AWS Security & Encryption — bài này chỉ tập trung vào cách S3 vận dụng chúng.

2.3. SSE-C — bạn mang khóa, S3 mã hóa hộ

SSE-C dành cho trường hợp bạn muốn tự quản lý khóa hoàn toàn bên ngoài AWS, nhưng vẫn để S3 làm việc mã hóa.

Cách hoạt động: bạn đính kèm khoá trong headers của mỗi request. S3 dùng khóa đó để mã hóa (hoặc giải mã) object, rồi vứt bỏ khóa ngay — AWS không bao giờ lưu khóa của bạn.

  • Vấn đề nó giải quyết: “tôi bắt buộc phải tự giữ khóa ngoài AWS, nhưng không muốn tự viết code mã hóa.”
  • Use case: yêu cầu tuân thủ bắt buộc khóa không được nằm trong hạ tầng nhà cung cấp cloud.

Vì khóa truyền qua đường mạng trong mỗi request, HTTPS là bắt buộc. Mất khóa = mất luôn dữ liệu, vì AWS không giữ bản sao nào.

2.4. DSSE-KMS — mã hóa hai lớp cho compliance khắt khe

DSSE-KMS giống SSE-KMS nhưng áp dụng hai lớp mã hóa chồng lên nhau, cả hai đều dùng khóa KMS. Nó ra đời để đáp ứng các tiêu chuẩn tuân thủ rất nghiêm ngặt (ví dụ một số yêu cầu của chính phủ/quốc phòng Mỹ) vốn đòi hỏi mã hóa nhiều lớp.

  • Vấn đề nó giải quyết: “quy định bắt buộc dữ liệu phải được mã hóa bằng hai lớp độc lập.”
  • Use case: tổ chức chịu khung tuân thủ đặc thù yêu cầu dual-layer encryption.

2.5. Client-Side Encryption — server không bao giờ thấy plaintext

Với mã hóa phía client (client-side encryption), bạn mã hóa dữ liệu trước khi gửi lên S3 và tự giữ khóa. S3 chỉ nhận và lưu ciphertext — nó không bao giờ thấy nội dung gốc hay khóa. Khi tải về, chính client giải mã.

  • Vấn đề nó giải quyết: “tôi không tin tưởng giao việc mã hóa cho server, kể cả AWS.”
  • Use case: dữ liệu cực kỳ nhạy cảm, mô hình zero-trust với nhà cung cấp cloud.

Vì khóa truyền qua đường mạng trong mỗi request, HTTPS là bắt buộc. Mất khóa = mất luôn dữ liệu, vì AWS không giữ bản sao nào.

2.6. Mã hóa in-transit (encryption in flight)

Bốn mục trên nói về dữ liệu nằm yên. Còn dữ liệu đang di chuyển giữa client và S3 thì sao? Đó là mã hóa in-transit, hay SSL/TLS. S3 cung cấp cả endpoint HTTP lẫn HTTPS.

Để ép buộc chỉ dùng HTTPS, ta thêm vào bucket policy một điều kiện từ chối khi aws:SecureTransport bằng false.

Như đã nói, SSE-C bắt buộc HTTPS sẵn.


3. Default Encryption & ép buộc mã hóa

Biết các kiểu mã hóa là một chuyện, đảm bảo không object nào lọt qua mà chưa mã hóa lại là chuyện khác. Đây là nơi hai cơ chế bổ trợ xuất hiện.

3.1. Default Encryption

Từ đầu năm 2023, AWS bật mã hóa mặc định cho mọi bucket, mọi object mới tự động được mã hóa bằng SSE-S3, không tốn thêm phí và không cần cấu hình. Bạn có thể đổi mặc định của bucket sang SSE-KMS nếu muốn kiểm soát khóa.

Default Encryption đảm bảo object được mã hóa, nhưng không ngăn ai đó cố tình upload với một kiểu mã hóa khác. Nếu cần ép buộc một kiểu cụ thể, dùng bucket policy.

3.2. Ép buộc mã hóa bằng bucket policy

Muốn bắt buộc mọi upload phải dùng đúng một kiểu mã hóa (ví dụ chỉ chấp nhận SSE-KMS), ta viết một bucket policy từ chối lệnh s3:PutObject nếu thiếu hoặc sai header mã hóa mong muốn.

Ví dụ policy dưới đây từ chối mọi s3:PutObject không mã hóa bằng SSE-KMS. Điều kiện dựa trên s3:x-amz-server-side-encryption — header mà client khai báo kiểu mã hóa khi upload:

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

Bucket policy được kiểm tra trước khi Default Encryption áp dụng. Nghĩa là policy là “người gác cổng” — nó có thể chặn đứng một request không hợp lệ ngay từ đầu, còn Default Encryption chỉ là lưới an toàn áp mã hóa cho những request đã được cho qua.


4. Bảo toàn & chống xóa: Object Lock, Glacier Vault Lock, MFA Delete

Mã hóa bảo vệ tính bí mật. Nhóm tính năng này bảo vệ tính toàn vẹn — chống việc dữ liệu bị sửa hoặc xóa, dù do tai nạn, do người trong cuộc, hay do ransomware. Khái niệm trung tâm là WORM: ghi một lần, không ai sửa/xóa được nữa.

4.1. S3 Object Lock

Yêu cầu: Versioning enable

Object Lock áp mô hình WORM cho từng phiên bản object: khóa không cho xóa một object version trong một khoảng thời gian xác định.

Có ba khái niệm cần tách bạch:

  • Retention mode — có hai loại:
    • Governance mode: đa số người dùng không thể xóa/ghi đè, nhưng một số ít có quyền đặc biệt (s3:BypassGovernanceRetention) vẫn vượt được khi cần. Phù hợp khi bạn muốn bảo vệ mạnh nhưng giữ một “đường thoát” cho admin.
    • Compliance mode: không một ai xóa/ghi đè được, kể cả root account. Đã đặt là không thể rút ngắn thời hạn hay đổi mode. Dùng khi quy định pháp lý không cho phép bất kỳ ngoại lệ nào.
  • Retention Period: bảo vệ object trong một khoảng thời gian cố định, có thể gia hạn thêm.
  • Legal Hold: bảo vệ object vô thời hạn, độc lập hoàn toàn với retention period, bật/tắt tự do bằng quyền s3:PutObjectLegalHold. Hình dung như một “lệnh phong tỏa” trong vụ kiện: giữ chừng nào còn cần, gỡ khi xong.

4.2. Glacier Vault Lock

Trong khi Object Lock bảo vệ object trong S3, Glacier Vault Lock bảo vệ dữ liệu trong kho lưu trữ lạnh Amazon S3 Glacier. Nó cũng theo mô hình WORM, nhưng điểm đặc trưng nằm ở lock policy — một khi đã khóa, chính sách đó trở thành bất biến.

4.3. MFA Delete

Yêu cầu: Versioning enable

MFA Delete buộc người dùng phải nhập mã MFA cho những thao tác nguy hiểm nhất trên bucket. Chỉ chủ bucket (root account) mới bật/tắt được MFA Delete.

Cụ thể, MFA được yêu cầu khi:

  • Xóa vĩnh viễn một object version.
  • Tắt versioning của bucket.

Ngược lại, MFA không cần khi bật Versioning hay liệt kê các version đã xóa.


5. Quan sát & cấp quyền tạm thời

5.1. S3 Server Access Logs

Muốn trả lời “ai đã truy cập object nào, lúc nào”, bạn bật S3 Server Access Logging. Nó ghi lại mọi request đến bucket (kể cả request bị từ chối, và từ tài khoản khác), rồi giao log sang một bucket S3 khác trong cùng region để bạn phân tích sau (ví dụ truy vấn bằng Athena).

Đừng để bucket chứa log trùng với bucket đang được giám sát. Làm vậy tạo vòng lặp: ghi log lại sinh ra request, request lại sinh log… bucket phình ra theo cấp số nhân.

5.2. Pre-signed URLs

Pre-signed URL là URL do bạn ký, cho phép người cầm nó truy cập một object cụ thể trong thời gian giới hạn — kế thừa đúng quyền của người đã ký. Nhờ đó bạn cấp quyền tải lên/tải xuống mà không cần mở bucket ra public.

  • Use case: cho phép tải nội dung premium chỉ với người đã đăng nhập, danh sách người được tải thay đổi liên tục, cho người dùng upload thẳng vào đúng vị trí trong bucket.
  • Cần nhớ: URL có thời hạn (CLI mặc định một giờ, cấu hình được). Bucket vẫn private hoàn toàn.

Đây là tính năng có riêng một bài đi sâu — gồm cả luồng upload bằng presigned POST, download, và tích hợp CloudFront Signed Cookies cho ảnh: S3 Presigned URL.


6. Quản lý ở quy mô lớn & biến đổi dữ liệu

6.1. S3 Access Points

Hình dung một bucket dữ liệu chung mà năm team cùng dùng: finance đọc/ghi thư mục finance/, sales chỉ đọc sales/, đối tác chỉ đọc shared/… Nếu nhồi tất cả quy tắc vào một bucket policy, nó sẽ phình thành một khối JSON khổng lồ, khó đọc, dễ sai. Mới ba team mà bucket policy của company-data-lake đã thành thế này:

{ "Version": "2012-10-17", "Statement": [ { "Sid": "FinanceReadWrite", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::111122223333:role/finance-app" }, "Action": ["s3:GetObject", "s3:PutObject"], "Resource": "arn:aws:s3:::company-data-lake/finance/*" }, { "Sid": "FinanceList", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::111122223333:role/finance-app" }, "Action": "s3:ListBucket", "Resource": "arn:aws:s3:::company-data-lake", "Condition": { "StringLike": { "s3:prefix": "finance/*" } } }, { "Sid": "SalesReadOnly", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::111122223333:role/sales-app" }, "Action": "s3:GetObject", "Resource": "arn:aws:s3:::company-data-lake/sales/*" }, { "Sid": "PartnerReadShared", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::444455556666:role/partner-app" }, "Action": "s3:GetObject", "Resource": "arn:aws:s3:::company-data-lake/shared/*" } ] }

Bốn statement chỉ cho ba team. Thêm analytics, ml-training, logs… mỗi team kéo theo vài statement nữa, tất cả dồn vào một tài liệu duy nhất. Bucket policy lại có trần kích thước 20 KB, nên kiểu gì cũng có ngày đụng trần — và nguy hơn là mỗi lần sửa một dòng cho team này lại có rủi ro làm hỏng quyền của mọi team khác.

S3 Access Points giải bài toán này bằng cách tách quyền ra nhiều “cửa vào”. Mỗi access point có DNS name riêngaccess point policy riêng (giống bucket policy nhưng chỉ cho phạm vi của nó). Bạn cấp cho mỗi team một access point với policy gọn gàng, thay vì một policy trung tâm phức tạp.

Cụ thể: bucket chỉ giữ đúng một statement uỷ quyền (delegate) cho mọi access point thuộc account — viết một lần và không bao giờ phình thêm, vì điều kiện s3:DataAccessPointAccount lọc theo account ID chứ không liệt kê từng team:

{ "Version": "2012-10-17", "Statement": [ { "Sid": "DelegateToAccessPoints", "Effect": "Allow", "Principal": "*", "Action": "*", "Resource": ["arn:aws:s3:::company-data-lake", "arn:aws:s3:::company-data-lake/*"], "Condition": { "StringEquals": { "s3:DataAccessPointAccount": "111122223333" } } } ] }

Quyền thật sự của từng team nằm trong access point policy riêng của nó. Ví dụ access point finance-ap cho team finance — gọn đúng một statement, không dính dáng gì tới team khác:

{ "Version": "2012-10-17", "Statement": [ { "Sid": "FinanceTeamAccess", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::111122223333:role/finance-app" }, "Action": ["s3:GetObject", "s3:PutObject"], "Resource": "arn:aws:s3:ap-southeast-1:111122223333:accesspoint/finance-ap/object/finance/*" } ] }

ARN của object khi đi qua access point có dạng .../accesspoint/<tên-ap>/object/<prefix>, khác với ARN bucket trực tiếp. Team finance gọi qua DNS name của finance-ap mà không cần biết tên bucket gốc. Thêm một team mới chỉ là tạo thêm một access point với một policy nhỏ độc lập, thay vì chèn thêm một dòng vào khối JSON trung tâm.

  • VPC Origin: một access point có thể đặt nguồn là Internet hoặc VPC. Nếu chọn VPC Origin, access point chỉ truy cập được từ trong VPC, và bạn phải tạo một VPC Endpoint (Gateway hoặc Interface) để tới nó — policy của VPC endpoint phải cho phép truy cập tới cả bucket lẫn access point.

Access Points là để mở rộng quản lý quyền (nhiều team/prefix). Mỗi Access Points = một policy + một DNS.

6.2. S3 Object Lambda

Đôi khi các ứng dụng khác nhau cần các phiên bản khác nhau của cùng một object: đội phân tích cần bản đã che PII, ứng dụng cũ cần định dạng XML thay vì JSON, trang web cần ảnh đã resize. Cách cũ là tạo và lưu nhiều bản sao đã xử lý — tốn kém và khó đồng bộ.

S3 Object Lambda cho phép chèn một hàm AWS Lambda vào giữa, để biến đổi object ngay lúc nó được đọc (GET), không cần lưu bản sao. Kiến trúc cần: một bucket, một S3 Access Point hỗ trợ, và một S3 Object Lambda Access Point. Ứng dụng gọi Object Lambda Access Point → nó kích hoạt Lambda → Lambda đọc object gốc qua access point hỗ trợ, biến đổi, rồi trả kết quả về cho người gọi.

  • Use case: che (redact) thông tin nhạy cảm như PII cho môi trường phân tích, chuyển đổi định dạng (XML → JSON), resize/đóng dấu mờ ảnh on-the-fly, làm giàu dữ liệu từ nguồn khác.
  • Cần nhớ: chỉ một bucket duy nhất, điểm mấu chốt là biến đổi lúc đọc, dùng cặp Access Point + Object Lambda Access Point.

6.3. S3 CORS

CORS là cơ chế bảo mật của trình duyệt, không phải của riêng S3. Một origin được định nghĩa bởi scheme + host + port (ví dụ https://www.example.com). Theo mặc định, trình duyệt chặn JavaScript của một origin gọi tài nguyên ở origin khác — trừ khi origin đích cho phép qua các header CORS.

Cách hoạt động: trước request thật, trình duyệt gửi một request “thăm dò” (preflight) tới S3, nếu cấu hình CORS của bucket cho phép origin đó, trình duyệt mới gửi request thật.

  • Use case kinh điển trong đề thi: một website tĩnh host trên bucket A nạp file (ảnh, font, dữ liệu) từ bucket B → bạn phải bật cấu hình CORS trên bucket B (bucket đích), cho phép origin của bucket A.
  • Cần nhớ: thấy “trình duyệt”, “cross-origin”, “website tĩnh gọi bucket khác” → CORS, và cấu hình đặt ở bucket được gọi.

Liên quan