Skip to main content

Insecure Use of Cryptography

Why is this important?โ€‹

Cryptography is hard. And when it is used in an application, it's usually to make sure user data is secure in transit and at rest. Cryptographic libraries are not always easy to use and can even contain insecurities. They often require the Developer to have a good understanding of the primitives available and expect the Developer to make the right choices. A great cryptographic library has minimal knobs and dials and uses the best cryptographic primitives by default thus freeing the Developer from having to understand the primitives and make the right decisions.

Check out this video for a high-level explanation:

Insecure Cryptographic Storage

ELB Using Weak Ciphersโ€‹

ELB Predefined or Custom Security Policies must not use weak ciphers, to reduce the risk of the SSL connection between the client and the load balancer being exploited. This means that the name of the policy_attribute must not coincide with any of a predefined list of weak ciphers.

Option A: Locate out-of-date (known weak) cipher or policy and replace it with a strong cipher or policyโ€‹

  1. Review the value of aws_load_balancer_policy policy_attribute.name. The value should be set to one of the known secure TLS ciphers. The default value (if not visibly present) of policy_attribute.name uses a known good TLS cipher
  2. If an out of date (less than optimal) cipher is being used replace it with a good one

Detailed Instructionsโ€‹

  1. Locate one of the following vulnerable patterns:

    resource "aws_load_balancer_policy" "positive1" {
    load_balancer_name = aws_elb.wu-tang.name
    policy_name = "wu-tang-ssl"
    policy_type_name = "SSLNegotiationPolicyType"

    policy_attribute {
    name = "Protocol-TLSv1.2"
    value = "true"
    }

    policy_attribute {
    // Weak cipher.
    name = "TLS_RSA_ARCFOUR_128_SHA1"
    value = "true"
    }
    }

    resource "aws_load_balancer_policy" "positive2" {
    load_balancer_name = aws_elb.wu-tang.name
    policy_name = "wu-tang-ssl"
    policy_type_name = "SSLNegotiationPolicyType"

    policy_attribute {
    // Weak cipher.
    name = "DES-CBC3-SHA"
    value = "true"
    }
    }

    resource "aws_load_balancer_policy" "positive3" {
    load_balancer_name = aws_elb.wu-tang.name
    policy_name = "wu-tang-ssl"
    policy_type_name = "SSLNegotiationPolicyType"

    policy_attribute {
    // Weak cipher.
    name = "TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384"
    value = "true"
    }
    }
  2. Modify the config to something like one of the following:

    resource "aws_load_balancer_policy" "negative1" {
    load_balancer_name = aws_elb.wu-tang.name
    policy_name = "wu-tang-ssl"
    policy_type_name = "SSLNegotiationPolicyType"

    policy_attribute {
    // Strong cipher.
    name = "ECDHE-ECDSA-AES128-GCM-SHA256"
    value = "true"
    }

    policy_attribute {
    // Strong policy.
    name = "Protocol-TLSv1.2"
    value = "true"
    }
    }

    resource "aws_load_balancer_policy" "negative2" {
    load_balancer_name = aws_elb.wu-tang.name
    policy_name = "wu-tang-ssl"
    policy_type_name = "SSLNegotiationPolicyType"

    policy_attribute {
    // Acceptable policy.
    name = "Reference-Security-Policy"
    value = "ELBSecurityPolicy-TLS-1-1-2017-01"
    }
    }
  3. Test it

  4. Ship it ๐Ÿšข and relax ๐ŸŒด

References:โ€‹

Missing Cryptographic Key Auto Rotationโ€‹

AWS KMS makes secret management easy, however, there are additional considerations to further secure the customer master keys. AWS KMS allows key rotation, which changes the backing key and provides a couple of benefits. For more information see the key rotation. Note that changing this setting won't have any impact on the encrypted data.

Option A: Enable Key Rotationโ€‹

  1. Go through the issues that GuardRails identified

  2. Change the master key resource as shown below:

    resource "aws_kms_key" "a" {
    description = "KMS key 1"
    deletion_window_in_days = 10
    enable_key_rotation = true
    }
  3. Test it

  4. Ship it ๐Ÿšข and relax ๐ŸŒด

Missing Server Side Encryptionโ€‹

AWS allows to leverage server-side encryption for SQS queues, SNS topics and S3 buckets, which is considered a security best practice. For example, when server-side encryption is used for S3, an object is encrypted before saving it to disk and decrypted when it is downloaded.

