Back to Checks
aws

Kinesis Data Firehose delivery stream is encrypted at rest

firehose_stream_encrypted_at_rest
Service: firehoseSeverity: medium
OfficialBuilt-inVerified
Categories:encryption
Resource Type:AwsKinesisStream

Amazon Data Firehose delivery streams must enable server-side encryption at rest with AWS KMS regardless of the source type. Encryption of upstream sources such as Kinesis Data Streams or MSK does not replace the need to protect the delivery stream itself.

Risk

Unencrypted Firehose data at rest can be read if storage or backups are accessed, harming confidentiality and integrity. Disk-level access, snapshots, or misconfigured destinations enable data exfiltration or tampering. Lacking KMS-backed controls also reduces key rotation, segregation of duties, and auditability.

Run this check with Prowler

Run in Prowler Cloud
Run
prowler aws --checks firehose_stream_encrypted_at_rest

Recommendation

Enable server-side encryption for Firehose with AWS KMS. Prefer customer managed keys (CMEK) to enforce least privilege, rotation, and auditing. Ensure upstream Kinesis sources are encrypted and confirm MSK defaults meet policy. Monitor KMS health signals and deny writes without encryption. Apply defense in depth at destinations.

Remediation

CLI

aws firehose start-delivery-stream-encryption --delivery-stream-name <delivery-stream-name> --delivery-stream-encryption-configuration-input KeyType=AWS_OWNED_CMK

Terraform

# Terraform: Enable at-rest encryption for a Firehose delivery stream
resource "aws_kinesis_firehose_delivery_stream" "<example_resource_name>" {
  name        = "<example_resource_name>"
  destination = "extended_s3"

  server_side_encryption {
    enabled = true  # critical: turns on SSE at rest (uses AWS owned KMS key by default)
  }

  extended_s3_configuration {
    role_arn   = "arn:aws:iam::<example_account_id>:role/<example_resource_name>"
    bucket_arn = "arn:aws:s3:::<example_resource_name>"
  }
}

NativeIaC

# CloudFormation: Enable at-rest encryption for a Firehose delivery stream
Resources:
  <example_resource_name>:
    Type: AWS::KinesisFirehose::DeliveryStream
    Properties:
      DeliveryStreamEncryptionConfigurationInput:
        KeyType: AWS_OWNED_CMK  # critical: enables SSE at rest using AWS owned KMS key
      ExtendedS3DestinationConfiguration:
        BucketARN: arn:aws:s3:::<example_resource_name>
        RoleARN: arn:aws:iam::<example_account_id>:role/<example_resource_name>

Other

1In the AWS Console, go to Amazon Data Firehose
2Select the affected delivery stream and click Edit
3Under Server-side encryption, set to Enabled (choose AWS owned key)
4Click Save changes

Check Code

from typing import List

from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.firehose.firehose_client import firehose_client
from prowler.providers.aws.services.firehose.firehose_service import EncryptionStatus
from prowler.providers.aws.services.kafka.kafka_client import kafka_client
from prowler.providers.aws.services.kinesis.kinesis_client import kinesis_client
from prowler.providers.aws.services.kinesis.kinesis_service import EncryptionType


class firehose_stream_encrypted_at_rest(Check):
    """Check if Firehose Streams are encrypted at rest.

    This class verifies that all Firehose Streams have at rest encryption enabled by checking if KMS encryption is active and a KMS Key is configured.
    """

    def execute(self) -> List[Check_Report_AWS]:
        """Execute the Firehose Stream Encrypted at Rest check.

        Iterates over all Firehose Streams and checks if KMS encryption is enabled and a KMS Key is configured.

        Returns:
            List[Check_Report_AWS]: A list of reports for each Firehose Stream.
        """
        findings = []
        for stream in firehose_client.delivery_streams.values():
            report = Check_Report_AWS(metadata=self.metadata(), resource=stream)
            report.status = "FAIL"
            report.status_extended = f"Firehose Stream {stream.name} does not have at rest encryption enabled."

            if stream.kms_encryption == EncryptionStatus.ENABLED:
                report.status = "PASS"
                report.status_extended = f"Firehose Stream {stream.name} does have at rest encryption enabled."

            elif stream.delivery_stream_type == "KinesisStreamAsSource":
                source_stream_arn = stream.source.kinesis_stream.kinesis_stream_arn
                source_stream = kinesis_client.streams.get(source_stream_arn, None)
                if source_stream:
                    if source_stream.encrypted_at_rest == EncryptionType.KMS:
                        report.status_extended = f"Firehose Stream {stream.name} does not have at rest encryption enabled even though source stream {source_stream.name} has at rest encryption enabled."
                    else:
                        report.status_extended = f"Firehose Stream {stream.name} does not have at rest encryption enabled and the source stream {source_stream.name} is not encrypted at rest."
                else:
                    report.status_extended = f"Firehose Stream {stream.name} does not have at rest encryption enabled and the referenced source stream could not be found."

            elif stream.delivery_stream_type == "MSKAsSource":
                msk_cluster_arn = stream.source.msk.msk_cluster_arn
                msk_cluster = None
                if msk_cluster_arn:
                    for cluster in kafka_client.clusters.values():
                        if cluster.arn == msk_cluster_arn:
                            msk_cluster = cluster
                            break

                    if msk_cluster:
                        # All MSK clusters (both provisioned and serverless) always have encryption at rest enabled by AWS
                        # AWS MSK always encrypts data at rest - either with AWS managed keys or CMK
                        report.status = "PASS"
                        if msk_cluster.kafka_version == "SERVERLESS":
                            report.status_extended = f"Firehose Stream {stream.name} uses MSK serverless source which always has encryption at rest enabled by default."
                        else:
                            report.status_extended = f"Firehose Stream {stream.name} uses MSK provisioned source which always has encryption at rest enabled by AWS (either with AWS managed keys or CMK)."
                    else:
                        report.status_extended = f"Firehose Stream {stream.name} uses MSK source which always has encryption at rest enabled by AWS."

            findings.append(report)

        return findings