CloudFront ディストリビューションでのカスタムオリジンへのトラフィックの暗号化について

このブログシリーズ 「クラウドセキュリティ 実践集」 では、一般的なセキュリティ課題を取り上げ、「なぜ危険なのか?」 というリスクの解説から、「どうやって直すのか?」 という具体的な修復手順(コンソール、AWS CLI、Terraformなど)まで、分かりやすく解説します。

今回は、Amazon CloudFrontディストリビューションからカスタムオリジンへのトラフィックが暗号化されていない状態について、そのリスクと対策を解説します。

リスク

Amazon CloudFrontは、AWSのグローバルなCDN (コンテンツデリバリーネットワーク) サービスで、ウェブサイト、API、動画コンテンツなどを高速かつ安全にユーザーに配信します。CloudFrontは、コンテンツを配信するために、S3バケット、EC2インスタンス、ELB、またはオンプレミスサーバーなどの「オリジン」からコンテンツを取得します。このオリジンへの通信において、HTTPS(TLS)による暗号化が適切に設定されていない場合、以下のような重大なセキュリティリスクが発生します。

  • データ傍受(盗聴)のリスク: CloudFrontとオリジン間の通信がHTTP(平文)である場合、ネットワーク経路上の攻撃者によってデータが傍受(盗聴)される可能性があります。これにより、ユーザーの個人情報、セッションデータ、APIキー、ビジネスロジックに関する情報など、機密性の高い情報が露出するリスクがあります。
  • データ改ざんのリスク: 平文の通信は、データの整合性が保証されません。攻撃者が通信経路上のデータを改ざんし、ウェブサイトのコンテンツやアプリケーションの挙動を不正に変更する可能性があります。これにより、マルウェアの注入、不正なリダイレクト、誤情報の表示などが発生し、ユーザー体験の低下やブランドイメージの毀損につながります。
  • 中間者攻撃 (Man-in-the-Middle, MitM) のリスク: HTTPSが使用されていない場合、攻撃者がCloudFrontとオリジンの間に介入し、あたかも正規の通信相手であるかのように振る舞う中間者攻撃が可能になります。これにより、通信内容の盗聴や改ざんが容易になり、深刻なセキュリティ侵害につながります。
  • 信頼性の低下: ユーザーからCloudFrontまでの通信(ビューワープロトコル)がHTTPSで保護されていても、CloudFrontからオリジンまでの通信がHTTPの場合、エンドツーエンドのセキュリティは不完全です。これにより、ユーザーからの信頼が損なわれる可能性があります。

対策

Amazon CloudFrontディストリビューションからカスタムオリジンへのトラフィックをHTTPS (TLS) で暗号化することは、上記のすべてのリスクを軽減し、エンドツーエンドのセキュリティを確保するための重要なベストプラクティスです。

  • オリジンでのHTTPS設定: カスタムオリジン(ELB、EC2、オンプレミスサーバーなど)が、信頼できる認証局(CA)によって署名された有効なSSL/TLS証明書をインストールし、HTTPS接続を受け入れられるように設定されていることを確認します。自己署名証明書は使用しないよう強く推奨します。
  • CloudFrontオリジンプロトコルの設定: CloudFrontディストリビューションのオリジン設定において、「オリジンプロトコルポリシー」を「HTTPS Only」に設定します。これにより、CloudFrontは常にオリジンへのHTTPS接続を確立します。
  • オリジン証明書の検証: CloudFrontのオリジン設定で「オリジン SSL 証明書の検証」を「Yes」に設定します。これにより、CloudFrontはオリジンサーバーが提示する証明書を検証し、信頼できるCAによって発行され、ホスト名と一致することを確認します。これにより、中間者攻撃を防ぎます。
  • 最小TLSバージョンの設定: CloudFrontのオリジン設定において、「最小オリジン SSL プロトコル」を最新かつ最も安全なTLSバージョン(例: TLSv1.2)に設定します。これにより、脆弱な旧バージョンのTLSプロトコルを介した接続を防ぎます。
  • OAI/OACの利用: S3バケットをオリジンとする場合、Origin Access Control (OAC) または Origin Access Identity (OAI) を使用して、CloudFrontのみがS3バケットにアクセスできるように制限します。これにより、S3バケットへの直接アクセスを防ぎ、CloudFront経由のHTTPS通信を強制できます。

修復方法

Amazon CloudFrontディストリビューションからカスタムオリジンへのトラフィックをHTTPSで暗号化する方法は、AWSコンソールまたはTerraformで行えます。ここでは、カスタムHTTPサーバー(ELBやEC2など)をオリジンとする場合の設定を中心に説明します。

AWSコンソールでの修復手順

AWSコンソールを使用して、CloudFrontディストリビューションのオリジン設定を変更し、HTTPSのみを許可するようにします。

