10. Bảo mật trong S3



Như đã hẹn, hãy cùng tìm hiểu cách S3 bảo mật dữ liệu lưu trữ trên đó, trên hai khía cạnh: kiểm soát truy cậpmã hoá dữ liệu.

Trong bài này:

1. Bucket Policy

Đây là resource policy của S3 cho mỗi bucket, xác định quyền truy cập và các hành động được phép trên bucket và các object trong bucket. Bucket policy có thể xác định quyền cho các danh tính thuộc cùng tài khoản, khác tài khoản, thậm chí người dùng ẩn danh trên Internet.

S3 cũng sử dụng Access control list (ACL) để quản lý truy cập vào bucket và object, nhưng tính năng này không được khuyến khích và mặc định bị tắt, thay vào đó hãy sử dụng bucket policy.

1.1. Cấp quyền Chỉ-Đọc cho Người dùng Ẩn danh

Việc này là cần thiết nếu bạn muốn đặt một trang web tĩnh (chỉ gồm HTML và JavaScript cơ bản) lên S3. Mặc định, AWS chặn kết nối từ Internet đến bucket, nhưng có thể dễ dàng đặt lại trên giao diện, bằng cách bỏ chọn Block all public access trong Permissions của bucket.

Grant Public Access

1.2. Cấp quyền cho Danh tính trong cùng Tài khoản

Dưới đây là một bucket policy cho phép IAM User awscoban trong cùng tài khoản đọc và ghi dữ liệu lên bucket awscoban, nhưng không cho phép bất cứ ai xoá dữ liệu trong bucket đó.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::123456789012:user/awscoban"
      },
      "Action": [
        "s3:GetObject",
        "s3:PutObject"
      ],
      "Resource": "arn:aws:s3:::awscoban/*"
    },
    {
      "Effect": "Deny",
      "Principal": "*",
      "Action": "s3:DeleteObject",
      "Resource": "arn:aws:s3:::awscoban/*"
    }
  ]
}

1.3. Cấp quyền cho Danh tính ở Tài khoản khác

Bucket policy dưới đây cho phép Root User của tài khoản 987654321098 được đọc và ghi object trong bucket awscoban của tài khoản 123456789012:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "CrossAccountAccess",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::987654321098:root" // Tài khoản khác
      },
      "Action": [
        "s3:GetObject",
        "s3:PutObject"
      ],
      "Resource": "arn:aws:s3::123456789012:awscoban/*"
    }
  ]
}

Tham khảo thêm ví dụ các bucket policy cho nhiều trường hợp khác tại đây.

2. Mã hoá Dữ liệu trong S3

Với các tổ chức có yêu cầu bảo mật thông tin cao, hoặc với dữ liệu riêng tư cần tuân thủ các quy định chống rò rỉ, dữ liệu cần được mã hoá cả khi lưu trữ (encryption at rest) và khi truyền tải (encryption in-transit).

Mã hoá khi truyền tải có thể được tự động đảm bảo với kết nối HTTPS (sử dụng giao thức TLS). Người dùng chỉ cần tạo bucket policy chặn tất cả kết nối không có mã hoá TLS, ví dụ:

{
    "Version":"2012-10-17",		 	 	 
    "Statement": [{
        "Sid": "Chỉ chấp nhận kết nối có mã hoá TLS",
        "Action": "s3:*",
        "Effect": "Deny",
        "Resource": [
            "arn:aws:s3:::awscoban",
            "arn:aws:s3:::awscoban/*"
        ],
        "Condition": {
            "Bool": {
                "aws:SecureTransport": "false"
            }
        },
        "Principal": "*"
    }]
}

Trong đó, aws:SecureTransport là một biến điều kiện toàn cục (global condition key), luôn xuất hiện trong mọi kết nối đến AWS, cho biết kết nối này có sử dụng TLS hay không. Bucket policy trên sẽ chặn tất cả kết nối đến bucket awscoban và tất cả object trong bucket đó, nếu aws:SecureTransport == false, tức không mã hoá TLS khi truyền tải.

Mã hoá khi lưu trữ trên S3 phức tạp hơn, chia thành hai loại chính: mã hoá phía máy khách (client-side) và phía máy chủ (server-side). Loại thứ hai lại chia thành 3 phương pháp tuỳ thuộc vào khoá mật mã, ta sẽ tìm hiểu chi tiết dưới đây.

2.1. Client-Side Encryption

Client-Side Encryption

Như tên gọi, người dùng sẽ mã hoá sẵn dữ liệu trước khi tải lên S3. Lưu ý là trên đường truyền, khi dùng HTTPS, dữ liệu đã mã hoá sẽ được mã hoá thêm một lần nữa (in-transit, dùng TLS), rồi khi đến S3, bản mã hoá gốc sẽ được lưu trữ.

