The Problem
A centralized account collects log data into multiple S3 buckets. Requirements:
- Data must be encrypted before upload (not just after arrival at S3)
- Data must be encrypted in transit
Server-side encryption alone does not satisfy the “before upload” requirement.
The Solution
Use client-side encryption. The data is encrypted on your machine, then the ciphertext travels over HTTPS to S3. S3 never sees the plaintext.
How It Works
The Difference Between Client-Side and Server-Side
1
2
3
4
5
Client-Side:
[Plaintext] → encrypt locally → [Ciphertext] → HTTPS → S3 stores ciphertext
Server-Side (SSE-KMS, SSE-S3, SSE-C):
[Plaintext] → HTTPS → S3 receives plaintext → encrypt at rest
Server-side encryption means AWS sees your plaintext briefly. For strict “before upload” requirements, that is not acceptable.
Client-Side With AWS Encryption SDK + KMS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import boto3
from aws_encryption_sdk import EncryptionSDKClient, StrictAwsKmsMasterKeyProvider
client = EncryptionSDKClient()
kms_kwargs = dict(key_ids=[
"arn:aws:kms:us-east-1:123456789012:key/abc-123"
])
master_key_provider = StrictAwsKmsMasterKeyProvider(**kms_kwargs)
with open("log.dat", "rb") as f:
plaintext = f.read()
ciphertext, encryptor_header = client.encrypt(
source=plaintext,
key_provider=master_key_provider
)
# Upload the ciphertext (never plaintext) to S3
s3 = boto3.client("s3")
s3.put_object(Bucket="logs", Key="log.dat.enc", Body=ciphertext)
The AWS Encryption SDK:
- Calls KMS to generate a data key
- Encrypts the data locally with that key
- Wraps the encrypted data key in the ciphertext
- Returns ciphertext ready for upload
KMS never sees the plaintext. S3 never sees the plaintext. The plaintext existed only briefly in your process memory.
Client-Side Encryption Options
| Method | Key Storage | Complexity |
|---|---|---|
| AWS Encryption SDK + KMS | KMS manages the master key | Low |
| S3 Encryption Client (deprecated) | You manage the master key | Medium |
| Custom AES-256 | You manage everything | High |
For anything AWS-hosted, use the AWS Encryption SDK with KMS. It handles envelope encryption, key rotation, and multi-region key policies.
Why Not the Alternatives?
Server-side encryption (SSE-KMS, SSE-S3, SSE-C) — All of these encrypt after S3 receives the data. They fail the “before upload” requirement.
Bucket policy requiring SSE-S3 — This enforces server-side encryption. Same problem — encryption happens after arrival at S3.
Default KMS bucket encryption — Same server-side encryption category. Does not encrypt before upload.
Key Takeaways
- Client-side encryption is the only way to ensure data is encrypted before it reaches AWS
- AWS Encryption SDK + KMS is the recommended library — do not roll your own
- KMS generates a data key, but only the encrypted form travels to S3
- All server-side encryption options (SSE-*) encrypt after S3 receives the plaintext
- Client-side encryption also protects against a compromised transport layer — the ciphertext is safe even if TLS fails
Never miss a story from us, subscribe to our newsletter