Option A: Enable Server Side Encryptionโ€‹

  1. Go through the issues that GuardRails identified

  2. Change the resources to use server-side encryption

    # SQS queue example
    resource "aws_sqs_queue" "terraform_queue" {
    name = "terraform-example-queue"
    kms_master_key_id = "alias/aws/sqs"
    kms_data_key_reuse_period_seconds = 300
    }

    # SNS topic example
    resource "aws_sns_topic" "user_updates" {
    name = "user-updates-topic"
    kms_master_key_id = "alias/aws/sns"
    }

    # S3 bucket example

    resource "aws_kms_key" "mykey" {
    description = "This key is used to encrypt bucket objects"
    deletion_window_in_days = 10
    }

    resource "aws_s3_bucket" "mybucket" {
    bucket = "mybucket"

    server_side_encryption_configuration {
    rule {
    apply_server_side_encryption_by_default {
    kms_master_key_id = aws_kms_key.mykey.arn
    sse_algorithm = "aws:kms"
    }
    }
    }
    }
  3. Test it

  4. Ship it ๐Ÿšข and relax ๐ŸŒด

Secure Ciphers Disabledโ€‹

Check if secure ciphers aren't used in AWS CloudFront.

Option A: Make sure the Minimum Protocol Version is not out of date and the default certificate is trueโ€‹

  • The aws_cloudfront_distribution viewer_certificate.minimum_protocol_version should be at least "TLSv1.1" but preferably more recent
  • The aws_cloudfront_distribution viewer_certificate.cloudfront_default_certificate should be true

Detailed Instructionsโ€‹

  1. Locate the following vulnerable pattern:

    resource "aws_cloudfront_distribution" "positive1" {
    origin {
    domain_name = "mybucket"
    origin_id = "myS3Origin"

    s3_origin_config {
    origin_access_identity = "origin-access-identity/cloudfront/ABCDEFG1234567"
    }
    }

    enabled = true

    default_cache_behavior {
    allowed_methods = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
    cached_methods = ["GET", "HEAD"]
    target_origin_id = "myS3Origin"

    forwarded_values {
    query_string = false

    cookies {
    forward = "none"
    }
    }

    viewer_protocol_policy = "allow-all"
    min_ttl = 0
    default_ttl = 3600
    max_ttl = 86400
    }

    restrictions {
    geo_restriction {
    restriction_type = "whitelist"
    locations = ["US", "CA", "GB", "DE"]
    }
    }

    viewer_certificate {
    // Vulnerable pattern: Viewers don't have to use HTTPS to request your objects.
    cloudfront_default_certificate = false
    // Protocol version too low.
    minimum_protocol_version = "SSLv3"
    }
    }
  2. Modify the config to something like the following:

    resource "aws_cloudfront_distribution" "negative1" {
    origin {
    domain_name = "mybucket"
    origin_id = "myS3Origin"

    s3_origin_config {
    origin_access_identity = "origin-access-identity/cloudfront/ABCDEFG1234567"
    }
    }

    enabled = true

    default_cache_behavior {
    allowed_methods = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
    cached_methods = ["GET", "HEAD"]
    target_origin_id = "myS3Origin"

    forwarded_values {
    query_string = false

    cookies {
    forward = "none"
    }
    }

    viewer_protocol_policy = "allow-all"
    min_ttl = 0
    default_ttl = 3600
    max_ttl = 86400
    }

    restrictions {
    geo_restriction {
    restriction_type = "whitelist"
    locations = ["US", "CA", "GB", "DE"]
    }
    }

    viewer_certificate {
    // Viewers must use HTTPS to request your objects.
    cloudfront_default_certificate = true
    // Protocol version starts with TLSv1.2
    minimum_protocol_version = "TLSv1.2_2019"
    }
    }
  3. Test it

  4. Ship it ๐Ÿšข and relax ๐ŸŒด

References:โ€‹

Storage Account Not Using Latest TLS Encryption Versionโ€‹

Ensure Storage Account is using the latest version of TLS encryption.

Option A: Make sure the Minimum Storage Account TLS Version is set correctlyโ€‹

The azurerm_storage_account min_tls_version Should be defined and have a value of at least "TLS1_2". If the min_tls_version is not defined, new storage accounts will default to "TLS1_2" which is actually good, but will incorrectly fail this rule. To work around this simply be specific about the min_tls_version.

Detailed Instructionsโ€‹

  1. Locate one of the following vulnerable patterns:

    resource "azurerm_storage_account" "positive1" {
    name = "storageaccountname"
    resource_group_name = azurerm_resource_group.example.name
    location = azurerm_resource_group.example.location
    account_tier = "Standard"
    account_replication_type = "GRS"
    // Vulnerable.
    min_tls_version = "TLS1_1"

    tags = {
    environment = "staging"
    }
    }
  2. Modify the config to something like the following:

    resource "azurerm_storage_account" "negative1" {
    name = "storageaccountname"
    resource_group_name = azurerm_resource_group.example.name
    location = azurerm_resource_group.example.location
    account_tier = "Standard"
    account_replication_type = "GRS"
    // Not Vulnerable.
    min_tls_version = "TLS1_2"

    tags = {
    environment = "staging"
    }
    }
  3. Test it

  4. Ship it ๐Ÿšข and relax ๐ŸŒด

