---
title: "How to ransomware-proof S3 backups with Object Lock"
canonical: https://enum.co/blog/object-lock-ransomware
locale: en
publishedAt: 2026-04-30
author: "Max Heyer"
summary: "S3 Object Lock turns your bucket into write-once storage. Even with valid credentials, ransomware can't delete what's locked. Here's how it works on enum."
---
The modern ransomware playbook ends with a step most teams haven't planned for: before encrypting production, the attackers kill your backups.

They log into your S3 bucket with credentials pulled from a developer's machine and delete the recovery objects. Or, worse, they overwrite them with garbage, wait for the corruption to replicate to your offsite copy, then trigger the encryption. Your tested restore procedure now restores poison.

If your bucket trusts whoever holds the API key to delete or overwrite, your backups aren't backups. They're a liability with a versioning history.

That's the problem S3 Object Lock solves.

## The S3 default isn't enough

A standard S3 bucket has two delete behaviors. With versioning off, a `DELETE` removes the object permanently. With versioning on, a `DELETE` creates a delete marker. The object is still there, hidden, but anyone with `s3:DeleteObjectVersion` can remove it for good.

Both behaviors require nothing more than valid credentials. If an attacker has your access keys, they have your backups.

You can mitigate this with separate credentials, scoped IAM policies, MFA delete, separate accounts. All of those help. None of them are sufficient. Your protection still lives at the credential layer, exactly the layer attackers spend their time compromising.

Object Lock moves the protection somewhere they can't reach: the storage layer itself.

## What Object Lock does

S3 Object Lock implements a **Write-Once-Read-Many** model on top of the standard S3 API. Once an object version is locked, it cannot be deleted or overwritten until its retention period expires. Not by the bucket owner. Not by an admin. Not by anyone.

Two mechanisms:

**Retention period.** A timestamp on the object version. Until that timestamp passes, the object is immutable. You can extend it. Depending on the mode, you cannot shorten it.

**Legal hold.** A binary flag with no expiry. While set, the object can't be deleted. Used for litigation hold, regulatory investigations, anything that needs indefinite immutability.

Two prerequisites:

1. **Versioning must be enabled.** Object Lock locks specific object _versions_, not object names. Without versioning, the concept doesn't apply.
2. **Object Lock must be enabled at bucket creation.** You can't retroactively enable it on an existing bucket.

Provision your buckets with Object Lock from day one. Migrating later means copying every object to a new bucket, which gets painful fast.

## Governance Mode vs Compliance Mode

Object Lock has two modes, and the choice matters more than most teams realize.

**Governance Mode.** Objects are locked, but a principal with the `s3:BypassGovernanceRetention` permission can override the lock. Useful for "we want immutability, but we need an emergency escape hatch."

**Compliance Mode.** Objects are locked. Period. No principal, including the root account, can delete the object or shorten its retention until the period expires.

If you're using Object Lock as ransomware protection, only Compliance Mode counts. Governance Mode protects against operator error. Compliance Mode protects against attackers who fully own your account.

The trade-off is real: in Compliance Mode, a fat-fingered retention policy ("oops, 10 years instead of 10 days") costs you storage you can't reclaim until the timer runs out. The right answer for most workloads is 14 to 30 days for routine backups, with longer windows reserved for explicit regulatory requirements.

A common failure mode: teams default to Governance Mode "just in case," then never enforce restrictions on the bypass permission. That's worse than no Object Lock at all. It provides the feeling of safety without the protection.

## Setting it up on enum

enum's object storage is fully Object Lock compatible. Same API surface as AWS S3, same semantics.

Create a bucket with Object Lock enabled:

```bash
enumctl storage buckets create backups-prod \
  --object-lock \
  --region fra
```

Set a default retention configuration so every uploaded object inherits it:

```bash
aws s3api put-object-lock-configuration \
  --bucket backups-prod \
  --endpoint-url https://fra.s3.enum.cloud \
  --object-lock-configuration '{
    "ObjectLockEnabled": "Enabled",
    "Rule": {
      "DefaultRetention": {
        "Mode": "COMPLIANCE",
        "Days": 30
      }
    }
  }'
```

Upload a backup:

```bash
aws s3 cp pg-dump-2026-04-30.sql.gz \
  s3://backups-prod/postgres/ \
  --endpoint-url https://fra.s3.enum.cloud
```

Now try to delete a specific version. `--version-id` is the destructive form: what an attacker would use to permanently remove the object, not just hide it behind a delete marker:

```bash
aws s3api delete-object \
  --bucket backups-prod \
  --key postgres/pg-dump-2026-04-30.sql.gz \
  --version-id <version-id> \
  --endpoint-url https://fra.s3.enum.cloud

An error occurred (AccessDenied) when calling the DeleteObject operation:
Access Denied because object protected by object lock.
```

That's the entire point. The credential is valid. The IAM policy allows the action. The storage layer refuses anyway.

Verify the lock status on any object:

```bash
aws s3api get-object-retention \
  --bucket backups-prod \
  --key postgres/pg-dump-2026-04-30.sql.gz \
  --version-id <version-id> \
  --endpoint-url https://fra.s3.enum.cloud
```

```json
{
  "Retention": {
    "Mode": "COMPLIANCE",
    "RetainUntilDate": "2026-05-30T14:22:11+00:00"
  }
}
```

For a complete backup workflow, point Restic, pgBackRest, Velero, or whatever you already use at the bucket. They write to S3 normally; the bucket's default retention applies the lock automatically. Your tooling doesn't need to know.

## What Object Lock doesn't do

Object Lock is one layer. Not the entire defense.

**It doesn't protect what you haven't backed up yet.** If your attacker waits to encrypt production until after a clean snapshot rolls out of your retention window, you're back where you started. Industry medians for ransomware dwell time are still over a week. A 7-day retention is too short. Pick something longer than your worst-case detection time.

**It doesn't protect against malicious uploads.** An attacker with write credentials can upload garbage and lock it, leaving you paying for storage you can't delete. Restrict who holds write credentials. Monitor object counts and sizes. Enforce key prefix conventions through bucket policies.

**It doesn't replace least-privilege IAM.** Backup credentials should still be scoped narrowly: read-only for restore tooling, append-only for backup writers. Object Lock is a backstop, not the only line.

The lock is an enabler. The recovery procedure is the actual product.

## Get started

Object Lock is available on enum's object storage in Frankfurt today. Default retention configurations, Compliance Mode, Legal Hold, all supported, S3-API compatible.

Full reference in the [docs](https://docs.enum.co). Questions about migrating? [mail@enum.co](mailto:mail@enum.co).
