AWS WAFでSQLインジェクション対策|設定方法と運用のポイント

AWS WAFを使用したSQLインジェクション対策の完全ガイド。マネージドルール選定、カスタムルール作成、CloudFront/ALB統合、コスト最適化まで実践的に解説。月額$76から始める本格的なクラウドセキュリティ実装方法を、設定例とテンプレート付きで提供。

AWS WAFのSQLインジェクション防御機能

AWS WAF(Web Application Firewall)は、AWSのマネージドサービスとして提供される、クラウドネイティブなWebアプリケーション防御ソリューションです。特にSQLインジェクション対策において、高度な検知機能と柔軟なルール設定により、効果的な防御を実現します。

Core Rule Set(CRS)の概要

AWS WAFの中核となるCore Rule Set(CRS)は、OWASP Top 10に対応した包括的なルールセットです。SQLインジェクション対策として、複数の検知レイヤーを提供します。

SQLインジェクション検知ルール

CRSには、SQLインジェクションを検知するための階層的なルールが含まれています。

ルールID 検知内容 感度レベル 誤検知率 推奨用途
942100 SQLキーワード検出(SELECT、UNION等) 1-4 基本防御
942200 SQL演算子検出(OR、AND、=等) 1-4 標準防御
942300 SQL関数検出(CONCAT、SLEEP等) 2-4 高度な攻撃対策
942400 チェーンSQL検出(複合的な攻撃) 3-4 複雑な攻撃対策
942500 MySQL特有の攻撃パターン 2-4 MySQL環境
942600 PostgreSQL特有の攻撃パターン 2-4 PostgreSQL環境

感度レベルは1(最も緩い)から4(最も厳しい)まで設定可能で、ビジネス要件に応じて調整できます。

Managed Rulesの選択と設定

AWS WAFでは、AWSが管理・更新するマネージドルールを利用することで、専門知識なしに高度な防御を実現できます。

AWS Managed Rules for SQL Database
SQLインジェクション対策に特化したルールセットです。月額$20から利用可能で、データベース種別(MySQL、PostgreSQL、SQL Server、Oracle)に応じた最適化されたルールが含まれています。定期的な更新により、新しい攻撃パターンにも自動対応します。誤検知を最小化するため、機械学習により継続的に改善されています。
感度レベル設定
Low(誤検知最小)は、ビジネスクリティカルなアプリケーション向けで、確実な攻撃のみをブロックします。Medium(バランス型)は、一般的なWebアプリケーションに推奨され、防御力と可用性のバランスを取ります。High(検知率最大)は、セキュリティを最優先する環境向けで、疑わしいリクエストも積極的にブロックします。
除外ルール設定
特定のルールIDを無効化することで、業務に影響する誤検知を削減できます。例えば、CMSの管理画面でHTMLエディタを使用する場合、特定のパスに対してSQL関数検出ルールを無効化するなど、きめ細かな調整が可能です。除外設定は、パス、クエリパラメータ、ヘッダー単位で指定できます。

カスタムルールによる強化

標準的なマネージドルールに加え、自社環境に特化したカスタムルールを作成することで、より精密な防御を実現できます。

{
  "Name": "CustomSQLiProtection",
  "Priority": 100,
  "Statement": {
	"OrStatement": {
	  "Statements": [
		{
		  "SqliMatchStatement": {
			"FieldToMatch": {
			  "QueryString": {}
			},
			"TextTransformations": [
			  {
				"Priority": 0,
				"Type": "URL_DECODE"
			  },
			  {
				"Priority": 1,
				"Type": "HTML_ENTITY_DECODE"
			  },
			  {
				"Priority": 2,
				"Type": "LOWERCASE"
			  }
			]
		  }
		},
		{
		  "SqliMatchStatement": {
			"FieldToMatch": {
			  "Body": {
				"OversizeHandling": "MATCH"
			  }
			},
			"TextTransformations": [
			  {
				"Priority": 0,
				"Type": "BASE64_DECODE"
			  },
			  {
				"Priority": 1,
				"Type": "URL_DECODE"
			  }
			]
		  }
		},
		{
		  "RegexMatchStatement": {
			"RegexString": "(?i)(\\bUNION\\b.*\\bSELECT\\b|\\bOR\\b.*1\\s*=\\s*1|\\bAND\\b.*1\\s*=\\s*1)",
			"FieldToMatch": {
			  "AllQueryArguments": {}
			},
			"TextTransformations": [
			  {
				"Priority": 0,
				"Type": "URL_DECODE"
			  }
			]
		  }
		}
	  ]
	}
  },
  "Action": {
	"Block": {
	  "CustomResponse": {
		"ResponseCode": 403,
		"CustomResponseBodyKey": "blocked-request",
		"ResponseHeaders": [
		  {
			"Name": "X-Block-Reason",
			"Value": "SQL-Injection-Detected"
		  }
		]
	  }
	}
  },
  "VisibilityConfig": {
	"SampledRequestsEnabled": true,
	"CloudWatchMetricsEnabled": true,
	"MetricName": "CustomSQLiProtection"
  }
}

マネージドルールセットの選定と設定

適切なマネージドルールセットの選択は、効果的な防御と運用負荷軽減の鍵となります。

利用可能なルールセット一覧

AWS WAFで利用可能な主要ルールセットと、その特徴を理解することが重要です。

AWS提供のマネージドルール

# AWS Managed Rules 価格表(2025年版)

1. Core Rule Set (CRS)
   料金構成:
   - 基本料金: $5/月
   - リクエスト料金: $1/百万リクエスト
   - Web ACL: $5/月
   特徴:
   - OWASP Top 10完全対応
   - SQLインジェクション基本検知
   - XSS、コマンドインジェクション対応
   - 誤検知率: 低
   
2. SQL Database Rule Set
   料金構成:
   - 基本料金: $20/月
   - リクエスト料金: $0.60/百万リクエスト
   特徴:
   - SQL特化の詳細検知
   - データベース種別対応
   - ブラインドSQLi検知
   - 時間ベース攻撃検知
   
3. Known Bad Inputs Rule Set
   料金構成:
   - 基本料金: $5/月
   - リクエスト料金: $1/百万リクエスト
   特徴:
   - 既知の攻撃パターンDB
   - 週次更新
   - CVEデータベース連携
   - 低誤検知率

