mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-24 21:11:53 +00:00
feat(acm): Add new check for insecure algorithms in certificates (#4551)
Co-authored-by: Sergio Garcia <38561120+sergargar@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
80e13bffa2
commit
1b18aef0f0
@@ -271,6 +271,11 @@ aws:
|
||||
# AWS ACM Configuration
|
||||
# aws.acm_certificates_expiration_check
|
||||
days_to_expire_threshold: 7
|
||||
# aws.acm_certificates_rsa_key_length
|
||||
insecure_key_algorithms:
|
||||
[
|
||||
"RSA-1024",
|
||||
]
|
||||
|
||||
# AWS EKS Configuration
|
||||
# aws.eks_control_plane_logging_all_types_enabled
|
||||
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"Provider": "aws",
|
||||
"CheckID": "acm_certificates_with_secure_key_algorithms",
|
||||
"CheckTitle": "Check if ACM Certificates use a secure key algorithm",
|
||||
"CheckType": [
|
||||
"Data Protection"
|
||||
],
|
||||
"ServiceName": "acm",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "arn:partition:acm:region:account-id:certificate/resource-id",
|
||||
"Severity": "high",
|
||||
"ResourceType": "AwsCertificateManagerCertificate",
|
||||
"Description": "Check if ACM Certificates use a secure key algorithm (RSA 2048 bits or more, or ECDSA 256 bits or more). For example certificates that use RSA-1024 can be compromised because the encryption could be broken in no more than 2^80 guesses making it vulnerable to a factorization attack.",
|
||||
"Risk": "Certificates with weak RSA or ECDSA keys can be compromised because the length of the key defines the security of the encryption. The number of bits in the key determines the number of guesses an attacker would have to make in order to decrypt the data. The more bits in the key, the more secure the encryption.",
|
||||
"RelatedUrl": "https://docs.aws.amazon.com/acm/latest/userguide/acm-certificate.html",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "",
|
||||
"NativeIaC": "",
|
||||
"Other": "",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Ensure that all ACM certificates use a secure key algorithm. If any certificates use smaller keys, regenerate them with a secure key size and update any systems that rely on these certificates.",
|
||||
"Url": "https://docs.aws.amazon.com/securityhub/latest/userguide/acm-controls.html#acm-2"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": ""
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
from prowler.lib.check.models import Check, Check_Report_AWS
|
||||
from prowler.providers.aws.services.acm.acm_client import acm_client
|
||||
|
||||
|
||||
class acm_certificates_with_secure_key_algorithms(Check):
|
||||
def execute(self):
|
||||
findings = []
|
||||
for certificate in acm_client.certificates:
|
||||
if certificate.in_use or acm_client.provider.scan_unused_services:
|
||||
report = Check_Report_AWS(self.metadata())
|
||||
report.region = certificate.region
|
||||
report.resource_id = certificate.id
|
||||
report.resource_details = certificate.name
|
||||
report.resource_arn = certificate.arn
|
||||
report.resource_tags = certificate.tags
|
||||
|
||||
report.status = "PASS"
|
||||
report.status_extended = f"ACM Certificate {certificate.id} for {certificate.name} uses a secure key algorithm ({certificate.key_algorithm})."
|
||||
if certificate.key_algorithm in acm_client.audit_config.get(
|
||||
"insecure_algorithms", ["RSA-1024"]
|
||||
):
|
||||
report.status = "FAIL"
|
||||
report.status_extended = f"ACM Certificate {certificate.id} for {certificate.name} does not use a secure key algorithm ({certificate.key_algorithm})."
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
@@ -14,17 +14,28 @@ class ACM(AWSService):
|
||||
# Call AWSService's __init__
|
||||
super().__init__(__class__.__name__, provider)
|
||||
self.certificates = []
|
||||
self.__threading_call__(self.__list_certificates__)
|
||||
self.__describe_certificates__()
|
||||
self.__list_tags_for_certificate__()
|
||||
self.__threading_call__(self._list_certificates)
|
||||
self._describe_certificates()
|
||||
self._list_tags_for_certificate()
|
||||
|
||||
def __list_certificates__(self, regional_client):
|
||||
def _list_certificates(self, regional_client):
|
||||
logger.info("ACM - Listing Certificates...")
|
||||
try:
|
||||
includes = {
|
||||
"keyTypes": [
|
||||
"RSA_1024",
|
||||
"RSA_2048",
|
||||
"RSA_3072",
|
||||
"RSA_4096",
|
||||
"EC_prime256v1",
|
||||
"EC_secp384r1",
|
||||
"EC_secp521r1",
|
||||
]
|
||||
}
|
||||
list_certificates_paginator = regional_client.get_paginator(
|
||||
"list_certificates"
|
||||
)
|
||||
for page in list_certificates_paginator.paginate():
|
||||
for page in list_certificates_paginator.paginate(Includes=includes):
|
||||
for certificate in page["CertificateSummaryList"]:
|
||||
if not self.audit_resources or (
|
||||
is_resource_filtered(
|
||||
@@ -49,6 +60,7 @@ class ACM(AWSService):
|
||||
name=certificate["DomainName"],
|
||||
id=certificate["CertificateArn"].split("/")[-1],
|
||||
type=certificate["Type"],
|
||||
key_algorithm=certificate["KeyAlgorithm"],
|
||||
expiration_days=certificate_expiration_time,
|
||||
in_use=certificate.get("InUse", False),
|
||||
transparency_logging=False,
|
||||
@@ -60,7 +72,7 @@ class ACM(AWSService):
|
||||
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
|
||||
def __describe_certificates__(self):
|
||||
def _describe_certificates(self):
|
||||
logger.info("ACM - Describing Certificates...")
|
||||
try:
|
||||
for certificate in self.certificates:
|
||||
@@ -78,7 +90,7 @@ class ACM(AWSService):
|
||||
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
|
||||
def __list_tags_for_certificate__(self):
|
||||
def _list_tags_for_certificate(self):
|
||||
logger.info("ACM - List Tags...")
|
||||
try:
|
||||
for certificate in self.certificates:
|
||||
@@ -98,6 +110,7 @@ class Certificate(BaseModel):
|
||||
name: str
|
||||
id: str
|
||||
type: str
|
||||
key_algorithm: str
|
||||
tags: Optional[list] = []
|
||||
expiration_days: int
|
||||
in_use: bool
|
||||
|
||||
@@ -262,6 +262,9 @@ config_aws = {
|
||||
],
|
||||
"check_rds_instance_replicas": False,
|
||||
"days_to_expire_threshold": 7,
|
||||
"insecure_key_algorithms": [
|
||||
"RSA-1024",
|
||||
],
|
||||
"eks_required_log_types": [
|
||||
"api",
|
||||
"audit",
|
||||
|
||||
@@ -271,6 +271,11 @@ aws:
|
||||
# AWS ACM Configuration
|
||||
# aws.acm_certificates_expiration_check
|
||||
days_to_expire_threshold: 7
|
||||
# aws.acm_certificates_rsa_key_length
|
||||
insecure_key_algorithms:
|
||||
[
|
||||
"RSA-1024",
|
||||
]
|
||||
|
||||
# AWS EKS Configuration
|
||||
# aws.eks_control_plane_logging_all_types_enabled
|
||||
|
||||
+10
@@ -32,6 +32,7 @@ class Test_acm_certificates_expiration_check:
|
||||
certificate_arn = f"arn:aws:acm:{AWS_REGION}:{AWS_ACCOUNT_NUMBER}:certificate/{certificate_id}"
|
||||
certificate_name = "test-certificate.com"
|
||||
certificate_type = "AMAZON_ISSUED"
|
||||
certificate_key_algorithm = "RSA_2048"
|
||||
expiration_days = 5
|
||||
in_use = True
|
||||
|
||||
@@ -42,6 +43,7 @@ class Test_acm_certificates_expiration_check:
|
||||
id=certificate_id,
|
||||
name=certificate_name,
|
||||
type=certificate_type,
|
||||
key_algorithm=certificate_key_algorithm,
|
||||
expiration_days=expiration_days,
|
||||
in_use=in_use,
|
||||
transparency_logging=True,
|
||||
@@ -80,6 +82,7 @@ class Test_acm_certificates_expiration_check:
|
||||
certificate_arn = f"arn:aws:acm:{AWS_REGION}:{AWS_ACCOUNT_NUMBER}:certificate/{certificate_id}"
|
||||
certificate_name = "test-certificate.com"
|
||||
certificate_type = "AMAZON_ISSUED"
|
||||
certificate_key_algorithm = "RSA_2048"
|
||||
expiration_days = -400
|
||||
in_use = True
|
||||
|
||||
@@ -90,6 +93,7 @@ class Test_acm_certificates_expiration_check:
|
||||
id=certificate_id,
|
||||
name=certificate_name,
|
||||
type=certificate_type,
|
||||
key_algorithm=certificate_key_algorithm,
|
||||
expiration_days=expiration_days,
|
||||
in_use=in_use,
|
||||
transparency_logging=True,
|
||||
@@ -127,6 +131,7 @@ class Test_acm_certificates_expiration_check:
|
||||
certificate_arn = f"arn:aws:acm:{AWS_REGION}:{AWS_ACCOUNT_NUMBER}:certificate/{certificate_id}"
|
||||
certificate_name = "test-certificate.com"
|
||||
certificate_type = "AMAZON_ISSUED"
|
||||
certificate_key_algorithm = "RSA_2048"
|
||||
expiration_days = 365
|
||||
in_use = True
|
||||
|
||||
@@ -137,6 +142,7 @@ class Test_acm_certificates_expiration_check:
|
||||
id=certificate_id,
|
||||
name=certificate_name,
|
||||
type=certificate_type,
|
||||
key_algorithm=certificate_key_algorithm,
|
||||
expiration_days=expiration_days,
|
||||
in_use=in_use,
|
||||
transparency_logging=True,
|
||||
@@ -173,6 +179,7 @@ class Test_acm_certificates_expiration_check:
|
||||
certificate_arn = f"arn:aws:acm:{AWS_REGION}:{AWS_ACCOUNT_NUMBER}:certificate/{certificate_id}"
|
||||
certificate_name = "test-certificate.com"
|
||||
certificate_type = "AMAZON_ISSUED"
|
||||
certificate_key_algorithm = "RSA_2048"
|
||||
expiration_days = 365
|
||||
in_use = False
|
||||
|
||||
@@ -183,6 +190,7 @@ class Test_acm_certificates_expiration_check:
|
||||
id=certificate_id,
|
||||
name=certificate_name,
|
||||
type=certificate_type,
|
||||
key_algorithm=certificate_key_algorithm,
|
||||
expiration_days=expiration_days,
|
||||
in_use=in_use,
|
||||
transparency_logging=True,
|
||||
@@ -212,6 +220,7 @@ class Test_acm_certificates_expiration_check:
|
||||
certificate_arn = f"arn:aws:acm:{AWS_REGION}:{AWS_ACCOUNT_NUMBER}:certificate/{certificate_id}"
|
||||
certificate_name = "test-certificate.com"
|
||||
certificate_type = "AMAZON_ISSUED"
|
||||
certificate_key_algorithm = "RSA_2048"
|
||||
expiration_days = -400
|
||||
in_use = False
|
||||
|
||||
@@ -222,6 +231,7 @@ class Test_acm_certificates_expiration_check:
|
||||
id=certificate_id,
|
||||
name=certificate_name,
|
||||
type=certificate_type,
|
||||
key_algorithm=certificate_key_algorithm,
|
||||
expiration_days=expiration_days,
|
||||
in_use=in_use,
|
||||
transparency_logging=True,
|
||||
|
||||
+6
@@ -31,6 +31,7 @@ class Test_acm_certificates_transparency_logs_enabled:
|
||||
certificate_arn = f"arn:aws:acm:{AWS_REGION}:{AWS_ACCOUNT_NUMBER}:certificate/{certificate_id}"
|
||||
certificate_name = "test-certificate.com"
|
||||
certificate_type = "AMAZON_ISSUED"
|
||||
certificate_key_algorithm = "RSA_2048"
|
||||
|
||||
acm_client = mock.MagicMock
|
||||
acm_client.certificates = [
|
||||
@@ -39,6 +40,7 @@ class Test_acm_certificates_transparency_logs_enabled:
|
||||
id=certificate_id,
|
||||
name=certificate_name,
|
||||
type=certificate_type,
|
||||
key_algorithm=certificate_key_algorithm,
|
||||
expiration_days=365,
|
||||
transparency_logging=True,
|
||||
in_use=True,
|
||||
@@ -74,6 +76,7 @@ class Test_acm_certificates_transparency_logs_enabled:
|
||||
certificate_arn = f"arn:aws:acm:{AWS_REGION}:{AWS_ACCOUNT_NUMBER}:certificate/{certificate_id}"
|
||||
certificate_name = "test-certificate.com"
|
||||
certificate_type = "AMAZON_ISSUED"
|
||||
certificate_key_algorithm = "RSA_2048"
|
||||
|
||||
acm_client = mock.MagicMock
|
||||
acm_client.certificates = [
|
||||
@@ -82,6 +85,7 @@ class Test_acm_certificates_transparency_logs_enabled:
|
||||
id=certificate_id,
|
||||
name=certificate_name,
|
||||
type=certificate_type,
|
||||
key_algorithm=certificate_key_algorithm,
|
||||
expiration_days=365,
|
||||
transparency_logging=False,
|
||||
in_use=True,
|
||||
@@ -116,6 +120,7 @@ class Test_acm_certificates_transparency_logs_enabled:
|
||||
certificate_id = str(uuid.uuid4())
|
||||
certificate_arn = f"arn:aws:acm:{AWS_REGION}:{AWS_ACCOUNT_NUMBER}:certificate/{certificate_id}"
|
||||
certificate_name = "test-certificate.com"
|
||||
certificate_key_algorithm = "RSA_2048"
|
||||
certificate_type = "IMPORTED"
|
||||
|
||||
acm_client = mock.MagicMock
|
||||
@@ -125,6 +130,7 @@ class Test_acm_certificates_transparency_logs_enabled:
|
||||
id=certificate_id,
|
||||
name=certificate_name,
|
||||
type=certificate_type,
|
||||
key_algorithm=certificate_key_algorithm,
|
||||
expiration_days=365,
|
||||
transparency_logging=True,
|
||||
in_use=True,
|
||||
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
import uuid
|
||||
from unittest import mock
|
||||
|
||||
from prowler.providers.aws.services.acm.acm_service import Certificate
|
||||
|
||||
AWS_REGION = "us-east-1"
|
||||
AWS_ACCOUNT_NUMBER = "123456789012"
|
||||
|
||||
|
||||
class Test_acm_certificates_with_secure_key_algorithms:
|
||||
def test_no_acm_certificates(self):
|
||||
acm_client = mock.MagicMock
|
||||
acm_client.certificates = []
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.aws.services.acm.acm_service.ACM",
|
||||
new=acm_client,
|
||||
):
|
||||
# Test Check
|
||||
from prowler.providers.aws.services.acm.acm_certificates_with_secure_key_algorithms.acm_certificates_with_secure_key_algorithms import (
|
||||
acm_certificates_with_secure_key_algorithms,
|
||||
)
|
||||
|
||||
check = acm_certificates_with_secure_key_algorithms()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 0
|
||||
|
||||
def test_acm_certificate_secure_algorithm(self):
|
||||
certificate_id = str(uuid.uuid4())
|
||||
certificate_arn = f"arn:aws:acm:{AWS_REGION}:{AWS_ACCOUNT_NUMBER}:certificate/{certificate_id}"
|
||||
certificate_name = "test-certificate.com"
|
||||
certificate_type = "AMAZON_ISSUED"
|
||||
certificate_key_algorithm = "RSA-2048"
|
||||
|
||||
acm_client = mock.MagicMock
|
||||
acm_client.certificates = [
|
||||
Certificate(
|
||||
arn=certificate_arn,
|
||||
id=certificate_id,
|
||||
name=certificate_name,
|
||||
type=certificate_type,
|
||||
key_algorithm=certificate_key_algorithm,
|
||||
expiration_days=365,
|
||||
transparency_logging=True,
|
||||
in_use=True,
|
||||
region=AWS_REGION,
|
||||
)
|
||||
]
|
||||
|
||||
acm_client.audit_config = {"insecure_algorithm": ["RSA-1024"]}
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.aws.services.acm.acm_service.ACM",
|
||||
new=acm_client,
|
||||
):
|
||||
# Test Check
|
||||
from prowler.providers.aws.services.acm.acm_certificates_with_secure_key_algorithms.acm_certificates_with_secure_key_algorithms import (
|
||||
acm_certificates_with_secure_key_algorithms,
|
||||
)
|
||||
|
||||
check = acm_certificates_with_secure_key_algorithms()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"ACM Certificate {certificate_id} for {certificate_name} uses a secure key algorithm ({certificate_key_algorithm})."
|
||||
)
|
||||
assert result[0].resource_id == certificate_id
|
||||
assert result[0].resource_arn == certificate_arn
|
||||
assert result[0].region == AWS_REGION
|
||||
assert result[0].resource_tags == []
|
||||
|
||||
def test_acm_certificate_insecure_algorithm(self):
|
||||
certificate_id = str(uuid.uuid4())
|
||||
certificate_arn = f"arn:aws:acm:{AWS_REGION}:{AWS_ACCOUNT_NUMBER}:certificate/{certificate_id}"
|
||||
certificate_name = "test-certificate.com"
|
||||
certificate_type = "AMAZON_ISSUED"
|
||||
certificate_key_algorithm = "RSA-1024"
|
||||
|
||||
acm_client = mock.MagicMock
|
||||
acm_client.certificates = [
|
||||
Certificate(
|
||||
arn=certificate_arn,
|
||||
id=certificate_id,
|
||||
name=certificate_name,
|
||||
type=certificate_type,
|
||||
key_algorithm=certificate_key_algorithm,
|
||||
expiration_days=365,
|
||||
transparency_logging=False,
|
||||
in_use=True,
|
||||
region=AWS_REGION,
|
||||
)
|
||||
]
|
||||
|
||||
acm_client.audit_config = {"insecure_algorithm": ["RSA-1024"]}
|
||||
|
||||
with mock.patch(
|
||||
"prowler.providers.aws.services.acm.acm_service.ACM",
|
||||
new=acm_client,
|
||||
):
|
||||
# Test Check
|
||||
from prowler.providers.aws.services.acm.acm_certificates_with_secure_key_algorithms.acm_certificates_with_secure_key_algorithms import (
|
||||
acm_certificates_with_secure_key_algorithms,
|
||||
)
|
||||
|
||||
check = acm_certificates_with_secure_key_algorithms()
|
||||
result = check.execute()
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"ACM Certificate {certificate_id} for {certificate_name} does not use a secure key algorithm ({certificate_key_algorithm})."
|
||||
)
|
||||
assert result[0].resource_id == certificate_id
|
||||
assert result[0].resource_arn == certificate_arn
|
||||
assert result[0].region == AWS_REGION
|
||||
assert result[0].resource_tags == []
|
||||
@@ -18,6 +18,7 @@ make_api_call = botocore.client.BaseClient._make_api_call
|
||||
certificate_arn = f"arn:aws:acm:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:certificate/{str(uuid.uuid4())}"
|
||||
certificate_name = "test-certificate.com"
|
||||
certificate_type = "AMAZON_ISSUED"
|
||||
certificate_key_algorithm = "RSA-4096"
|
||||
|
||||
|
||||
def mock_make_api_call(self, operation_name, kwargs):
|
||||
@@ -40,7 +41,7 @@ def mock_make_api_call(self, operation_name, kwargs):
|
||||
"HasAdditionalSubjectAlternativeNames": False,
|
||||
"Status": "ISSUED",
|
||||
"Type": certificate_type,
|
||||
"KeyAlgorithm": "RSA_4096",
|
||||
"KeyAlgorithm": "RSA-4096",
|
||||
"KeyUsages": ["DIGITAL_SIGNATURE"],
|
||||
"ExtendedKeyUsages": ["TLS_WEB_SERVER_AUTHENTICATION"],
|
||||
"InUse": True,
|
||||
@@ -127,7 +128,7 @@ class Test_ACM_Service:
|
||||
|
||||
# Test ACM List Certificates
|
||||
# @mock_acm
|
||||
def test__list_and_describe_certificates__(self):
|
||||
def test_list_and_describe_certificates(self):
|
||||
# Generate ACM Client
|
||||
# acm_client = client("acm", region_name=AWS_REGION)
|
||||
# Request ACM certificate
|
||||
@@ -142,13 +143,14 @@ class Test_ACM_Service:
|
||||
assert acm.certificates[0].arn == certificate_arn
|
||||
assert acm.certificates[0].name == certificate_name
|
||||
assert acm.certificates[0].type == certificate_type
|
||||
assert acm.certificates[0].key_algorithm == certificate_key_algorithm
|
||||
assert acm.certificates[0].expiration_days == 365
|
||||
assert acm.certificates[0].transparency_logging is False
|
||||
assert acm.certificates[0].region == AWS_REGION_US_EAST_1
|
||||
|
||||
# Test ACM List Tags
|
||||
# @mock_acm
|
||||
def test__list_tags_for_certificate__(self):
|
||||
def test_list_tags_for_certificate(self):
|
||||
# Generate ACM Client
|
||||
# acm_client = client("acm", region_name=AWS_REGION)
|
||||
# Request ACM certificate
|
||||
|
||||
Reference in New Issue
Block a user