S3 Bucket Allows Delete Action From All Principals

  • Query id: ffdf4b37-7703-4dfe-a682-9d2e99bc6c09
  • Query name: S3 Bucket Allows Delete Action From All Principals
  • Platform: Terraform
  • Severity: Critical
  • Category: Access Control
  • CWE: 732
  • URL: Github

Description

S3 Buckets must not allow Delete Action From All Principals, as to prevent leaking private information to the entire internet or allow unauthorized data tampering / deletion. This means the 'Effect' must not be 'Allow' when the 'Action' is Delete, for all Principals.
Documentation

Code samples

Code samples with security vulnerabilities

Positive test num. 1 - tf file
resource "aws_s3_bucket_policy" "positive1" {
  bucket = aws_s3_bucket.b.id

  policy = <<POLICY
{
  "Version": "2012-10-17",
  "Id": "MYBUCKETPOLICY",
  "Statement": [
    {
      "Sid": "IPAllow",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:DeleteObject",
      "Resource": "arn:aws:s3:::my_tf_test_bucket/*",
      "Condition": {
         "IpAddress": {"aws:SourceIp": "8.8.8.8/32"}
      }
    }
  ]
}
POLICY
}
Positive test num. 2 - tf file
resource "aws_s3_bucket_policy" "positive2" {
  bucket = aws_s3_bucket.b.id

  policy = <<POLICY
{
  "Version": "2012-10-17",
  "Id": "MYBUCKETPOLICY",
  "Statement": [
    {
      "Sid": "IPAllow",
      "Effect": "Allow",
      "Principal": {
        "AWS": "*"
      },
      "Action": "s3:DeleteObject",
      "Resource": "arn:aws:s3:::my_tf_test_bucket/*",
      "Condition": {
         "IpAddress": {"aws:SourceIp": "8.8.8.8/32"}
      }
    }
  ]
}
POLICY
}
Positive test num. 3 - tf file
module "s3_bucket" {
  source = "terraform-aws-modules/s3-bucket/aws"
  version = "3.7.0"

  bucket = "my-s3-bucket"
  acl    = "private"

  versioning = {
    enabled = true
  }

  policy = <<POLICY
{
  "Version": "2012-10-17",
  "Id": "MYBUCKETPOLICY",
  "Statement": [
    {
      "Sid": "IPAllow",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:DeleteObject",
      "Resource": "arn:aws:s3:::my_tf_test_bucket/*",
      "Condition": {
         "IpAddress": {"aws:SourceIp": "8.8.8.8/32"}
      }
    }
  ]
}
POLICY
}

Positive test num. 4 - tf file
# test action "s3:Delete*"
resource "aws_s3_bucket_public_access_block" "positive4" {
  count = length(var.positive4)

  bucket = var.positive4[count.index]

  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

data "aws_iam_policy_document" "positive4" {
  statement {
    principals {
      type        = "AWS"
      identifiers = ["*"]
    }

    effect = "Allow"

    actions = [
      "s3:Delete*",
    ]

    resources = [
      var.positive4,
      "${var.positive4}/*",
    ]
  }
}

#   "Action": "s3:Delete", "Principal":"*" and "Type":"AWS"
resource "aws_s3_bucket_policy" "positive4" {
  depends_on = [aws_s3_bucket_public_access_block.positive4]
  bucket     = var.positive4
  policy     = data.aws_iam_policy_document.positive4.json
}
Positive test num. 5 - tf file
# test action "s3:*"
resource "aws_s3_bucket_public_access_block" "positive5" {
  count = length(var.positive5)

  bucket = var.positive5[count.index]

  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

data "aws_iam_policy_document" "positive5" {
  statement {
    principals {
      type        = "AWS"
      identifiers = ["*"]
    }

    effect = "Allow"

    actions = [
      "s3:*",
    ]

    resources = [
      var.positive5,
      "${var.positive5}/*",
    ]
  }
}

#   "Action": "s3:Delete", "Principal":"*" and "Type":"AWS"
resource "aws_s3_bucket_policy" "positive5" {
  depends_on = [aws_s3_bucket_public_access_block.positive5]
  bucket     = var.positive5
  policy     = data.aws_iam_policy_document.positive5.json
}
Positive test num. 6 - tf file
# test action "*"
resource "aws_s3_bucket_public_access_block" "positive6" {
  count = length(var.positive6)

  bucket = var.positive6[count.index]

  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

data "aws_iam_policy_document" "positive6" {
  statement {
    principals {
      type        = "AWS"
      identifiers = ["*"]
    }

    effect = "Allow"

    actions = [
      "*",
    ]

    resources = [
      var.positive6,
      "${var.positive6}/*",
    ]
  }
}

#   "Action": "s3:Delete", "Principal":"*" and "Type":"AWS"
resource "aws_s3_bucket_policy" "positive6" {
  depends_on = [aws_s3_bucket_public_access_block.positive6]
  bucket     = var.positive6
  policy     = data.aws_iam_policy_document.positive6.json
}

Code samples without security vulnerabilities

Negative test num. 1 - tf file
resource "aws_s3_bucket_policy" "negative1" {
  bucket = aws_s3_bucket.b.id

  policy = <<POLICY
{
  "Version": "2012-10-17",
  "Id": "MYBUCKETPOLICY",
  "Statement": [
    {
      "Sid": "IPAllow",
      "Effect": "Deny",
      "Action": "s3:*",
      "Resource": "arn:aws:s3:::my_tf_test_bucket/*",
      "Condition": {
         "IpAddress": {"aws:SourceIp": "8.8.8.8/32"}
      }
    }
  ]
}
POLICY
}
Negative test num. 2 - tf file
module "s3_bucket" {
  source = "terraform-aws-modules/s3-bucket/aws"
  version = "3.7.0"

  bucket = "my-s3-bucket"
  acl    = "private"

  versioning = {
    enabled = true
  }

  policy = <<POLICY
{
  "Version": "2012-10-17",
  "Id": "MYBUCKETPOLICY",
  "Statement": [
    {
      "Sid": "IPAllow",
      "Effect": "Deny",
      "Action": "s3:*",
      "Resource": "arn:aws:s3:::my_tf_test_bucket/*",
      "Condition": {
         "IpAddress": {"aws:SourceIp": "8.8.8.8/32"}
      }
    }
  ]
}
POLICY
}
Negative test num. 3 - tf file
# test principal type not AWS
resource "aws_s3_bucket_public_access_block" "negative3" {
  count = length(var.negative3)

  bucket = var.negative3[count.index]

  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

data "aws_iam_policy_document" "negative3" {
  statement {
    principals {
      type        = "Service"
      identifiers = ["*"]
    }

    effect = "Allow"

    actions = [
      "s3:Delete*",
    ]

    resources = [
      var.negative3,
      "${var.negative3}/*",
    ]
  }
}

#   "Action": "s3:Delete", "Principal":"*" and "Type":"Service"
resource "aws_s3_bucket_policy" "negative3" {
  depends_on = [aws_s3_bucket_public_access_block.negative3]
  bucket     = var.negative3
  policy     = data.aws_iam_policy_document.negative3.json
}