4. PHP Application Rule Set
   料金構成:
   - 基本料金: $20/月
   - リクエスト料金: $0.60/百万リクエスト
   特徴:
   - PHPアプリ特有の脆弱性対策
   - LFI/RFI検知
   - PHPインジェクション対策
   
5. Linux Operating System Rule Set
   料金構成:
   - 基本料金: $20/月
   - リクエスト料金: $0.60/百万リクエスト
   特徴:
   - OSコマンドインジェクション対策
   - パストラバーサル検知
   - シェルショック対策

サードパーティ製ルール

AWS Marketplaceで提供される、セキュリティベンダーの高度なルールセットも利用可能です。

プロバイダ 製品名 月額 特徴 推奨環境
F5 Networks F5 WAF Rules $30~ 高精度検知、AI活用 エンタープライズ
Imperva ThreatFeed $50~ リアルタイム脅威情報 Eコマース
Fortinet FortiRules $40~ 機械学習ベース 金融機関
Cyber Security Cloud WafCharm $45~ 自動チューニング 中小企業
Signal Sciences Advanced Rules $60~ DevSecOps統合 SaaS企業

最適な組み合わせパターン

企業規模と要件に応じた、コスト効率の良いルールセット組み合わせを提案します。

スタートアップ向け(月額$25)
Core Rule Set($5)+ Rate Limiting(カスタムルール)+ IP Reputation List($20)の組み合わせで、基本的な攻撃の95%を防御できます。SQLインジェクションの基本パターン、DDoS攻撃、既知の悪意あるIPからのアクセスをブロックします。小規模なWebアプリケーションやAPIには十分な防御力です。
中小企業向け(月額$45)
Core Rule Set($5)+ SQL Database Rule Set($20)+ Geographic Restriction(カスタム)+ Known Bad Inputs($5)+ Rate Limiting(カスタム)の構成で、包括的な防御を実現します。日本国内向けサービスなら、海外からのアクセスを制限することで、攻撃の80%を事前に防げます。
エンタープライズ向け(月額$100+)
全AWS Managed Rules(約$70)+ F5 WAF Rules($30)+ カスタムルール群 + IP Intelligence Feed統合により、最高レベルの防御を構築します。複数のレイヤーでの防御により、ゼロデイ攻撃や高度な回避技術にも対応可能です。SOCとの連携により、24時間体制での監視も実現できます。

カスタムルール作成のベストプラクティス

効果的なカスタムルールは、パフォーマンスと防御力のバランスを考慮して設計する必要があります。

効果的なルール設計

カスタムルールの設計では、処理効率と検知精度の両立が重要です。

正規表現パターンの最適化

# 効率的な正規表現パターン設計例
import re
import time

# パフォーマンス比較用のテストコード
class RegexOptimizer:
	def __init__(self):
		# 悪い例:バックトラックが多く、処理負荷が高い
		self.bad_pattern = r".*(\bSELECT\b.*\bFROM\b.*|\bUNION\b.*\bSELECT\b.*|\bINSERT\b.*\bINTO\b.*)"
		
		# 良い例:アンカーと制限を活用し、効率的
		self.good_pattern = r"(?i)^.{0,100}(SELECT|UNION|INSERT|UPDATE|DELETE|DROP|CREATE|ALTER|EXEC)"
		
		# 最適化版:より具体的なパターン
		self.optimized_pattern = r"(?i)(?:^|\s|[^a-z])(SELECT\s+[\w\*]+\s+FROM|UNION\s+SELECT|INSERT\s+INTO)"
	
	def test_performance(self, test_strings):
		results = {}
		
		for pattern_name, pattern in [
			("悪い例", self.bad_pattern),
			("良い例", self.good_pattern),
			("最適化版", self.optimized_pattern)
		]:
			compiled = re.compile(pattern)
			start_time = time.time()
			
			for test_string in test_strings:
				compiled.search(test_string)
			
			elapsed = time.time() - start_time
			results[pattern_name] = elapsed
		
		return results
	
	def generate_waf_rule(self, pattern):
		"""AWS WAF用のルール生成"""
		return {
			"Name": "OptimizedSQLiDetection",
			"Priority": 100,
			"Statement": {
				"RegexMatchStatement": {
					"RegexString": pattern,
					"FieldToMatch": {
						"AllQueryArguments": {}
					},
					"TextTransformations": [
						{"Priority": 0, "Type": "URL_DECODE"},
						{"Priority": 1, "Type": "LOWERCASE"},
						{"Priority": 2, "Type": "COMPRESS_WHITE_SPACE"}
					]
				}
			},
			"Action": {"Block": {}},
			"VisibilityConfig": {
				"SampledRequestsEnabled": True,
				"CloudWatchMetricsEnabled": True,
				"MetricName": "OptimizedSQLiDetection"
			}
		}

# 使用例
optimizer = RegexOptimizer()

# テスト用の文字列生成
test_strings = [
	"Normal request with some text" * 10,
	"SELECT * FROM users WHERE id=1",
	"' OR '1'='1' UNION SELECT * FROM passwords",
	"普通のリクエスト with 日本語"
] * 100

# パフォーマンステスト実行
results = optimizer.test_performance(test_strings)

# 結果表示
baseline = results["悪い例"]
for name, time_taken in results.items():
	improvement = ((baseline / time_taken - 1) * 100) if name != "悪い例" else 0
	print(f"{name}: {time_taken:.4f}秒 (改善率: {improvement:+.1f}%)")

# WAFルール生成
waf_rule = optimizer.generate_waf_rule(optimizer.optimized_pattern)
print(f"\n生成されたWAFルール:\n{waf_rule}")

Rate Limitingとの組み合わせ

レート制限は、SQLインジェクション攻撃の試行を効果的に抑制します。

