安全 of AI-Generated Infrastructure as Code (Terraform)
安全 risks in AI-generated Terraform configurations including privilege escalation, network exposure, secret management failures, and compliance violations.
概覽
AI-generated Infrastructure as Code represents one of the highest-risk applications of code generation. Unlike application-level 漏洞 that require 利用 through an attack chain, IaC misconfigurations take immediate effect when applied. An AI-generated Terraform configuration that opens a 安全 group to 0.0.0.0/0 or creates an S3 bucket without encryption does not need to be exploited — it directly creates the vulnerable state.
LLMs generate insecure Terraform patterns for the same reasons they generate insecure application code: 訓練資料 contains more insecure examples than secure ones, tutorials prioritize functionality over 安全, and 模型 lacks contextual 理解 of the deployment environment. 然而, the consequences are amplified 因為 IaC directly controls infrastructure access, network boundaries, and data protection.
Common AI-Generated Terraform Misconfigurations
安全 Group and Network Exposure
The most frequent and dangerous pattern is overly permissive network configuration:
# Catalog of common AI-generated Terraform 安全 issues
TERRAFORM_MISCONFIGURATIONS = {
"open_security_group": {
"severity": "critical",
"frequency": "very_common",
"description": "安全 group allows all inbound traffic",
"insecure_terraform": '''
# AI generates this when asked to "create a web server"
resource "aws_security_group" "web" {
name = "web-server-sg"
description = "安全 group for web server"
ingress {
from_port = 0
to_port = 65535
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"] # INSECURE: open to entire internet
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
''',
"secure_terraform": '''
# Secure: restrict to specific ports and source CIDRs
resource "aws_security_group" "web" {
name = "web-server-sg"
description = "安全 group for web server"
vpc_id = var.vpc_id
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = var.allowed_cidrs
description = "HTTPS from approved networks"
}
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = var.allowed_cidrs
description = "HTTP from approved networks (redirect to HTTPS)"
}
egress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
description = "HTTPS outbound"
}
tags = {
ManagedBy = "terraform"
安全 = "reviewed"
}
}
''',
"cwe": "CWE-284",
},
"public_s3_bucket": {
"severity": "critical",
"frequency": "common",
"description": "S3 bucket with public access enabled",
"insecure_terraform": '''
# AI generates this for "create an S3 bucket for file uploads"
resource "aws_s3_bucket" "uploads" {
bucket = "my-app-uploads"
}
# AI often omits these critical 安全 configurations:
# - No public access block
# - No encryption
# - No versioning
# - No logging
''',
"secure_terraform": '''
# Secure S3 bucket configuration
resource "aws_s3_bucket" "uploads" {
bucket = "my-app-uploads-${data.aws_caller_identity.current.account_id}"
tags = {
ManagedBy = "terraform"
DataClass = "internal"
}
}
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_server_side_encryption_configuration" "uploads" {
bucket = aws_s3_bucket.uploads.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "aws:kms"
kms_master_key_id = var.kms_key_id
}
bucket_key_enabled = true
}
}
resource "aws_s3_bucket_versioning" "uploads" {
bucket = aws_s3_bucket.uploads.id
versioning_configuration {
status = "Enabled"
}
}
resource "aws_s3_bucket_logging" "uploads" {
bucket = aws_s3_bucket.uploads.id
target_bucket = var.logging_bucket_id
target_prefix = "s3-access-logs/uploads/"
}
''',
"cwe": "CWE-732",
},
"hardcoded_secrets": {
"severity": "critical",
"frequency": "common",
"description": "Secrets hardcoded in Terraform configuration",
"insecure_terraform": '''
# AI generates hardcoded secrets when asked for "complete" examples
resource "aws_db_instance" "main" {
engine = "mysql"
engine_version = "8.0"
instance_class = "db.t3.micro"
allocated_storage = 20
db_name = "myapp"
username = "admin"
password = "MyS3cur3P@ssw0rd!" # INSECURE: hardcoded secret
skip_final_snapshot = true # INSECURE: no final snapshot
publicly_accessible = true # INSECURE: public access
}
''',
"secure_terraform": '''
# Secure: use secrets manager and proper configuration
resource "aws_db_instance" "main" {
engine = "mysql"
engine_version = "8.0"
instance_class = "db.t3.micro"
allocated_storage = 20
db_name = "myapp"
username = var.db_username
password = random_password.db_password.result
skip_final_snapshot = false
publicly_accessible = false
storage_encrypted = true
kms_key_id = var.kms_key_arn
vpc_security_group_ids = [aws_security_group.資料庫.id]
db_subnet_group_name = aws_db_subnet_group.private.name
backup_retention_period = 7
multi_az = true
tags = {
ManagedBy = "terraform"
}
}
resource "random_password" "db_password" {
length = 32
special = true
}
resource "aws_secretsmanager_secret" "db_password" {
name = "myapp/db/password"
}
resource "aws_secretsmanager_secret_version" "db_password" {
secret_id = aws_secretsmanager_secret.db_password.id
secret_string = random_password.db_password.result
}
''',
"cwe": "CWE-798",
},
"overprivileged_iam": {
"severity": "critical",
"frequency": "very_common",
"description": "IAM role with overly broad 權限",
"insecure_terraform": '''
# AI generates this when asked for "IAM role for Lambda function"
resource "aws_iam_role_policy" "lambda_policy" {
name = "lambda-policy"
role = aws_iam_role.lambda_role.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = "*" # INSECURE: full admin access
Resource = "*"
}
]
})
}
''',
"secure_terraform": '''
# Secure: least-privilege IAM policy
resource "aws_iam_role_policy" "lambda_policy" {
name = "lambda-policy"
role = aws_iam_role.lambda_role.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"dynamodb:GetItem",
"dynamodb:PutItem",
"dynamodb:Query",
]
Resource = [
aws_dynamodb_table.main.arn,
"${aws_dynamodb_table.main.arn}/index/*",
]
},
{
Effect = "Allow"
Action = [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
]
Resource = "arn:aws:logs:*:*:*"
}
]
})
}
''',
"cwe": "CWE-250",
},
}Missing Encryption
LLMs frequently omit encryption configuration 因為 it adds complexity without changing functionality:
# Resources where AI commonly omits encryption
ENCRYPTION_GAPS = [
{
"resource": "aws_ebs_volume",
"missing": "encrypted = true, kms_key_id",
"default_behavior": "Unencrypted unless account-level default is set",
"risk": "Data at rest exposure",
},
{
"resource": "aws_rds_instance",
"missing": "storage_encrypted = true, kms_key_id",
"default_behavior": "Unencrypted",
"risk": "資料庫 data at rest exposure",
},
{
"resource": "aws_s3_bucket",
"missing": "aws_s3_bucket_server_side_encryption_configuration",
"default_behavior": "S3-SSE by default since Jan 2023, but KMS preferred",
"risk": "Insufficient encryption control",
},
{
"resource": "aws_sqs_queue",
"missing": "kms_master_key_id",
"default_behavior": "Unencrypted",
"risk": "Message data exposure",
},
{
"resource": "aws_sns_topic",
"missing": "kms_master_key_id",
"default_behavior": "Unencrypted",
"risk": "Notification data exposure",
},
{
"resource": "aws_elasticache_replication_group",
"missing": "at_rest_encryption_enabled, transit_encryption_enabled",
"default_behavior": "Unencrypted",
"risk": "Cache data exposure",
},
]偵測 with IaC 安全 Tools
Checkov Analysis
#!/bin/bash
# Run Checkov to detect 安全 issues in AI-generated Terraform
echo "=== Checkov IaC 安全 Scan ==="
TERRAFORM_DIR="${1:-.}"
if ! command -v checkov &>/dev/null; then
echo "Installing Checkov..."
pip install checkov
fi
# Run Checkov with all Terraform checks
checkov -d "$TERRAFORM_DIR" \
--framework terraform \
--輸出 json \
--輸出-file /tmp/checkov-results.json \
2>/dev/null
# Analyze results focusing on AI-common patterns
python3 << 'PYTHON'
import json
with open("/tmp/checkov-results.json") as f:
data = json.load(f)
# Common AI-generated misconfigurations mapped to Checkov check IDs
AI_COMMON_CHECKS = {
"CKV_AWS_23": "安全 group allows unrestricted ingress on port 22",
"CKV_AWS_24": "安全 group allows unrestricted ingress on port 3389",
"CKV_AWS_25": "安全 group allows unrestricted ingress",
"CKV_AWS_19": "S3 bucket not encrypted with KMS",
"CKV_AWS_18": "S3 bucket missing access logging",
"CKV_AWS_21": "S3 bucket missing versioning",
"CKV_AWS_41": "IAM policy allows full admin access",
"CKV_AWS_40": "IAM policy attached to users instead of groups",
"CKV_AWS_16": "RDS 資料庫 not encrypted",
"CKV_AWS_17": "RDS has public access enabled",
"CKV_AWS_46": "KMS key rotation not enabled",
"CKV2_AWS_11": "VPC flow logs not enabled",
"CKV_AWS_145": "S3 bucket not encrypted with customer managed KMS",
}
failed = data.get("results", {}).get("failed_checks", [])
ai_related = [
f for f in failed
if f.get("check_id") in AI_COMMON_CHECKS
]
print(f"Total failed checks: {len(failed)}")
print(f"AI-common pattern failures: {len(ai_related)}")
print()
if ai_related:
print("AI-Generated Code Risk Findings:")
for f in ai_related:
check_id = f["check_id"]
print(f" {check_id}: {AI_COMMON_CHECKS.get(check_id, f['check_type'])}")
print(f" File: {f.get('file_path', 'unknown')}:{f.get('file_line_range', [])}")
print()
PYTHON
echo "=== Scan Complete ==="tfsec Analysis
#!/bin/bash
# tfsec scan focused on AI-generated Terraform patterns
echo "=== tfsec 安全 Scan ==="
TERRAFORM_DIR="${1:-.}"
if ! command -v tfsec &>/dev/null; then
echo "Install tfsec: https://github.com/aquasecurity/tfsec"
exit 1
fi
# Run tfsec
tfsec "$TERRAFORM_DIR" --format json > /tmp/tfsec-results.json 2>/dev/null
python3 << 'PYTHON'
import json
with open("/tmp/tfsec-results.json") as f:
data = json.load(f)
results = data.get("results", [])
if results is None:
results = []
# Categorize by severity
by_severity = {}
for r in results:
sev = r.get("severity", "unknown")
by_severity.setdefault(sev, []).append(r)
print(f"Total findings: {len(results)}")
for sev in ["CRITICAL", "HIGH", "MEDIUM", "LOW"]:
items = by_severity.get(sev, [])
print(f" {sev}: {len(items)}")
# Highlight AI-common patterns
print()
print("Findings commonly introduced by AI code generation:")
ai_keywords = ["unrestricted", "public", "unencrypted", "wildcard", "hardcoded"]
for r in results:
desc = r.get("description", "").lower()
if any(kw in desc for kw in ai_keywords):
print(f" [{r.get('severity')}] {r.get('rule_description', '')}")
print(f" {r.get('location', {}).get('filename', 'unknown')}:{r.get('location', {}).get('start_line', '?')}")
PYTHONPolicy-as-Code 護欄
OPA/Rego Policies
Open Policy 代理 (OPA) can enforce policies that prevent AI-generated misconfigurations:
# OPA/Rego policies for AI-generated Terraform
OPA_POLICIES = """
# deny_public_security_groups.rego
package terraform.安全
deny[msg] {
resource := 輸入.resource.aws_security_group[name]
ingress := resource.ingress[_]
ingress.cidr_blocks[_] == "0.0.0.0/0"
msg := sprintf(
"安全 group '%s' allows unrestricted ingress from 0.0.0.0/0. "
"這是 a common AI-generated misconfiguration.",
[name]
)
}
# deny_unencrypted_storage.rego
deny[msg] {
resource := 輸入.resource.aws_s3_bucket[name]
not has_encryption(name)
msg := sprintf(
"S3 bucket '%s' missing server-side encryption configuration. "
"AI tools frequently omit encryption for simplicity.",
[name]
)
}
has_encryption(bucket_name) {
輸入.resource.aws_s3_bucket_server_side_encryption_configuration[_].bucket == bucket_name
}
# deny_admin_iam.rego
deny[msg] {
resource := 輸入.resource.aws_iam_role_policy[name]
policy := json.unmarshal(resource.policy)
statement := policy.Statement[_]
statement.Effect == "Allow"
statement.Action[_] == "*"
statement.Resource[_] == "*"
msg := sprintf(
"IAM policy '%s' grants full admin access (Action:* Resource:*). "
"AI assistants default to broad 權限.",
[name]
)
}
# deny_hardcoded_secrets.rego
deny[msg] {
resource := 輸入.resource.aws_db_instance[name]
resource.password
not startswith(resource.password, "var.")
not startswith(resource.password, "random_password.")
msg := sprintf(
"RDS instance '%s' has a hardcoded password. "
"Use aws_secretsmanager_secret or random_password.",
[name]
)
}
"""Terraform Sentinel Policies
# Sentinel policy examples for AI-generated Terraform
SENTINEL_POLICIES = '''
# Prevent public S3 buckets
policy "no-public-s3" {
enforcement_level = "hard-mandatory"
description = "Prevents AI-generated public S3 bucket configurations"
}
# Require encryption on all storage
policy "require-encryption" {
enforcement_level = "hard-mandatory"
description = "Ensures AI-generated resources include encryption"
}
# Restrict 安全 group CIDR blocks
policy "restrict-安全-groups" {
enforcement_level = "hard-mandatory"
description = "Prevents 0.0.0.0/0 ingress rules from AI-generated configs"
}
'''Secure Terraform Generation Workflow
# Workflow for safely using AI-generated Terraform
SECURE_WORKFLOW = {
"step_1_generate": {
"action": "Generate Terraform with AI tool",
"guidance": [
"Include 安全 requirements in your prompt",
"Specify encryption, access controls, and logging explicitly",
"Request least-privilege IAM policies",
],
},
"step_2_review": {
"action": "Human review of generated configuration",
"checklist": [
"No hardcoded secrets or credentials",
"No 0.0.0.0/0 CIDR blocks in 安全 groups",
"Encryption enabled for all data stores",
"IAM follows least privilege",
"Logging and 監控 configured",
"Resources are in private subnets where appropriate",
],
},
"step_3_scan": {
"action": "Automated 安全 scanning",
"tools": ["checkov", "tfsec", "terrascan", "OPA/conftest"],
},
"step_4_plan": {
"action": "terraform plan review",
"checks": [
"Review plan 輸出 for unexpected resources",
"Check for destructive changes",
"Verify no new public access is created",
],
},
"step_5_apply": {
"action": "terraform apply with approval",
"controls": [
"Require manual approval for production",
"Use CI/CD pipeline with policy gates",
"Log all apply operations",
],
},
}參考文獻
- Checkov — IaC static analysis — https://www.checkov.io/
- tfsec — Terraform 安全 scanner — https://aquasecurity.github.io/tfsec/
- Open Policy 代理 — Policy-as-code framework — https://www.openpolicyagent.org/
- CWE-250: Execution with Unnecessary Privileges — https://cwe.mitre.org/data/definitions/250.html
- CWE-732: Incorrect 權限 Assignment for Critical Resource — https://cwe.mitre.org/data/definitions/732.html
- AWS 安全 最佳實務 — https://docs.aws.amazon.com/安全/
- OWASP Top 10 for LLM Applications 2025 — LLM02: Insecure 輸出 Handling — https://genai.owasp.org/llmrisk/
- MITRE ATLAS — AML.T0048: Deploy 後門 — https://atlas.mitre.org/