← Alle Neuigkeiten
security5 Min. Lesezeit

S3 bucket misconfigurations in AI-generated infrastructure code

Von Seaworthy · 24. Juni 2026

Network cables plugged into server rack switches in a data center

Photo by Taylor Vick on Unsplash

When you ask an AI assistant to "write me some Terraform to create an S3 bucket for user uploads," you usually get code that works. The bucket gets created, files go in, your app runs. What you often don't get is a bucket that's configured correctly from a security standpoint.

This isn't a knock on any particular model. It's structural. AI tools optimise for satisfying the literal request. "Create a bucket" doesn't include "and lock it down properly," so the assistant skips the locking down.

What gets left out

The AWS S3 console has spent years adding default-on protections. Block Public Access is now on by default for new buckets created through the console. The AWS CLI and SDKs, though, don't enforce this. Neither does most AI-generated Terraform.

A typical AI-generated bucket resource looks like this:

resource "aws_s3_bucket" "uploads" {
  bucket = "my-app-uploads"
}

That's it. Bucket created. The AI might add a few tags if you asked nicely. What it almost certainly won't add is explicit public access blocking, versioning, server-side encryption, or access logging. Each of those omissions is exploitable in its own way.

The public access issue is the most immediate. Without an explicit aws_s3_bucket_public_access_block resource, your bucket's public access settings are inherited from the account level. If your account was itself set up with AI-generated code and that code didn't configure account-level block public access, you now have a publicly readable bucket sitting in production.

The bucket policy problem

It gets worse when you ask for permissions. When developers ask an AI assistant to "allow my Lambda to access the uploads bucket," the generated policy often ends up looking like this:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:*",
      "Resource": [
        "arn:aws:s3:::my-app-uploads",
        "arn:aws:s3:::my-app-uploads/*"
      ]
    }
  ]
}

Principal: "*" means anyone. Action: "s3:*" means everything: read, write, delete, list. This policy allows any unauthenticated request from the public internet to download, overwrite, or wipe every object in the bucket.

The AI didn't do this out of malice. You asked it to "allow access" and it generated a policy that allows access, very thoroughly.

The correct approach is to scope the principal to the specific Lambda execution role and restrict the actions to only what's needed:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::123456789012:role/my-lambda-role"
      },
      "Action": ["s3:GetObject", "s3:PutObject"],
      "Resource": "arn:aws:s3:::my-app-uploads/*"
    }
  ]
}

That's the difference between a bucket that can be wiped by anyone with curl and one that only your Lambda can touch.

What a secure bucket actually looks like in Terraform

Here's a minimal configuration that at least covers the basics AWS expects from production infrastructure:

resource "aws_s3_bucket" "uploads" {
  bucket = "my-app-uploads"
}

resource "aws_s3_bucket_public_access_block" "uploads" {
  bucket                  = aws_s3_bucket.uploads.id
  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

resource "aws_s3_bucket_versioning" "uploads" {
  bucket = aws_s3_bucket.uploads.id
  versioning_configuration {
    status = "Enabled"
  }
}

resource "aws_s3_bucket_server_side_encryption_configuration" "uploads" {
  bucket = aws_s3_bucket.uploads.id
  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm = "AES256"
    }
  }
}

That's four resources for what the AI wrote in one. Every one of those extra resources is doing real work. The public access block stops accidental exposure from a badly scoped policy. Versioning means you can recover if someone overwrites or deletes objects. Encryption at rest means the data isn't readable if storage-level access is ever compromised.

Why AI skips the security defaults

Models learn from training data. Most Terraform examples on the internet show the minimal resource definition, because tutorials aim for brevity and getting something working fast. Security configuration is covered separately in a "hardening" section that many tutorials never reach, or as an afterthought that doesn't make it into the snippets people copy.

The model isn't wrong, strictly speaking. The code does what you asked. It just doesn't do what you meant.

You can prompt your way around this to some extent: "Write production-ready Terraform for an S3 bucket with public access blocked, versioning enabled, and AES-256 encryption at rest." That usually gets you closer. But you shouldn't have to remember to ask. Security defaults should be the default, and they're not.

Checking what you already have

If you have AI-generated infrastructure code running in production right now, the fastest audit is two commands:

aws s3api get-public-access-block --bucket YOUR_BUCKET_NAME
aws s3api get-bucket-policy --bucket YOUR_BUCKET_NAME

If the first command returns any false values, public access may be possible. If the second command returns a policy with "Principal": "*", treat that as a live incident, not a future to-do.

The CapitalOne breach in 2019 exposed over 100 million customer records partly through a misconfigured S3 bucket. That wasn't AI-generated code, but the same class of mistake is now being introduced at much higher volume by developers who are shipping faster than they're reviewing.

Seaworthy is a static analysis tool that scans repositories for security and configuration issues like these, aimed at solo developers and small teams who use AI to write code and want a quick confidence check before shipping.

Dieser Artikel wurde von KI generiert und fasst öffentlich verfügbare Quellen zusammen.

Von den Machern von Seaworthy

Seaworthy scannt dein Repository nach den Problemen, die in Artikeln wie diesem behandelt werden.

Sicherheitslücken, offengelegte Geheimnisse und Fehlkonfigurationen — erkannt, bevor du bereitstellst. Kostenlos, kein Konto nötig.

$ npx seaworthycode