{
  "Name": "APIRateLimitWithSQLiProtection",
  "Priority": 1,
  "Statement": {
	"RateBasedStatement": {
	  "Limit": 100,
	  "AggregateKeyType": "IP",
	  "ScopeDownStatement": {
		"AndStatement": {
		  "Statements": [
			{
			  "ByteMatchStatement": {
				"SearchString": "/api/",
				"FieldToMatch": {
				  "UriPath": {}
				},
				"TextTransformations": [
				  {
					"Priority": 0,
					"Type": "LOWERCASE"
				  }
				],
				"PositionalConstraint": "STARTS_WITH"
			  }
			},
			{
			  "NotStatement": {
				"Statement": {
				  "IPSetReferenceStatement": {
					"ARN": "arn:aws:wafv2:us-east-1:123456789012:global/ipset/trusted-ips/xxxxx"
				  }
				}
			  }
			}
		  ]
		}
	  }
	}
  },
  "Action": {
	"Block": {
	  "CustomResponse": {
		"ResponseCode": 429,
		"CustomResponseBodyKey": "rate-limit-exceeded",
		"ResponseHeaders": [
		  {
			"Name": "Retry-After",
			"Value": "60"
		  },
		  {
			"Name": "X-RateLimit-Limit",
			"Value": "100"
		  }
		]
	  }
	}
  },
  "VisibilityConfig": {
	"SampledRequestsEnabled": true,
	"CloudWatchMetricsEnabled": true,
	"MetricName": "APIRateLimitWithSQLiProtection"
  }
}

地理的制限の実装

攻撃の多くは海外から来るため、地理的制限は効果的な防御層となります。

国レベルの制限
高リスク国(攻撃の発信源として知られる国)からのアクセスを自動的にブロックします。AWS WAFのGeo Match Statementを使用し、許可する国のリストを定義する「ホワイトリスト方式」が推奨されます。日本国内向けサービスなら、JP(日本)のみを許可し、それ以外をブロックすることで、攻撃の70-80%を防げます。
IP評価リストの活用
AWS WAF Fraud Control Account Takeover Prevention(ATP)やBot Controlと連携し、悪意のあるIPアドレスを自動的に識別・ブロックします。Amazon IP Reputation Listには、世界中のハニーポットやセキュリティ研究機関から収集された悪意あるIPが含まれ、リアルタイムで更新されます。
VPN/Proxy検出
匿名化サービス経由のアクセスは、攻撃者が身元を隠すために使用することが多いため、検出・制限することが重要です。AWS WAF Bot Controlの Anonymous IP List機能を使用することで、TorノードやVPNサービスのIPアドレスを識別できます。必要に応じて、CAPTCHAチャレンジを要求することで、正当なユーザーとボットを区別できます。

CloudFront、ALBとの統合設定

AWS WAFの効果を最大化するには、CloudFrontやApplication Load Balancer(ALB)との適切な統合が不可欠です。

CloudFrontでのWAF設定

CloudFrontのエッジロケーションでWAFを動作させることで、グローバルな防御と高速なレスポンスを実現できます。

エッジロケーション活用のメリット

# CloudFrontエッジでのWAF実行による効果

【遅延削減効果】
従来構成(東京リージョンのみ):
- 日本国内: 平均50ms
- アジア太平洋: 平均150ms
- 北米: 平均200ms
- ヨーロッパ: 平均250ms

CloudFrontエッジWAF構成:
- 日本国内: 平均10ms(80%改善)
- アジア太平洋: 平均30ms(80%改善)
- 北米: 平均20ms(90%改善)
- ヨーロッパ: 平均25ms(90%改善)

【コスト最適化効果】
オリジンへのリクエスト削減:
- 総リクエスト数: 1000万/月
- エッジでブロック: 100万(10%)
- キャッシュヒット: 800万(80%)
- オリジン到達: 100万(10%)
→ オリジンへの負荷90%削減

帯域幅コスト削減:
- 削減前: $500/月(10TB転送)
- 削減後: $150/月(3TB転送)
→ 70%のコスト削減

EC2/ALB負荷削減:
- CPU使用率: 60% → 15%
- メモリ使用率: 70% → 20%
- インスタンスサイズ縮小可能
→ EC2コスト50%削減可能

キャッシュ設定との調整

CloudFrontのキャッシュとWAFを最適に組み合わせる設定例です。

// CloudFormationテンプレート(YAML形式)
const cloudFormationTemplate = {
  "AWSTemplateFormatVersion": "2010-09-09",
  "Description": "CloudFront with WAF Integration",
  "Resources": {
	"WebACL": {
	  "Type": "AWS::WAFv2::WebACL",
	  "Properties": {
		"Scope": "CLOUDFRONT",
		"DefaultAction": {
		  "Allow": {}
		},
		"Rules": [
		  {
			"Name": "SQLiProtection",
			"Priority": 1,
			"Statement": {
			  "ManagedRuleGroupStatement": {
				"VendorName": "AWS",
				"Name": "AWSManagedRulesSQLiRuleSet"
			  }
			},
			"Action": {
			  "Block": {}
			},
			"VisibilityConfig": {
			  "SampledRequestsEnabled": true,
			  "CloudWatchMetricsEnabled": true,
			  "MetricName": "SQLiProtection"
			}
		  }
		]
	  }
	},
	"CloudFrontDistribution": {
	  "Type": "AWS::CloudFront::Distribution",
	  "Properties": {
		"DistributionConfig": {
		  "WebACLId": {
			"Fn::GetAtt": ["WebACL", "Arn"]
		  },
		  "Origins": [
			{
			  "Id": "ALBOrigin",
			  "DomainName": "alb.example.com",
			  "CustomOriginConfig": {
				"HTTPPort": 80,
				"HTTPSPort": 443,
				"OriginProtocolPolicy": "https-only",
				"OriginSSLProtocols": ["TLSv1.2"]
			  }
			}
		  ],
		  "DefaultCacheBehavior": {
			"TargetOriginId": "ALBOrigin",
			"ViewerProtocolPolicy": "redirect-to-https",
			"AllowedMethods": ["GET", "HEAD", "OPTIONS", "PUT", "POST", "PATCH", "DELETE"],
			"CachedMethods": ["GET", "HEAD", "OPTIONS"],
			"CachePolicyId": "658327ea-f89e-4fab-a63d-7e88639e58f6",
			"OriginRequestPolicyId": "88a5eaf4-2fd4-4709-b370-b4c650ea3fcf",
			"ResponseHeadersPolicyId": "67f04c6a-8e36-4720-a678-344ed4b3357e"
		  },
		  "CacheBehaviors": [
			{
			  "PathPattern": "/api/*",
			  "TargetOriginId": "ALBOrigin",
			  "ViewerProtocolPolicy": "https-only",
			  "AllowedMethods": ["GET", "HEAD", "OPTIONS", "PUT", "POST", "PATCH", "DELETE"],
			  "CachePolicyId": "4135ea2d-6df8-44a3-9df3-4b5a84be39ad",
			  "OriginRequestPolicyId": "88a5eaf4-2fd4-4709-b370-b4c650ea3fcf"
			},
			{
			  "PathPattern": "/admin/*",
			  "TargetOriginId": "ALBOrigin",
			  "ViewerProtocolPolicy": "https-only",
			  "TrustedSigners": ["self"],
			  "CachePolicyId": "4135ea2d-6df8-44a3-9df3-4b5a84be39ad"
			}
		  ],
		  "CustomErrorResponses": [
			{
			  "ErrorCode": 403,
			  "ResponseCode": 403,
			  "ResponsePagePath": "/error/403.html",
			  "ErrorCachingMinTTL": 60
			}
		  ]
		}
	  }
	}
  }
};