References:โ€‹

Unencrypted Data Storageโ€‹

Option A: Ensure AWS block devices are encrypted at restโ€‹

AWS offers block devices to store data for AWS instances. There are two types:

  1. Root Block Devices
  2. EBS Block Devices

Root block devices are the main disk for an instance, and EBS block instances are additional disks that can be used for storage.

Block devices can be encrypted to provide better security.

  1. Go through the issues that GuardRails identified

  2. Identify the affected block devices:

    ebs_block_device = [{
    device_name = "/dev/sdf"
    volume_type = "gp2"
    volume_size = 100
    encrypted = false
    }]
  3. Modify the encrypted argument to true

  4. Test it

  5. Ship it ๐Ÿšข and relax ๐ŸŒด

Option B: Ensure Azure disks are encrypted at restโ€‹

Azure offers managed disks to store data. These disks can be encrypted to provide enhanced security.

  1. Go through the issues that GuardRails identified

  2. Identify the affected azurerm_managed_disk resources

    resource "azurerm_managed_disk" "example" {
    name = "acctestmd"
    location = "West US 2"
    resource_group_name = azurerm_resource_group.example.name
    storage_account_type = "Standard_LRS"
    create_option = "Empty"
    disk_size_gb = "1"
    }
  3. Ensure that the encryption_settings are configured properly

    resource "azurerm_managed_disk" "example" {
    name = "acctestmd"
    location = "West US 2"
    resource_group_name = azurerm_resource_group.example.name
    storage_account_type = "Standard_LRS"
    create_option = "Empty"
    disk_size_gb = "1"
    encryption_settings = "enabled"
    }

Option C: Ensure Azure Data Lakes are encrypted at restโ€‹

Azure offers managed data lakes to store data, which support encryption at rest to provide enhanced security.

  1. Go through the issues that GuardRails identified

  2. Identify the affected azurerm_data_lake_store resources

    resource "azurerm_data_lake_store" "example" {
    name = "consumptiondatalake"
    resource_group_name = azurerm_resource_group.example.name
    location = azurerm_resource_group.example.location
    encryption_state = "Disabled"
    }
  3. Ensure that the encryption_state is configured properly

    resource "azurerm_data_lake_store" "example" {
    name = "consumptiondatalake"
    resource_group_name = azurerm_resource_group.example.name
    location = azurerm_resource_group.example.location
    encryption_state = "Enabled"
    encryption_type = "ServiceManaged"
    }

Option D: Ensure GCP disks are encrypted at restโ€‹

GCP offers managed disks to store data. These disks can be encrypted to provide enhanced security.

  1. Go through the issues that GuardRails identified

  2. Identify the affected google_compute_disk resources

    resource "google_compute_disk" "default" {
    name = "test-disk"
    type = "pd-ssd"
    zone = "us-central1-a"
    image = "debian-8-jessie-v20170523"

    }
  3. Ensure that the disk_encryption_key block is configured properly

    resource "google_compute_disk" "default" {
    name = "test-disk"
    type = "pd-ssd"
    zone = "us-central1-a"
    image = "debian-8-jessie-v20170523"
    disk_encryption_key {
    kms_key_self_link = "${var.encryption_key}"
    }
    }

Option E: Ensure GCP buckets are encrypted at restโ€‹

  1. Go through the issues that GuardRails identified

  2. Identify the affected google_storage_bucket resources

    resource "google_storage_bucket" "static-site" {
    name = "image-store.com"
    location = "EU"
    force_destroy = true

    }
  3. Ensure that the encryption block is set and configured properly

    resource "google_storage_bucket" "static-site" {
    name = "image-store.com"
    location = "EU"
    force_destroy = true
    encryption {
    default_kms_key_name = "${google_kms_crypto_key.bucket_key.self_link}"
    }
    }

User Data Contains Encoded Private Keyโ€‹

AWS ECS Launch Configuration User Data should not contain an encoded RSA Private Key. Encoding secrets does not protect them at all, it simply obscures them. Do not rely on obscurity as a technique to provide security.

Option A: Make sure EC2 Launch Configuration User Data does not contain encoded RSA Private Keyโ€‹