前提:

  • CloudFrontディストリビューションに紐付けたいカスタムオリジン(ELB、EC2、オンプレミスWebサーバーなど)が、HTTPS接続を受け入れられるように設定されており、有効なSSL/TLS証明書がインストールされていること。
  1. CloudFrontサービスへ移動: AWSコンソールにログインし、Amazon CloudFront サービスを開きます。
  2. ディストリビューションを選択: 設定を変更したいCloudFrontディストリビューションを選択します。
  3. 「ビヘイビア」タブへ移動: ディストリビューションの詳細ページで「ビヘイビア」タブをクリックします。
  4. ビヘイビアを選択して編集: 編集したいビヘイビアを選択し、「編集」ボタンをクリックします。
  5. ビューワープロトコルポリシーの設定:
    • ビューワープロトコルポリシー」のメニューで、「HTTPS Only」を選択します。
  1. オリジン SSL 証明書の検証の確認:
    • オリジン SSL 証明書の検証」が「Yes」に設定されていることを確認します。これにより、CloudFrontはオリジン証明書を検証します。
      • (もしオリジンが自己署名証明書を使用している場合や、ACMなどの信頼できるCA以外で発行された証明書を使用している場合は「No」に設定することも可能ですが、セキュリティリスクが高まるため非推奨です。)
  1. 変更を保存: 設定内容を確認し、「変更を保存」をクリックします。
    • 変更が反映されるまでには、ディストリビューションのステータスが「Deploying」となり、数分から数十分かかる場合があります。

Terraformでの修復手順

TerraformでAmazon CloudFrontディストリビューションのカスタムオリジンへのトラフィックをHTTPSで暗号化するには、aws_cloudfront_distribution リソースの origin ブロックにある origin_protocol_policyorigin_ssl_protocols パラメータを設定します。

# (前提) カスタムオリジン (例: ELB)
resource "aws_lb" "example_lb" {
  name               = "example-lb"
  internal           = false
  load_balancer_type = "application"
  security_groups    = [aws_security_group.lb_sg.id]
  subnets            = [aws_subnet.public_a.id, aws_subnet.public_b.id] # パブリックサブネット

  enable_deletion_protection = false

  tags = {
    Environment = "Production"
  }
}

resource "aws_lb_listener" "http_listener" {
  load_balancer_arn = aws_lb.example_lb.arn
  port              = 80
  protocol          = "HTTP"
  default_action {
    type = "redirect" # HTTP から HTTPS へのリダイレクト
    redirect {
      port        = "443"
      protocol    = "HTTPS"
      status_code = "HTTP_301"
    }
  }
}

resource "aws_acm_certificate" "lb_cert" {
  domain_name       = "app.example.com" # あなたのカスタムドメイン名に置き換え
  validation_method = "DNS"
  tags = {
    Environment = "Production"
  }
  lifecycle {
    create_before_destroy = true
  }
}

resource "aws_route53_record" "lb_cert_validation" {
  for_each = {
    for dvo in aws_acm_certificate.lb_cert.domain_validation_options : dvo.domain_name => dvo
  }
  allow_overwrite = true
  name            = each.value.resource_record_name
  records         = [each.value.resource_record_value]
  type            = each.value.resource_record_type
  zone_id         = "YOUR_ROUTE53_HOSTED_ZONE_ID" # あなたのRoute 53 ホストゾーンIDに置き換え
  ttl             = 60
}

resource "aws_acm_certificate_validation" "lb_cert_validation" {
  certificate_arn = aws_acm_certificate.lb_cert.arn
  validation_record_fqdns = [for record in aws_route53_record.lb_cert_validation : record.fqdn]
}

resource "aws_lb_listener" "https_listener" {
  load_balancer_arn = aws_lb.example_lb.arn
  port              = 443
  protocol          = "HTTPS"
  certificate_arn   = aws_acm_certificate.lb_cert.arn # ELBにカスタム証明書を設定
  ssl_policy        = "ELBSecurityPolicy-TLS13-Ext-2023-08" # 推奨される最新のポリシー

  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.example_tg.arn
  }
}

resource "aws_lb_target_group" "example_tg" {
  name     = "example-tg"
  port     = 80 # バックエンドアプリケーションがHTTP 80でリッスンしている場合
  protocol = "HTTP"
  vpc_id   = aws_vpc.main.id
}