Application Load Balancer統合

ALBとの統合により、アプリケーションレイヤーでの詳細な制御が可能になります。

設定項目 推奨値 理由 設定方法
評価位置 リクエスト前 不要な負荷を削減 ALB リスナールールの最上位
ロギング S3バケット 長期保存と分析 ALB アクセスログ有効化
サンプリング 100% 完全な可視化 WAF ロギング設定
メトリクス CloudWatch リアルタイム監視 詳細メトリクス有効化
ヘルスチェック 除外 誤検知防止 ヘルスチェックパスを除外
タイムアウト 30秒 SQLi時間攻撃対策 ALB アイドルタイムアウト

API Gateway保護

API GatewayでのWAF統合により、APIエンドポイントを保護します。

# Terraform による API Gateway + WAF 設定
terraform_config = """
# WAF Web ACL
resource "aws_wafv2_web_acl" "api_protection" {
  name  = "api-gateway-waf"
  scope = "REGIONAL"
  
  default_action {
	allow {}
  }
  
  rule {
	name     = "SQLiProtection"
	priority = 1
	
	override_action {
	  none {}
	}
	
	statement {
	  managed_rule_group_statement {
		name        = "AWSManagedRulesSQLiRuleSet"
		vendor_name = "AWS"
		
		excluded_rule {
		  name = "SQLi_QUERYARGUMENTS"
		}
	  }
	}
	
	visibility_config {
	  cloudwatch_metrics_enabled = true
	  metric_name               = "SQLiProtectionMetric"
	  sampled_requests_enabled  = true
	}
  }
  
  rule {
	name     = "RateLimiting"
	priority = 2
	
	action {
	  block {}
	}
	
	statement {
	  rate_based_statement {
		limit              = 2000
		aggregate_key_type = "IP"
	  }
	}
	
	visibility_config {
	  cloudwatch_metrics_enabled = true
	  metric_name               = "RateLimitingMetric"
	  sampled_requests_enabled  = true
	}
  }
  
  visibility_config {
	cloudwatch_metrics_enabled = true
	metric_name               = "APIProtectionMetric"
	sampled_requests_enabled  = true
  }
}

# API Gateway REST API
resource "aws_api_gateway_rest_api" "main" {
  name = "protected-api"
  
  endpoint_configuration {
	types = ["REGIONAL"]
  }
}

# WAF Association
resource "aws_wafv2_web_acl_association" "api_gateway" {
  resource_arn = aws_api_gateway_stage.prod.arn
  web_acl_arn  = aws_wafv2_web_acl.api_protection.arn
}

# API Gateway Method Settings
resource "aws_api_gateway_method_settings" "settings" {
  rest_api_id = aws_api_gateway_rest_api.main.id
  stage_name  = aws_api_gateway_stage.prod.stage_name
  method_path = "*/*"
  
  settings {
	throttling_rate_limit  = 1000
	throttling_burst_limit = 2000
	logging_level         = "ERROR"
	data_trace_enabled    = true
	metrics_enabled       = true
	
	# WAF統合でのキャッシュ設定
	caching_enabled      = true
	cache_ttl_in_seconds = 300
	cache_data_encrypted = true
  }
}

# API Gateway Stage
resource "aws_api_gateway_stage" "prod" {
  deployment_id = aws_api_gateway_deployment.main.id
  rest_api_id   = aws_api_gateway_rest_api.main.id
  stage_name    = "prod"
  
  xray_tracing_enabled = true
  
  access_log_settings {
	destination_arn = aws_cloudwatch_log_group.api_gateway.arn
	format = jsonencode({
	  requestId      = "$context.requestId"
	  ip            = "$context.identity.sourceIp"
	  requestTime   = "$context.requestTime"
	  httpMethod    = "$context.httpMethod"
	  routeKey      = "$context.routeKey"
	  status        = "$context.status"
	  protocol      = "$context.protocol"
	  responseLength = "$context.responseLength"
	  error         = "$context.error.message"
	  wafResponse   = "$context.wafResponseCode"
	})
  }
}
"""

ログ分析とモニタリング設定

効果的なログ分析により、攻撃の早期発見と対応が可能になります。

CloudWatch Insightsクエリ例

CloudWatch Logs Insightsを使用した、SQLインジェクション攻撃の詳細分析です。

SQLインジェクション攻撃の分析

-- 1. 攻撃パターン分析クエリ(上位攻撃者の特定)
fields @timestamp, httpRequest.clientIp as ClientIP,
	   httpRequest.uri as URI,
	   httpRequest.args as QueryString,
	   terminatingRuleId as BlockRule,
	   action as Action
| filter action = "BLOCK"
| filter terminatingRuleId like /SQL/
| stats count() as AttackCount by ClientIP
| sort AttackCount desc
| limit 20

-- 2. 時系列攻撃傾向(5分間隔でのブロック数推移)
fields @timestamp, action, terminatingRuleId
| filter action = "BLOCK"
| filter terminatingRuleId like /SQL/
| stats count() as BlockedRequests by bin(@timestamp, 5m) as TimeWindow
| sort TimeWindow desc

-- 3. 攻撃対象URLの分析
fields httpRequest.uri as TargetURL, 
	   httpRequest.httpMethod as Method
| filter action = "BLOCK"
| filter terminatingRuleId like /SQL/
| stats count() as Attacks by TargetURL, Method
| sort Attacks desc
| limit 50

