【実務・中級編】 ALBでサポートされるTLSプロトコルバージョン(TLS 1.0, 1.1, 1.2, 1.3)と暗号スイート – クラウド&コンテナネットワーク実践ガイド

セキュリティと互換性の狭間で:AWS ALBにおけるTLSポリシー設計の「正解」

現場でインフラを預かる我々SREにとって、TLSのバージョンと暗号スイートの選定は、単なる「設定値の変更」以上の重みを持っています。PCI DSSや金融系のセキュリティ要件に直面したとき、あるいはレガシーなクライアントからの接続を断ち切らなければならないとき、ALB(Application Load Balancer)のリスナーポリシーは、我々がコントロールできる最も強力な防壁の一つとなります。

今日は、公式ドキュメントの羅列ではなく、パケットがどのように暗号化され、現場でどのようなトラブルが起きるのか、その「リアルな挙動」に焦点を当てて解説します。

—

1. TLSの握手(ハンドシェイク)で何が起きているのか

TLSの通信は、クライアントとALBが「どの言語(暗号スイート)で会話するか」を決める交渉から始まります。この交渉において、ALBのSecurity Policyが強力なフィルターとして機能します。

1. Client Hello: クライアントが「私はTLS 1.2と1.3を喋れます。暗号スイートはこれらを持っています」と提案する。
2. Server Hello: ALBが設定されたポリシーに基づき、「じゃあ、TLS 1.3のTLS_AES_256_GCM_SHA384でいこう」と合意する。
3. Key Exchange: 鍵を交換し、以降の通信を暗号化する。

ここで最も重要なのは、「クライアントがサポートしていても、ALB側で許可していなければ握手は失敗する(Alert: Handshake Failure)」という点です。特にTLS 1.0や1.1は現代では脆弱性とみなされており、セキュリティ要件が厳しい環境では、これらを排除するELBSecurityPolicy-TLS13-1-2-2021-06のようなモダンなポリシーを選択する必要があります。

—

2. 推奨されるセキュリティポリシーの選び方

AWSが提供するSecurity Policyは数多く存在しますが、迷ったら以下の基準で選んでください。

  • 最強のモダン構成: ELBSecurityPolicy-TLS13-1-2-2021-06
  • TLS 1.3 と 1.2 のみを許可。レガシーなクライアントがいないなら、これがデフォルトの選択肢です。
  • 互換性とのバランス: ELBSecurityPolicy-2016-08
  • TLS 1.0からのサポートが必要な「どうしても古いガラケーや組み込み機器」を切り捨てられない場合の妥協案です。ただし、PCI DSS等の基準ではTLS 1.0/1.1はNGとされることが多いため、注意が必要です。

—

3. 実践:クライアント側からデバッグする

「なぜか接続できない」というアラートを受けたとき、サーバー側のログを見る前に、まずクライアント(手元の端末)からどのプロトコルで接続を試みているかを確認するのが、SREの鉄則です。

openssl を使った確認コマンド

特定のTLSバージョンを指定して、ALBが受け付けるかを確認します。

# TLS 1.2 で接続を試みる
openssl s_client -connect example.com:443 -tls1_2

# TLS 1.3 で接続を試みる
openssl s_client -connect example.com:443 -tls1_3

もし1.0で接続を強制して失敗させたい場合は、-tls1オプションを試してください。ここでHandshake failureが返ってくれば、ALBの設定が機能している証拠です。

—

4. アプリケーションコードからの検証(Python/Fetch API)

フロントエンドやバックエンドからAPIを叩く際、TLSバージョンが低いとALBに拒絶されることがあります。

Python (requestsライブラリ) の場合

標準のrequestsはシステム側のOpenSSLに依存しますが、TLSバージョンを明示的に指定してテストするコードがこちらです。

import ssl
import requests
from requests.adapters import HTTPAdapter
from urllib3.poolmanager import PoolManager

class TLSAdapter(HTTPAdapter):
    def __init__(self, ssl_version=None, **kwargs):
        self.ssl_version = ssl_version
        super().__init__(**kwargs)

    def init_poolmanager(self, connections, maxsize, block=False):
        # TLSバージョンを強制的に指定して接続する
        ctx = ssl.create_default_context()
        ctx.set_ciphers('DEFAULT@SECLEVEL=1')
        ctx.minimum_version = self.ssl_version
        self.poolmanager = PoolManager(num_pools=connections, maxsize=maxsize,
                                       block=block, ssl_context=ctx)

# TLS 1.2 以上を要求するセッションの構築
session = requests.Session()
session.mount('https://', TLSAdapter(ssl.TLSVersion.TLSv1_2))

try:
    response = session.get('https://api.your-domain.com')
    print(f"Status Code: {response.status_code}")
except Exception as e:
    print(f"Connection Failed: {e}")

—

5. SREとしての現場Tips

最後に、運用でよくある落とし穴をいくつか共有しておきます。

1. 「とりあえず全部許可」は厳禁: TLS 1.0を残すと、脆弱性を突く攻撃(POODLEなど)の対象になります。要件が許す限り、ELBSecurityPolicy-TLS13-1-2-2021-06への移行をロードマップに入れてください。
2. 暗号スイートの順序: ALBは「クライアントが提示したスイートの中で、サーバー側で優先度が高いもの」を選びます。ポリシーを選択する際は、Forward Secrecy (FS)(前方秘匿性)をサポートする暗号スイート(ECDHEで始まるもの)が優先されるポリシーを選びましょう。
3. ALBログの活用: 接続エラーが頻発している場合、ALBのアクセスログのssl_protocolとssl_cipherフィールドを確認してください。ここに値が入っていない、あるいは意図しないプロトコルが記録されている場合、そこがトラブルの温床です。

TLSの設定変更は、ときに広範囲に影響を及ぼします。必ず開発環境やステージング環境でopensslコマンドを使い、意図したポリシーが適用されていることを確認してから、本番環境へデプロイしてください。

「動いているから触らない」のではなく、「セキュアにアップデートし続ける」ことこそが、我々エンジニアの誇りです。皆さんの環境が、今日も堅牢なパケットで満たされていることを願っています。

コメント

タイトルとURLをコピーしました