# (前提) VPC, サブネット, セキュリティグループは別途定義 (上記Sagemakerの例などを参照)
resource "aws_vpc" "main" {
  cidr_block = "10.0.0.0/16"
}
resource "aws_subnet" "public_a" {
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.1.0/24"
  availability_zone = "${data.aws_region.current.name}a"
}
resource "aws_subnet" "public_b" {
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.2.0/24"
  availability_zone = "${data.aws_region.current.name}b"
}
resource "aws_security_group" "lb_sg" {
  name        = "lb-security-group"
  vpc_id      = aws_vpc.main.id
  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  ingress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

# CloudFront ディストリビューション
resource "aws_cloudfront_distribution" "custom_origin_distribution" {
  origin {
    domain_name = aws_lb.example_lb.dns_name # ELBのDNS名をオリジンとして指定
    origin_id   = "My-Custom-Origin-ELB"

    # ここでオリジンへのHTTPS設定を構成
    custom_origin_config {
      http_port              = 80
      https_port             = 443
      origin_protocol_policy = "https-only" # **重要: HTTPS Only に設定**
      origin_ssl_protocols   = ["TLSv1.2"]  # **重要: 最小TLSバージョンを指定**
      # ELBがACM証明書を使用しているため、`origin_read_timeout` などはデフォルトでOK
    }
  }

  enabled             = true
  is_ipv6_enabled     = true
  comment             = "CloudFront distribution for custom origin"
  default_root_object = "index.html" # 必要に応じて設定

  # ビューワー(ユーザー)からの通信をHTTPS Onlyに設定 (推奨)
  default_cache_behavior {
    allowed_methods  = ["GET", "HEAD", "OPTIONS", "PUT", "POST", "PATCH", "DELETE"]
    cached_methods   = ["GET", "HEAD", "OPTIONS"]
    target_origin_id = "My-Custom-Origin-ELB"

    forwarded_values {
      query_string = true
      headers      = ["Origin", "Accept", "Authorization"] # 必要に応じて追加
      cookies {
        forward = "all"
      }
    }

    viewer_protocol_policy = "redirect-to-https" # または "https-only"
    min_ttl                = 0
    default_ttl            = 3600
    max_ttl                = 86400
  }

  # CloudFrontディストリビューションにカスタムSSL証明書を設定
  # この証明書はus-east-1で発行されたACM証明書である必要があります
  # (上記「CloudFront:カスタムSSL/TLS証明書を設定する」のACM証明書定義を参照)
  viewer_certificate {
    acm_certificate_arn      = "arn:aws:acm:us-east-1:123456789012:certificate/abcdef-123-456" # us-east-1のACM証明書ARNに置き換え
    ssl_support_method       = "sni-only"
    minimum_protocol_version = "TLSv1.2_2021"
  }

  restrictions {
    geo_restriction {
      restriction_type = "none"
    }
  }

  price_class = "PriceClass_100"

  tags = {
    Environment = "Production"
  }
}

# 現在のAWSアカウント情報とリージョンを取得
data "aws_caller_identity" "current" {}
data "aws_region" "current" {}

上記のTerraformコードでは、aws_cloudfront_distribution リソースの origin ブロック内で custom_origin_config を設定することで、CloudFrontからカスタムオリジンへのHTTPS通信を強制しています。

  • custom_origin_config: カスタムオリジンに関する設定を定義します。
    • origin_protocol_policy = "https-only": CloudFrontからオリジンへの通信にHTTPSのみを強制します。
    • origin_ssl_protocols = ["TLSv1.2"]: オリジンとの通信に使用する最小TLSバージョンを指定します。 TLSv1.2 以上のバージョンを強く推奨します。
  • aws_lb.example_lbaws_lb_listener.https_listener: オリジンとなるELBがHTTPS(ポート443)でリスニングし、有効なSSL/TLS証明書を使用していることを前提としています。

YOUR_ROUTE53_HOSTED_ZONE_IDapp.example.comarn:aws:acm:us-east-1:123456789012:certificate/abcdef-123-456 などのプレースホルダーは、実際の環境に合わせて修正してください。また、カスタムオリジン側のセキュリティグループがCloudFrontからのインバウンドHTTPSトラフィック(通常443ポート)を許可していることを確認してください。

最後に

この記事では、Amazon CloudFrontディストリビューションからカスタムオリジンへのトラフィックをHTTPSで暗号化することの重要性について解説しました。この設定は、エンドツーエンドのデータセキュリティを確保し、データ傍受や改ざんのリスクを防ぎ、コンプライアンス要件を満たす上で不可欠です。 貴社のCloudFrontディストリビューションからカスタムオリジンへのトラフィックは、HTTPSで安全に保護されていますか?この機会にぜひ設定を確認・強化してみてください。 こちらの内容の検出は弊社が提供するSecurifyのCSPM機能で簡単に検出及び管理する事が可能です。運用が非常にラクに出来る製品になっていますのでぜひ興味がある方はお問い合わせお待ちしております。

最後までお読みいただきありがとうございました。この記事が皆さんの役に立てば幸いです。

この記事をシェアする

クラウドセキュリティ対策実践集一覧へ戻る

貴社の利用状況に合わせた見積もりを作成します。

料金プランを詳しく見る