-- 4. 攻撃ペイロードの詳細分析
fields @timestamp,
	   httpRequest.clientIp as AttackerIP,
	   httpRequest.uri as URI,
	   httpRequest.args as Payload,
	   terminatingRuleMatchDetails[0].conditionEvaluations[0].matchedData as MatchedPattern
| filter action = "BLOCK"
| filter terminatingRuleId like /SQLi/
| sort @timestamp desc
| limit 100

-- 5. 地理的分布分析
fields httpRequest.country as Country,
	   httpRequest.clientIp as IP
| filter action = "BLOCK"
| stats count() as Attacks by Country
| sort Attacks desc

-- 6. User-Agent分析(ボット検出)
fields httpRequest.headers[?name=='User-Agent'].value as UserAgent
| filter action = "BLOCK"
| stats count() as Count by UserAgent
| sort Count desc
| limit 20

アラート設定

段階的なアラート設定により、適切なエスカレーションを実現します。

Critical(緊急対応)- 即座にページャー通知
5分間で同一IPから100回以上のSQLインジェクション検知、または異なる10個以上のIPから同時攻撃を検知した場合。これは組織的な攻撃やボットネット攻撃の可能性が高く、即座の対応が必要です。自動的にIPをブロックリストに追加し、セキュリティチームに電話通知を送信します。インシデント対応プロセスを自動起動し、証拠保全を開始します。
Warning(要確認)- Slackまたはメール通知
新規の攻撃パターン検出、通常の3倍以上のブロック率、または特定のURLに対する集中攻撃を検知した場合。これらは潜在的な脅威として、営業時間内に確認が必要です。ログを自動的にS3に保存し、分析レポートを生成します。必要に応じて、追加のWAFルールを検討します。
Info(記録のみ)- ログ記録とダッシュボード更新
既知パターンの単発検知、低リスク国からの散発的アクセス、または誤検知の可能性が高いケース。これらは統計情報として記録し、週次・月次レポートに含めます。パターン分析により、将来の攻撃予測に活用します。

Security Hubとの統合

AWS Security Hubとの統合により、セキュリティ態勢の一元管理を実現します。

{
  "SecurityHubIntegration": {
	"Enabled": true,
	"FindingPublishing": {
	  "Severity": ["CRITICAL", "HIGH", "MEDIUM"],
	  "Types": [
		"Software and Configuration Checks/AWS Security Best Practices",
		"Sensitive Data Identifications/PII",
		"TTPs/Initial Access/SQL Injection"
	  ],
	  "ProductArn": "arn:aws:securityhub:us-east-1:123456789012:product/123456789012/default"
	},
	"AutoRemediation": {
	  "Enabled": true,
	  "LambdaFunction": "arn:aws:lambda:us-east-1:123456789012:function:WAFAutoRemediation",
	  "Actions": [
		{
		  "Type": "BlockIP",
		  "Threshold": 50,
		  "Duration": 3600
		},
		{
		  "Type": "NotifySOC",
		  "Channel": "security-alerts",
		  "Priority": "HIGH"
		},
		{
		  "Type": "CreateTicket",
		  "System": "ServiceNow",
		  "Template": "security-incident"
		},
		{
		  "Type": "EnableEnhancedMonitoring",
		  "Target": "SourceIP",
		  "Duration": 86400
		}
	  ]
	},
	"ComplianceMapping": {
	  "PCI-DSS": ["6.5.1", "6.6"],
	  "ISO27001": ["A.14.2.5", "A.13.1.1"],
	  "NIST": ["SI-10", "SC-5"]
	}
  }
}

月額コストの試算と最適化

AWS WAFのコストを正確に把握し、最適化することで、費用対効果の高い運用を実現できます。

コスト最適化テクニック

効果的なコスト削減施策とその実装方法を紹介します。

施策 削減率 実装難易度 実装方法 注意点
不要ルール削除 20-30% 使用頻度の低いルールを特定して削除 セキュリティレベル低下リスク
サンプリング設定 10-15% ログのサンプリングレートを調整 完全な可視性の喪失
リージョン集約 15-20% 複数リージョンのWAFを統合 レイテンシー増加の可能性
Reserved Capacity 25% 年間契約による割引適用 柔軟性の低下
ログ階層化 30-40% S3 Intelligent-Tiering活用 アクセス速度の低下

実装手順とテンプレート

Infrastructure as Codeを活用した、再現性のある実装方法を提供します。

Infrastructure as Codeでの展開

AWS CDKを使用した、モダンな実装アプローチです。

CDKでの実装例

// AWS CDK v2 での WAF 実装
import * as cdk from 'aws-cdk-lib';
import * as wafv2 from 'aws-cdk-lib/aws-wafv2';
import * as cloudfront from 'aws-cdk-lib/aws-cloudfront';
import * as s3 from 'aws-cdk-lib/aws-s3';
import * as logs from 'aws-cdk-lib/aws-logs';
import { Construct } from 'constructs';