Đây là cách thức mã hoá có độ bảo mật cao nhất, người dùng toàn quyền quyết định khoá mật mã và thuật toán mã hoá. S3 không thực hiện thao tác nào, và cũng không nhìn thấy dữ liệu gốc. Điểm trừ tất nhiên là quản lý phức tạp hơn, tăng khối lượng tính toán phía máy khách (để triển khai mã hoá).

2.2. Server-Side Encryption

Ngược lại, Server-Side Encryption (SSE) thực hiện mã hoá phía máy chủ (tức S3). Dữ liệu S3 nhận được là dữ liệu gốc (có mã hoá khi truyền tải), tức người dùng cần tin tưởng giao cho S3 xử lý. Trong đa số trường hợp, tại các tổ chức không có yêu cầu bảo mật quá chặt chẽ, đây là lựa chọn phổ biến, giảm chi phí quản lý và tính toán phía máy khách. Chi phí tính toán cho việc mã hoá (CPU, RAM) tưởng như không đáng kể, nhưng thực tế sẽ gây ảnh hưởng lớn đến hiệu năng với số lượng và kích thước dữ liệu đủ lớn.

Có thể chia SSE thành 3 loại tuỳ thuộc ai quản lý khoá mật mã, cụ thể như sau:

2.2.1. SSE-C

SSE-C

Viết tắt của Server-Side Encryption with Customer provided key, tức người dùng sẽ cung cấp khoá mật mã cùng thuật toán mã hoá. Bắt buộc sử dụng HTTPS, S3 sẽ từ chối tất cả kết nối HTTP nếu sử dụng SSE-C. S3 nhận dữ liệu gốc và khoá mật mã, mã hoá dữ liệu bằng thuật toán được chỉ định. Sau đó, hash của khoá mật mã được gán vào và lưu cùng bản mã, và S3 xoá khoá mật mã được cung cấp.

Khi cần giải mã dữ liệu lưu trên S3, người dùng cũng cung cấp khoá mật mã. S3 kiểm tra nhanh khoá được cấp có đúng là khoá đã dùng để mã hoá hay không qua giá trị hash lưu cùng, rồi tiến hành giải mã và trả bản gốc dữ liệu cho người dùng.

Nhắc lại, S3 không lưu khoá mật mã, chỉ lưu hash của nó (không thể dịch ngược ra khoá). Người dùng tự quản lý khoá nào mã hoá object nào.

Từ tháng 4/2026, AWS sẽ ngừng cung cấp SSE-C cho các bucket mới và các bucket cũ không sử dụng SSE-C.

2.2.2. SSE-S3

SSE-S3

Thay vì người dùng tự quản lý khoá mật mã, nếu sử dụng phương án này, S3 sẽ đảm nhận cả việc quản lý khoá và mã hoá dữ liệu, người dùng chỉ cần tải dữ liệu gốc lên S3 (qua HTTPS để bảo đảm mã hoá khi truyền tải).

Mỗi object sẽ được mã hoá bằng một object key riêng biệt, bản chất là một Data Encryption Key (DEK) được tạo ra bởi một khoá chủ được quản lý bởi S3 (AWS Managed Key). Sau khi mã hoá, S3 lưu lại bản mã của object key (mã hoá bằng khoá chủ S3 key), rồi xoá bản rõ đi để tăng bảo mật.

Cả bản mã của dữ liệu và bản mã của object key sẽ được lưu lại, và khi cần giải mã, S3 sử dụng khoá chủ S3 key để giải mã ra object key tương ứng, sau đó dùng nó để giải mã dữ liệu.

Tất cả object tải lên S3 mặc định sẽ được tự động mã hoá bằng SSE-S3, như một lớp bảo mật cơ sở, miễn phí và không ảnh hưởng đến hiệu năng.

Tuy nhiên, SSE-S3 có 3 hạn chế:

  • Người dùng không thể quản lý khoá, không phù hợp trong môi trường đặt nặng bảo mật và kiểm soát.
  • Không hỗ trợ xoay tua khoá theo ý muốn (key rotation). Việc xoay tua do AWS tự động thực hiện do đây là AWS Managed Key.
  • Không thể phân tách chức năng (role separation). Ví dụ, nếu ta muốn quản trị viên S3 chỉ có quyền quản lý object và bucket, không được phép giải mã để đọc dữ liệu gốc, SSE-S3 không thể thực hiện. Thậm chí khi một người dùng chỉ với quyền thực hiện s3:GetObject yêu cầu dữ liệu, S3 cũng sẽ tự động giải mã object và trả về. Nếu muốn giới hạn quyền giải mã, cần sử dụng SSE-KMS.

2.2.3. SSE-KMS

SSE-KMS

Với lựa chọn này, việc quản lý khoá mật mã chủ được giao cho KMS thay vì S3. Người dùng tạo KMS Key là Customer Managed Key trong KMS, và áp dụng quy trình mã hoá phong bì (envelope encryption) như đã thảo luận trong bài KMS để mã hoá object.

