Skip to main content

Field-Level Encryption

Encrypt specific fields in your entities using the [Encrypted] attribute.

Overview

Field-level encryption lets you encrypt individual properties on your entities. Encrypted fields are stored as binary data in DynamoDB and decrypted transparently when entities are loaded.

Marking Fields for Encryption

Use the [Encrypted] attribute on any property that should be encrypted at rest:

[DynamoDbTable("customers")]
public partial class Customer
{
[PartitionKey]
[DynamoDbAttribute("pk")]
public string CustomerId { get; set; } = string.Empty;

[DynamoDbAttribute("name")]
public string Name { get; set; } = string.Empty;

[Encrypted]
[Sensitive]
[DynamoDbAttribute("ssn")]
public string SocialSecurityNumber { get; set; } = string.Empty;

[Encrypted]
[Sensitive]
[DynamoDbAttribute("creditCard")]
public string CreditCardNumber { get; set; } = string.Empty;
}

Transparent Encryption and Decryption

Once configured, encryption and decryption happen automatically during Put and Get operations:

// Data is encrypted before writing to DynamoDB
await table.Customers.PutAsync(customer);

// Data is decrypted after reading from DynamoDB
var loaded = await table.Customers.GetAsync("customer123");
Console.WriteLine(loaded.SocialSecurityNumber); // Decrypted value

Decryption Failure Modes

By default, any decryption failure throws an exception. You can configure the library to skip encrypted fields that fail to decrypt, which is useful for STS downscoping scenarios where a service has reduced KMS permissions.

DecryptionFailureMode Enum

ValueBehavior
Throw (default)Any decryption failure throws an exception
SkipFieldsRecoverable failures (no encryptor, access denied) leave the property at its CLR default and log a warning. Integrity failures (corrupted ciphertext, wrong key) always throw.

Configuration

using Oproto.FluentDynamoDb;
using Oproto.FluentDynamoDb.Providers.Encryption;

// Skip encrypted fields when decryption fails (e.g., access denied)
var options = new FluentDynamoDbOptions()
.WithEncryption(encryptor)
.WithDecryptionFailureMode(DecryptionFailureMode.SkipFields);

var table = new CustomersTable(client, "customers", options);

Read-Only Access Without Encryptor

When a service only needs non-encrypted fields:

// No encryptor configured — encrypted fields are skipped
var readOnlyOptions = new FluentDynamoDbOptions()
.WithDecryptionFailureMode(DecryptionFailureMode.SkipFields);

var table = new CustomersTable(client, "customers", readOnlyOptions);

// Non-encrypted fields load normally; encrypted fields stay at CLR default
var customer = await table.Customers.GetAsync("customer123");
Console.WriteLine(customer.Name); // Populated normally
Console.WriteLine(customer.SocialSecurityNumber); // "" (CLR default)

Write Behavior

Write operations (ToDynamoDbAsync) are never affected by this setting — they always throw on failure to prevent silent data loss.

Failure Classification

CategoryExamplesBehavior in SkipFields Mode
RecoverableNo encryptor, KMS access deniedField skipped, warning logged
IntegrityInvalid ciphertext, wrong key, context validation failedAlways throws

Combining with Other Attributes

The [Encrypted] attribute works alongside other attributes:

CombinationBehavior
[Encrypted] + [Sensitive]Encrypted at rest, redacted in logs
[Encrypted] + [BlobStorage]Encrypted before uploading to S3
[Encrypted] + [JsonBlob]JSON serialized, then encrypted

Cache TTL

Control how long decrypted data keys are cached:

[Encrypted(CacheTtlSeconds = 600)]
[DynamoDbAttribute("ssn")]
public string SocialSecurityNumber { get; set; } = string.Empty;

See Also