export class ComprehensiveWAFStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
	super(scope, id, props);
	
	// ログ用S3バケット
	const logBucket = new s3.Bucket(this, 'WAFLogBucket', {
	  bucketName: `waf-logs-${this.account}-${this.region}`,
	  encryption: s3.BucketEncryption.S3_MANAGED,
	  lifecycleRules: [{
		id: 'DeleteOldLogs',
		expiration: cdk.Duration.days(90),
		transitions: [{
		  storageClass: s3.StorageClass.INTELLIGENT_TIERING,
		  transitionAfter: cdk.Duration.days(30)
		}]
	  }],
	  blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL
	});
	
	// IPセット(信頼できるIP)
	const trustedIpSet = new wafv2.CfnIPSet(this, 'TrustedIPs', {
	  scope: 'CLOUDFRONT',
	  ipAddressVersion: 'IPV4',
	  addresses: [
		'10.0.0.0/8',      // 内部ネットワーク
		'192.168.0.0/16',  // プライベートネットワーク
	  ]
	});
	
	// カスタムレスポンスボディ
	const customResponseBodies = {
	  'blocked-request': {
		content: JSON.stringify({
		  error: 'Access Denied',
		  message: 'Your request has been blocked due to security reasons.',
		  reference: '{{request_id}}'
		}),
		contentType: 'APPLICATION_JSON'
	  },
	  'rate-limit-exceeded': {
		content: JSON.stringify({
		  error: 'Too Many Requests',
		  message: 'Rate limit exceeded. Please try again later.'
		}),
		contentType: 'APPLICATION_JSON'
	  }
	};
	
	// Web ACL
	const webAcl = new wafv2.CfnWebACL(this, 'WebACL', {
	  scope: 'CLOUDFRONT',
	  defaultAction: { allow: {} },
	  customResponseBodies: customResponseBodies,
	  rules: [
		// 1. IP許可リスト
		{
		  name: 'AllowTrustedIPs',
		  priority: 1,
		  statement: {
			ipSetReferenceStatement: {
			  arn: trustedIpSet.attrArn
			}
		  },
		  action: { allow: {} },
		  visibilityConfig: {
			sampledRequestsEnabled: true,
			cloudWatchMetricsEnabled: true,
			metricName: 'AllowTrustedIPs'
		  }
		},
		// 2. レート制限
		{
		  name: 'RateLimiting',
		  priority: 10,
		  statement: {
			rateBasedStatement: {
			  limit: 2000,
			  aggregateKeyType: 'IP'
			}
		  },
		  action: {
			block: {
			  customResponse: {
				responseCode: 429,
				customResponseBodyKey: 'rate-limit-exceeded'
			  }
			}
		  },
		  visibilityConfig: {
			sampledRequestsEnabled: true,
			cloudWatchMetricsEnabled: true,
			metricName: 'RateLimiting'
		  }
		},
		// 3. AWS Managed Rules - Core Rule Set
		{
		  name: 'AWSManagedRulesCommonRuleSet',
		  priority: 20,
		  overrideAction: { none: {} },
		  statement: {
			managedRuleGroupStatement: {
			  vendorName: 'AWS',
			  name: 'AWSManagedRulesCommonRuleSet',
			  excludedRules: [
				{ name: 'SizeRestrictions_BODY' },
				{ name: 'GenericRFI_BODY' }
			  ]
			}
		  },
		  visibilityConfig: {
			sampledRequestsEnabled: true,
			cloudWatchMetricsEnabled: true,
			metricName: 'CommonRuleSetMetric'
		  }
		},
		// 4. AWS Managed Rules - SQL Database
		{
		  name: 'AWSManagedRulesSQLiRuleSet',
		  priority: 30,
		  overrideAction: { none: {} },
		  statement: {
			managedRuleGroupStatement: {
			  vendorName: 'AWS',
			  name: 'AWSManagedRulesSQLiRuleSet',
			  scopeDownStatement: {
				notStatement: {
				  statement: {
					byteMatchStatement: {
					  searchString: '/admin/',
					  fieldToMatch: { uriPath: {} },
					  positionalConstraint: 'STARTS_WITH',
					  textTransformations: [{
						priority: 0,
						type: 'LOWERCASE'
					  }]
					}
				  }
				}
			  }
			}
		  },
		  visibilityConfig: {
			sampledRequestsEnabled: true,
			cloudWatchMetricsEnabled: true,
			metricName: 'SQLiRuleSetMetric'
		  }
		},
		// 5. カスタムSQLインジェクション検知
		{
		  name: 'CustomSQLiDetection',
		  priority: 40,
		  statement: {
			orStatement: {
			  statements: [
				{
				  sqliMatchStatement: {
					fieldToMatch: {
					  allQueryArguments: {}
					},
					textTransformations: [
					  { priority: 0, type: 'URL_DECODE' },
					  { priority: 1, type: 'HTML_ENTITY_DECODE' },
					  { priority: 2, type: 'LOWERCASE' }
					]
				  }
				},
				{
				  regexMatchStatement: {
					regexString: '(?i)(union.*select|select.*from|insert.*into|delete.*from)',
					fieldToMatch: {
					  body: {
						oversizeHandling: 'MATCH'
					  }
					},
					textTransformations: [
					  { priority: 0, type: 'URL_DECODE' },
					  { priority: 1, type: 'LOWERCASE' }
					]
				  }
				}
			  ]
			}
		  },
		  action: {
			block: {
			  customResponse: {
				responseCode: 403,
				customResponseBodyKey: 'blocked-request'
			  }
			}
		  },
		  visibilityConfig: {
			sampledRequestsEnabled: true,
			cloudWatchMetricsEnabled: true,
			metricName: 'CustomSQLiDetection'
		  }
		},
		// 6. 地理的制限(日本のみ許可)
		{
		  name: 'GeoRestriction',
		  priority: 50,
		  statement: {
			notStatement: {
			  statement: {
				geoMatchStatement: {
				  countryCodes: ['JP', 'US']  // 日本とアメリカのみ許可
				}
			  }
			}
		  },
		  action: {
			block: {
			  customResponse: {
				responseCode: 403,
				customResponseBodyKey: 'blocked-request'
			  }
			}
		  },
		  visibilityConfig: {
			sampledRequestsEnabled: true,
			cloudWatchMetricsEnabled: true,
			metricName: 'GeoRestriction'
		  }
		}
	  ],
	  visibilityConfig: {
		sampledRequestsEnabled: true,
		cloudWatchMetricsEnabled: true,
		metricName: 'WebACL'
	  }
	});
	
	// ログ設定
	const logConfig = new wafv2.CfnLoggingConfiguration(this, 'WAFLogging', {
	  resourceArn: webAcl.attrArn,
	  logDestinationConfigs: [
		`arn:aws:s3:::${logBucket.bucketName}/waf-logs/`
	  ],
	  loggingFilter: {
		defaultBehavior: 'KEEP',
		filters: [
		  {
			behavior: 'DROP',
			requirement: 'MEETS_ANY',
			conditions: [
			  {
				actionCondition: {
				  action: 'ALLOW'
				}
			  }
			]
		  }
		]
	  }
	});
	
	// CloudWatch Alarms
	const sqlInjectionAlarm = new cloudwatch.Alarm(this, 'SQLInjectionAlarm', {
	  metric: new cloudwatch.Metric({
		namespace: 'AWS/WAFV2',
		metricName: 'BlockedRequests',
		dimensionsMap: {
		  Rule: 'CustomSQLiDetection',
		  WebACL: webAcl.name!,
		  Region: 'GLOBAL'
		},
		statistic: 'Sum',
		period: cdk.Duration.minutes(5)
	  }),
	  threshold: 100,
	  evaluationPeriods: 1,
	  treatMissingData: cloudwatch.TreatMissingData.NOT_BREACHING
	});
	
	// Outputs
	new cdk.CfnOutput(this, 'WebACLArn', {
	  value: webAcl.attrArn,
	  description: 'Web ACL ARN for CloudFront association'
	});
	
	new cdk.CfnOutput(this, 'LogBucketName', {
	  value: logBucket.bucketName,
	  description: 'S3 bucket for WAF logs'
	});
  }
}