Khi tải dữ liệu lên S3:

  • S3 kiểm tra các quyền s3:PutObjectkms:GenerateDataKey của người dùng hoặc ứng dụng. Ngoài ra nếu sử dụng multi-part upload, cần thêm quyền kms:Decrypt.
  • Nếu không có vấn đề, S3 gọi API đến KMS thực hiện hành động kms:GenerateDataKey với KMS Key được chỉ định.
  • KMS trả về bản rõ và bản mã của DEK, sau đó xoá bản rõ DEK trong bộ nhớ của mình. Đây là thiết kế bảo mật của mã hoá phong bì, KMS không sử dụng DEK nên không cần giữ lại.
  • S3 sử dụng bản rõ DEK để mã hoá object, lưu lại cả bản mã của dữ liệu và DEK, cùng metadata về KMS Key ID đã sử dụng. Rồi xoá bản rõ DEK.

Phân tích rõ hơn một chút, khi sử dụng multi-part upload, vì object sẽ được chia nhỏ ra để tải lên, mỗi phần nhỏ đều được mã hoá bằng DEK, nên S3 cần giải mã các phần này để kết hợp thành object cuối cùng, do đó cần quyền kms:Decrypt. Tất nhiên sau khi kết hợp, object sẽ được mã hoá lại bằng DEK rồi mới lưu trữ.

Khi cần truy xuất dữ liệu:

  • S3 kiểm tra các quyền s3:GetObjectkms:Decrypt của người dùng hoặc ứng dụng.
  • Nếu thoả mãn, S3 đưa bản mã DEK và KMS Key ID đến KMS để giải mã DEK.
  • KMS trả về bản rõ DEK, sau đó xoábản rõ DEK trong bộ nhớ của mình.
  • S3 sử dụng bản rõ DEK để giải mã object, rồi xoá bản rõ DEK.
  • S3 trả về object đã giải mã cho người dùng/ứng dụng.

Rõ ràng, theo quy trình này, SSE-KMS khắc phục được ít nhất 2 điểm yếu của SSE-S3:

  • Xoay tua khoá (key rotation): Customer Managed Key hỗ trợ xoay tua khoá nếu người dùng bật tính năng này, với chu kỳ mặc định 365 ngày.
  • Phân tách chức năng (role separation): trong ví dụ ở SSE-S3, với việc tích hợp KMS để quản lý khoá, ta có thể phân tách việc quản lý object và bucket mà không cho phép quản trị viên (có tất cả quyền trên S3) giải mã để đọc dữ liệu, vì việc giải mã cần cả quyền KMS kms:Decrypt.

3. Bucket Key

Khi sử dụng SSE-KMS, một hạn chế lớn là mỗi lần tải dữ liệu lên S3 là một lần gọi KMS API (tại thời điểm viết bài là $0.03 mỗi 10,000 lần gọi). Với các ứng dụng có số lượng tải lên lớn, chi phí sẽ tăng lên, chưa kể việc có thể bị rate limit.

Bucket Key

Để giải quyết vấn đề này, AWS đưa ra giải pháp sử dụng bucket key. Hiểu đơn giản là áp dụng thêm một tầng mã hoá phong bì nữa. Dùng KMS Key tạo ra bucket key, là một khoá tổng lưu trên S3 trong thời gian ngắn. Tất cả DEK dùng để mã hoá object sẽ được sinh ra bởi bucket key, thay vì KMS Key. Như hình trên, giả sử người dùng tải lên 3 object, bucket key sẽ tạo ra 3 DEK tương ứng để mã hoá. Quá trình mã hoá tương tự như khi dùng KMS key để tạo DEK đã trình bày ở phần trên.

Hết thời gian lưu trữ, bản rõ của bucket key sẽ bị xoá đi. Tuy nhiên, S3 sẽ lưu lại bản mã của tất cả bucket key (được mã hoá bằng KMS Key) để giải mã dữ liệu. Quá trình giải mã dữ liệu ngắn gọn như sau:

  • S3 gọi KMS để giải mã bucket key đã dùng.
  • S3 sử dụng bucket key để giải mã ra DEK cho object tương ứng.
  • S3 sử dụng DEK để giải mã object.

Theo thiết kế này, S3 chỉ cần gọi KMS API một lần để tạo bucket key, và vì bucket key được lưu tạm trên S3, quá trình sinh DEK không cần đến KMS nữa. Vấn đề chi phí và hiệu năng khi tải lên một lượng lớn object được giải quyết.

Tài liệu tham khảo

  1. Ví dụ Bucket Policy
  2. S3 Server-Side Encryption
  3. Bucket Key

Hy vọng qua bài viết này, bạn đọc đã hiểu rõ cách cấp quyền truy cập vào S3 thông qua bucket policy, và phân biệt rõ các phương pháp mã hoá dữ liệu trong S3 để áp dụng đúng trong các tình huống cụ thể. Tiếp theo, hãy tìm hiểu các phân lớp lưu trữ trong S3 để tối ưu chi phí.

Nếu có câu hỏi, bạn có thể nhắn mình trên fanpage hoặc group. Cảm ơn bạn.