If one of the following contains an encoded RSA private key:

  • aws_launch_configuration user_data_base64
  • module user_data_base64

Move it to a location that stores secret values in AWS such as the following:

  • AWS Systems Manager Parameter Store
  • AWS Secrets Manager

Detailed Instructionsโ€‹

  1. Locate one of the following vulnerable patterns:

    Vulnerable resource aws_launch_configuration user_data_base64 contains private key pattern:

    resource "aws_launch_configuration" "positive1" {
    image_id = data.aws_ami.ubuntu.id
    instance_type = "m4.large"
    spot_price = "0.001"
    user_data_base64 = "LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpzb21lS2V5" # someKey

    lifecycle {
    create_before_destroy = true
    }
    }

    Vulnerable module user_data_base64 contains private key pattern:

    module "positive2" {
    source = "terraform-aws-modules/autoscaling/aws"
    version = "1.0.4"

    # Launch configuration
    lc_name = "example-lc"

    image_id = "ami-ebd02392"
    instance_type = "t2.micro"
    security_groups = ["sg-12345678"]
    user_data_base64 = "LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpzb21lS2V5"

    ebs_block_device = [
    {
    device_name = "/dev/xvdz"
    volume_type = "gp2"
    volume_size = "50"
    delete_on_termination = true
    },
    ]

    root_block_device = [
    {
    volume_size = "50"
    volume_type = "gp2"
    },
    ]

    # Auto scaling group
    asg_name = "example-asg"
    vpc_zone_identifier = ["subnet-1235678", "subnet-87654321"]
    health_check_type = "EC2"
    min_size = 0
    max_size = 1
    desired_capacity = 1
    wait_for_capacity_timeout = 0

    tags = [
    {
    key = "Environment"
    value = "dev"
    propagate_at_launch = true
    },
    {
    key = "Project"
    value = "megasecret"
    propagate_at_launch = true
    },
    ]
    }

    Vulnerable module user_data_base64 contains private key pattern:

    module "positive3" {
    source = "terraform-aws-modules/autoscaling/aws"
    version = "1.0.4"

    # Launch configuration
    lc_name = "example-lc"

    image_id = "ami-ebd02392"
    instance_type = "t2.micro"
    security_groups = ["sg-12345678"]
    user_data_base64 = "LS0tLS1CRUdJTiBPUEVOU1NIIFBSSVZBVEUgS0VZIEJMT0NLLS0tLS0="

    ebs_block_device = [
    {
    device_name = "/dev/xvdz"
    volume_type = "gp2"
    volume_size = "50"
    delete_on_termination = true
    },
    ]

    root_block_device = [
    {
    volume_size = "50"
    volume_type = "gp2"
    },
    ]

    # Auto scaling group
    asg_name = "example-asg"
    vpc_zone_identifier = ["subnet-1235678", "subnet-87654321"]
    health_check_type = "EC2"
    min_size = 0
    max_size = 1
    desired_capacity = 1
    wait_for_capacity_timeout = 0

    tags = [
    {
    key = "Environment"
    value = "dev"
    propagate_at_launch = true
    },
    {
    key = "Project"
    value = "megasecret"
    propagate_at_launch = true
    },
    ]
    }
  2. Modify the config to something like the following:

    Replacement resource aws_launch_configuration with no user_data_base64 defined pattern:

    resource "aws_launch_configuration" "negative1" {
    image_id = data.aws_ami.ubuntu.id
    instance_type = "m4.large"
    spot_price = "0.001"

    lifecycle {
    create_before_destroy = true
    }
    }

    Replacement resource aws_launch_configuration with empty user_data_base64 pattern:

    resource "aws_launch_configuration" "negative2" {
    image_id = data.aws_ami.ubuntu.id
    instance_type = "m4.large"
    spot_price = "0.001"
    user_data_base64 = ""

    lifecycle {
    create_before_destroy = true
    }
    }

    Replacement resource aws_launch_configuration user_data_base64 containing non-vulnerable pattern:

    resource "aws_launch_configuration" "negative3" {
    image_id = data.aws_ami.ubuntu.id
    instance_type = "m4.large"
    spot_price = "0.001"
    user_data_base64 = "dGVzdA=="

    lifecycle {
    create_before_destroy = true
    }
    }

    Replacement resource aws_launch_configuration user_data_base64 containing null pattern:

    resource "aws_launch_configuration" "negative4" {
    image_id = data.aws_ami.ubuntu.id
    instance_type = "m4.large"
    spot_price = "0.001"
    user_data_base64 = null

    lifecycle {
    create_before_destroy = true
    }
    }

    Replacement module with no user_data_base64 defined pattern:

    module "asg" {
    source = "terraform-aws-modules/autoscaling/aws"
    version = "1.0.4"

    # Launch configuration
    lc_name = "example-lc"

    image_id = "ami-ebd02392"
    instance_type = "t2.micro"
    security_groups = ["sg-12345678"]

    ebs_block_device = [
    {
    device_name = "/dev/xvdz"
    volume_type = "gp2"
    volume_size = "50"
    delete_on_termination = true
    },
    ]

    root_block_device = [
    {
    volume_size = "50"
    volume_type = "gp2"
    },
    ]

    # Auto scaling group
    asg_name = "example-asg"
    vpc_zone_identifier = ["subnet-1235678", "subnet-87654321"]
    health_check_type = "EC2"
    min_size = 0
    max_size = 1
    desired_capacity = 1
    wait_for_capacity_timeout = 0

    tags = [
    {
    key = "Environment"
    value = "dev"
    propagate_at_launch = true
    },
    {
    key = "Project"
    value = "megasecret"
    propagate_at_launch = true
    },
    ]
    }

    Replacement module with empty user_data_base64 pattern:

    module "asg" {
    source = "terraform-aws-modules/autoscaling/aws"
    version = "1.0.4"

    # Launch configuration
    lc_name = "example-lc"

    image_id = "ami-ebd02392"
    instance_type = "t2.micro"
    security_groups = ["sg-12345678"]
    user_data_base64 = ""

    ebs_block_device = [
    {
    device_name = "/dev/xvdz"
    volume_type = "gp2"
    volume_size = "50"
    delete_on_termination = true
    },
    ]

    root_block_device = [
    {
    volume_size = "50"
    volume_type = "gp2"
    },
    ]

    # Auto scaling group
    asg_name = "example-asg"
    vpc_zone_identifier = ["subnet-1235678", "subnet-87654321"]
    health_check_type = "EC2"
    min_size = 0
    max_size = 1
    desired_capacity = 1
    wait_for_capacity_timeout = 0

    tags = [
    {
    key = "Environment"
    value = "dev"
    propagate_at_launch = true
    },
    {
    key = "Project"
    value = "megasecret"
    propagate_at_launch = true
    },
    ]
    }

    Replacement module user_data_base64 containing non-vulnerable pattern:

    module "asg" {
    source = "terraform-aws-modules/autoscaling/aws"
    version = "1.0.4"

    # Launch configuration
    lc_name = "example-lc"

    image_id = "ami-ebd02392"
    instance_type = "t2.micro"
    security_groups = ["sg-12345678"]
    user_data_base64 = "dGVzdA=="

    ebs_block_device = [
    {
    device_name = "/dev/xvdz"
    volume_type = "gp2"
    volume_size = "50"
    delete_on_termination = true
    },
    ]

    root_block_device = [
    {
    volume_size = "50"
    volume_type = "gp2"
    },
    ]

    # Auto scaling group
    asg_name = "example-asg"
    vpc_zone_identifier = ["subnet-1235678", "subnet-87654321"]
    health_check_type = "EC2"
    min_size = 0
    max_size = 1
    desired_capacity = 1
    wait_for_capacity_timeout = 0

    tags = [
    {
    key = "Environment"
    value = "dev"
    propagate_at_launch = true
    },
    {
    key = "Project"
    value = "megasecret"
    propagate_at_launch = true
    },
    ]
    }

    Replacement module user_data_base64 containing null pattern:

    module "asg" {
    source = "terraform-aws-modules/autoscaling/aws"
    version = "1.0.4"

    # Launch configuration
    lc_name = "example-lc"

    image_id = "ami-ebd02392"
    instance_type = "t2.micro"
    security_groups = ["sg-12345678"]
    user_data_base64 = null

    ebs_block_device = [
    {
    device_name = "/dev/xvdz"
    volume_type = "gp2"
    volume_size = "50"
    delete_on_termination = true
    },
    ]

    root_block_device = [
    {
    volume_size = "50"
    volume_type = "gp2"
    },
    ]

    # Auto scaling group
    asg_name = "example-asg"
    vpc_zone_identifier = ["subnet-1235678", "subnet-87654321"]
    health_check_type = "EC2"
    min_size = 0
    max_size = 1
    desired_capacity = 1
    wait_for_capacity_timeout = 0

    tags = [
    {
    key = "Environment"
    value = "dev"
    propagate_at_launch = true
    },
    {
    key = "Project"
    value = "megasecret"
    propagate_at_launch = true
    },
    ]
    }
  3. Test it

  4. Ship it ๐Ÿšข and relax ๐ŸŒด

References:โ€‹

More information:โ€‹