段階的導入プラン

リスクを最小化しながら、確実にWAFを導入するための段階的アプローチです。

Phase 1(週1-2):監視モード
すべてのルールをCount modeに設定し、実際の攻撃をブロックせずに検知のみ行います。この期間に、正常なトラフィックパターンを学習し、誤検知となる可能性があるリクエストを特定します。ログを詳細に分析し、業務に影響を与える可能性があるルールを洗い出します。社内からのテストアクセスを行い、管理画面や特殊な機能が正常に動作することを確認します。
Phase 2(週3-4):部分適用
明確な攻撃パターン(SQLインジェクションの典型的なペイロード)のみをBlock modeに変更し、その他はCount modeを継続します。段階的にブロック対象を増やし、各段階で24時間の監視期間を設けます。誤検知が発生した場合は、即座にルールを調整またはCount modeに戻します。この期間に、ホワイトリストやカスタムルールの作成を行います。
Phase 3(週5-6):本番適用
すべてのルールをBlock modeに変更し、本格的な防御を開始します。例外設定やカスタムルールの調整が完了し、誤検知率が0.1%以下になることを確認します。24時間体制での監視を行い、問題が発生した場合の緊急対応手順を確立します。すべてのステークホルダーに運用開始を通知し、問い合わせ窓口を明確にします。
Phase 4(週7-8):最適化
蓄積されたデータを基に、パフォーマンスとコストの最適化を行います。使用頻度の低いルールを削除し、頻繁に実行されるルールの処理順序を最適化します。CloudFrontのキャッシュ設定と連携し、オリジンへの負荷を最小化します。月次レビュープロセスを確立し、継続的な改善サイクルを構築します。

トラブルシューティング

運用中に発生する一般的な問題と、その解決方法を解説します。

よくある問題と解決方法

実際の運用で頻繁に遭遇する問題への対処法です。

誤検知への対処

症状 原因 対策 予防策
正常リクエストがブロック ルール感度が高すぎる 感度を1段階下げる、除外ルール追加 段階的な感度調整
特定ユーザーのみ影響 特殊文字や日本語の使用 該当IPをホワイトリスト登録 文字エンコーディング検証
API呼び出しエラー ペイロードサイズ超過 サイズ制限を8KBから64KBに調整 API仕様の事前確認
管理画面アクセス不可 HTMLコード含むコンテンツ 管理パスを除外設定 管理URLの事前登録
ファイルアップロード失敗 Base64データの誤検知 アップロードパスを除外 専用エンドポイント設計

パフォーマンス問題

WAF導入によるレイテンシー影響を測定し、対策を講じます。

#!/bin/bash
# WAFパフォーマンス測定スクリプト

echo "=== AWS WAF Performance Testing ==="
echo "Date: $(date)"
echo ""

# テスト対象URL
DIRECT_URL="https://origin.example.com/api/test"
WAF_URL="https://waf-protected.example.com/api/test"
ITERATIONS=100

# 関数:レイテンシ測定
measure_latency() {
	local url=$1
	local name=$2
	local results=()
	
	echo "Testing $name ($url)..."
	
	for i in $(seq 1 $ITERATIONS); do
		# curl で応答時間を測定
		time=$(curl -w "%{time_total}" -o /dev/null -s "$url")
		results+=($time)
		
		# プログレス表示
		if [ $((i % 10)) -eq 0 ]; then
			echo -n "."
		fi
	done
	echo " Done!"
	
	# 統計計算
	total=0
	min=999999
	max=0
	
	for time in "${results[@]}"; do
		total=$(echo "$total + $time" | bc)
		
		if (( $(echo "$time < $min" | bc -l) )); then
			min=$time
		fi
		
		if (( $(echo "$time > $max" | bc -l) )); then
			max=$time
		fi
	done
	
	avg=$(echo "scale=4; $total / $ITERATIONS" | bc)
	
	# 結果表示
	echo "Results for $name:"
	echo "  Average: ${avg}s"
	echo "  Min: ${min}s"
	echo "  Max: ${max}s"
	echo ""
	
	# 配列を返す
	echo "${results[@]}"
}

# 直接アクセスの測定
echo "1. Direct Access (No WAF)"
direct_results=$(measure_latency "$DIRECT_URL" "Direct")

# WAF経由の測定
echo "2. WAF Protected Access"
waf_results=$(measure_latency "$WAF_URL" "WAF")

# オーバーヘッド計算
echo "3. Performance Impact Analysis"
direct_avg=$(echo "$direct_results" | awk '{sum=0; for(i=1;i<=NF;i++)sum+=$i; print sum/NF}')
waf_avg=$(echo "$waf_results" | awk '{sum=0; for(i=1;i<=NF;i++)sum+=$i; print sum/NF}')
overhead=$(echo "scale=2; ($waf_avg - $direct_avg) * 1000" | bc)
overhead_percent=$(echo "scale=2; (($waf_avg / $direct_avg) - 1) * 100" | bc)

echo "WAF Overhead: ${overhead}ms (${overhead_percent}%)"

# 推奨事項
echo ""
echo "4. Recommendations:"
if (( $(echo "$overhead_percent > 20" | bc -l) )); then
	echo "  ⚠️  High overhead detected. Consider:"
	echo "     - Reducing rule complexity"
	echo "     - Enabling CloudFront caching"
	echo "     - Optimizing regex patterns"
elif (( $(echo "$overhead_percent > 10" | bc -l) )); then
	echo "  ⚠️  Moderate overhead. Consider:"
	echo "     - Reviewing rule priorities"
	echo "     - Removing unnecessary transformations"
else
	echo "  ✅ Overhead is within acceptable range"
fi

# CloudWatch カスタムメトリクス送信
aws cloudwatch put-metric-data \
	--namespace "Custom/WAF" \
	--metric-name "Latency" \
	--dimensions Endpoint=Direct \
	--value "$direct_avg" \
	--unit Seconds

