【実務・中級編】 脆弱な暗号スイート(TLS 1.0/1.1, RC4, 3DES)の無効化 – オフェンシブセキュリティ & リバースエンジニアリング防御ガイド

暗号化の「賞味期限」はとっくに切れている:TLS 1.0/1.1を今すぐ殺すべき理由

現場でインシデント対応をしていると、いまだに「互換性のために」という甘い言葉でTLS 1.0や1.1を有効にしているサーバーに出くわす。これがどれほど危険なことか。攻撃者にとって、TLS 1.0/1.1が有効なサーバーは「通り魔を待つ無防備な路地裏」と同じだ。

今日は、なぜ古い暗号スイートが現代の脅威に対して無力なのか、そしてどうやって「鉄壁」の構成に書き換えるのかを、現場の視点で叩き込む。

なぜ「古い暗号」は攻撃者に愛されるのか

TLS 1.0/1.1には、もはやパッチで防げない「設計上の欠陥」が存在する。代表的なのが BEAST や POODLE といった攻撃手法だ。

特に恐ろしいのは、これらが中間者攻撃(MITM)を前提としていることだ。攻撃者は、ユーザーとサーバーの間に割って入り、暗号化された通信をリアルタイムで解読・改ざんする。通信を「ただの通過点」と考えているエンジニアは甘い。パブリックWi-Fiや脆弱なルーターを介した通信において、あなたのアプリのセッションクッキーが盗まれるのは一瞬だ。

さらに、RC4 や 3DES といったアルゴリズムは、計算機パワーが向上した現代では数時間から数日で解読される「オモチャ」に過ぎない。これらを使っている時点で、暗号化は「気休め」にしかなっていないと自覚してほしい。

攻撃の現場:MITMによるセッションハイジャック

想像してほしい。攻撃者は無料のWi-Fiスポットでパケットをキャプチャし、サーバーが古いプロトコルを許容していることを検知する。次に、通信をTLS 1.0に強制ダウングレードさせ、暗号化の脆弱性を突いてセッションIDを抽出する。

結果、攻撃者は正規ユーザーになりすまして管理画面にログインする。サーバー側には「正規のIPアドレス」からのアクセスとして記録されるため、ログだけでは不正アクセスだと気づくことすら難しい。これが、古い暗号スイートを放置する代償だ。

「鉄壁」のNginx設定:ここをコピペせよ

インフラエンジニア諸君、設定ファイルに迷う時間は無駄だ。以下の設定は、現代のベストプラクティスに基づいた「TLS 1.2/1.3のみ許可」「PFS(Perfect Forward Secrecy)強制」「脆弱な暗号スイート排除」を網羅した構成だ。

# /etc/nginx/conf.d/ssl.conf などのサーバー設定ブロックに記述

# TLS 1.0/1.1は無効化し、1.2/1.3のみを許可
ssl_protocols TLSv1.2 TLSv1.3;

# 脆弱な暗号スイートを排除し、PFSを優先する
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;

# サーバー側で暗号スイートの優先順位を強制する(これがないとクライアント任せになる)
ssl_prefer_server_ciphers on;

# セッションチケットの無効化(PFSを完璧にするため)
ssl_session_tickets off;

アプリケーション開発者が気をつけるべきこと

サーバー側だけを固めても、アプリケーション側の通信がザルでは意味がない。例えば、バックエンドから外部APIを叩く際、cURL や Requests ライブラリでプロトコルの指定を忘れていないだろうか。

Pythonの requests ライブラリでAPIを叩く場合も、明示的にTLSバージョンを指定する癖をつけよう。

import requests
import ssl
from requests.adapters import HTTPAdapter

# TLS 1.2以上を強制するためのアダプター
class TLSAdapter(HTTPAdapter):
    def init_poolmanager(self, *args, **kwargs):
        context = ssl.create_default_context()
        context.set_ciphers('ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256')
        context.minimum_version = ssl.TLSVersion.TLSv1_2
        kwargs['ssl_context'] = context
        return super(TLSAdapter, self).init_poolmanager(*args, **kwargs)

# セッションに適用
session = requests.Session()
session.mount('https://api.vulnerable-server.com', TLSAdapter())

まとめ:セキュリティは「削る」ことから始まる

セキュリティ対策というと、何か新しいツールを導入することばかり考えがちだが、最も強力な対策は「不要なものを削る(無効化する)」ことだ。

TLS 1.0/1.1を殺すことで、「古い端末が使えなくなる」という苦情が来るかもしれない。しかし、その苦情は「サイバー攻撃からユーザーを守った勲章」だと思ってほしい。

まずは明日、自社のサーバーが nmap --script ssl-enum-ciphers -p 443 <target> を叩いたときに何と答えるか確認してみてくれ。もしそこにTLS 1.0の文字が見えたら、それはもう火事と同じだ。今すぐ消火活動(設定変更)に取り掛かってほしい。

コメント

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