aws cloudwatch put-metric-data \
	--namespace "Custom/WAF" \
	--metric-name "Latency" \
	--dimensions Endpoint=WAF \
	--value "$waf_avg" \
	--unit Seconds

echo ""
echo "Metrics sent to CloudWatch successfully!"

よくある質問(FAQ)

Q: AWS WAFとCloudflare WAFどちらが良いですか?
A: AWS環境を中心に構築されているシステムなら、AWS WAFが統合性とコスト効率で優れています。CloudFront、ALB、API Gatewayとのネイティブ統合により、設定や管理が簡単で、IAMによる細かい権限管理も可能です。料金は従量課金制で、月1000万リクエストなら約$76(約11,400円)程度です。一方、Cloudflareは固定料金制(Pro版$20/月)で、DDoS対策も含まれており、マルチクラウド環境やCDNを重視する場合に有利です。AWSインフラが中心なら AWS WAF、グローバル展開やマルチクラウドなら Cloudflare を推奨します。どちらも[SQLインジェクション対策](/security/web-api/sql-injection/)として十分な機能を提供しています。
Q: マネージドルールとカスタムルールの使い分けは?
A: まずAWS Managed Rules(特にCore Rule Set)で基本的な防御体制を構築することを推奨します。これにより、[OWASP Top 10](/security/web-api/)に対応した包括的な保護が即座に実現できます。その後、自社の業務特性に応じてカスタムルールを追加していきます。例えば、日本語サイトなら海外からのアクセスを制限する地理的制限、APIならRate Limitingによる過剰アクセス防止、管理画面なら特定IPのみ許可するなどです。一般的な配分として、マネージドルールで攻撃の80%を防ぎ、カスタムルールで残り20%の業務固有のリスクに対応するイメージです。コストと運用負荷のバランスを考慮し、段階的に最適化していくことが重要です。
Q: ログの保存期間とコストはどう考えればよいですか?
A: 通常の運用では3ヶ月の保存を推奨します。これは、攻撃パターンの分析とインシデント調査に十分な期間です。S3 Intelligent-Tieringを使用することで、アクセス頻度に応じて自動的にストレージクラスが変更され、コストが最適化されます。月1000万リクエストの場合、ログ保存コストは約$10/月程度です。コンプライアンス要件(PCI DSS、個人情報保護法等)がある場合は、1年以上の保存が必要になることもあります。この場合、90日以降のログをS3 Glacier Deep Archiveに移行することで、コストを約80%削減できます。分析用途では、直近1週間分をCloudWatch Logsに、1ヶ月分をS3 Standard に、それ以前をS3 Glacier に保存する階層化戦略が効果的です。
Q: WAF導入後もアプリケーションの脆弱性対策は必要ですか?
A: はい、絶対に必要です。WAFは「仮想パッチ」として機能し、既知の攻撃パターンをブロックしますが、根本的な解決ではありません。[プレースホルダ](/security/web-api/sql-injection/column/placeholder-implementation/)や[適切なエスケープ処理](/security/web-api/sql-injection/column/escape-processing/)などの実装レベルの対策は継続的に行う必要があります。WAFは多層防御の一層であり、アプリケーション側のセキュア開発、定期的な脆弱性診断、迅速なパッチ適用と組み合わせることで、真の安全性が実現されます。WAFで時間を稼ぎながら、根本的な脆弱性を修正していくアプローチが理想的です。
Q: CloudFrontを使用していない場合でもAWS WAFは使えますか?
A: はい、使用できます。AWS WAFはREGIONALスコープとCLOUDFRONTスコープの2種類があり、REGIONALスコープではApplication Load Balancer(ALB)、API Gateway、AppSync GraphQL API、Cognito User Pool、App Runner、Verified Access に直接適用できます。ただし、CloudFrontと組み合わせることで、エッジロケーションでの防御により、レイテンシー削減とオリジンの負荷軽減という追加のメリットが得られます。既存のアーキテクチャを大きく変更せずにWAFを導入したい場合は、まずALBにREGIONAL WAFを適用し、段階的にCloudFrontの導入を検討することをお勧めします。

まとめ

AWS WAFは、AWSエコシステムにおける SQLインジェクション対策の要となるサービスです。適切に設定・運用することで、高度な攻撃からWebアプリケーションを守ることができます。

AWS WAF導入の重要ポイント

  1. 段階的な導入アプローチ

    • Count modeから始めて誤検知を最小化
    • 2-3週間かけて慎重に調整
    • 本番適用後も継続的な最適化
  2. コスト効率の最大化

    • 月額$76程度から本格的な防御が可能
    • 不要なルールの削除で20-30%のコスト削減
    • S3 Intelligent-Tieringでログコストを最適化
  3. 統合による相乗効果

    • CloudFrontとの組み合わせでグローバル防御
    • Security Hubで一元的なセキュリティ管理
    • CloudWatch Insightsで詳細な攻撃分析
  4. 継続的な改善サイクル

    • 週次でのルール見直し
    • 月次でのKPI評価
    • 四半期ごとの大規模レビュー

特に重要なのは、WAFを「設定して終わり」ではなく、継続的に改善していくことです。攻撃手法は日々進化しており、それに対応するためには、定期的なルールの更新と最適化が不可欠です。

AWS WAFは、中小企業から大企業まで、規模に応じた柔軟な防御を提供します。Infrastructure as Codeによる管理により、複数環境への展開も容易で、DevSecOpsの実践にも適しています。

最後に、WAFは万能ではありません。包括的なWebセキュリティ対策の一部として位置づけ、アプリケーションレベルの対策と組み合わせることで、真の安全性を実現してください。


【重要なお知らせ】

  • 本記事は一般的な情報提供を目的としており、個別の状況に対する助言ではありません
  • AWS WAFの仕様と価格は変更される可能性があります(2025年1月時点の情報)
  • 実装の際は、AWS公式ドキュメントで最新情報を確認してください
  • セキュリティ設定は定期的に見直し、最新の脅威に対応することが重要です

更新履歴

初稿公開

京都開発研究所

システム開発/サーバ構築・保守/技術研究

CMSの独自開発および各業務管理システム開発を行っており、 10年以上にわたり自社開発CMSにて作成してきた70,000以上のサイトを 自社で管理するサーバに保守管理する。