LESSON 40分

ストーリー

高橋アーキテクト
データを暗号化して保存しても、通信経路で傍受されたら意味がない

高橋アーキテクトがネットワーク図を広げました。

高橋アーキテクト
ブラウザからサーバー、サーバーからDB、サービス間。全ての通信経路を保護する必要がある。TLS、mTLS、そして証明書の管理について学ぼう

TLS(Transport Layer Security)

TLSは、通信を暗号化して盗聴・改ざんを防ぐプロトコルです。

TLSハンドシェイクの流れ

sequenceDiagram
    participant C as クライアント
    participant S as サーバー

    C->>S: ClientHello(対応する暗号スイート一覧)
    S->>C: ServerHello(選択した暗号スイート + 証明書)
    C->>S: 鍵交換(プリマスターシークレット)
    Note over C,S: 共通鍵で暗号化通信開始

TLS設定のベストプラクティス

import https from "https";
import fs from "fs";

const server = https.createServer(
  {
    // 証明書の設定
    key: fs.readFileSync("/etc/ssl/private/server.key"),
    cert: fs.readFileSync("/etc/ssl/certs/server.crt"),
    ca: fs.readFileSync("/etc/ssl/certs/ca.crt"),

    // TLS 1.2以上のみ許可
    minVersion: "TLSv1.2",

    // 安全な暗号スイートのみ使用
    ciphers: [
      "TLS_AES_256_GCM_SHA384",
      "TLS_CHACHA20_POLY1305_SHA256",
      "TLS_AES_128_GCM_SHA256",
      "ECDHE-ECDSA-AES256-GCM-SHA384",
      "ECDHE-RSA-AES256-GCM-SHA384",
    ].join(":"),

    // サーバーの暗号スイート優先順位を使用
    honorCipherOrder: true,
  },
  app,
);

// HTTP → HTTPS リダイレクト
import http from "http";
http.createServer((req, res) => {
  res.writeHead(301, { Location: `https://${req.headers.host}${req.url}` });
  res.end();
}).listen(80);

mTLS(mutual TLS)

mTLSは、サーバーだけでなくクライアントも証明書で認証する方式です。マイクロサービス間通信のゼロトラストに不可欠です。

// mTLSサーバーの設定
const mtlsServer = https.createServer(
  {
    key: fs.readFileSync("/etc/ssl/private/server.key"),
    cert: fs.readFileSync("/etc/ssl/certs/server.crt"),
    ca: fs.readFileSync("/etc/ssl/certs/ca.crt"),

    // クライアント証明書を要求
    requestCert: true,
    // クライアント証明書の検証を強制
    rejectUnauthorized: true,
  },
  (req, res) => {
    // クライアント証明書の情報を取得
    const clientCert = req.socket.getPeerCertificate();
    const clientService = clientCert.subject.CN; // Common Name

    console.log(`Authenticated service: ${clientService}`);
    // サービス名に基づいて認可チェック
  },
);

// mTLSクライアントの設定
import axios from "axios";

const mtlsClient = axios.create({
  httpsAgent: new https.Agent({
    cert: fs.readFileSync("/etc/ssl/certs/client.crt"),
    key: fs.readFileSync("/etc/ssl/private/client.key"),
    ca: fs.readFileSync("/etc/ssl/certs/ca.crt"),
  }),
});

// サービス間通信
const response = await mtlsClient.get("https://internal-service:8443/api/data");

証明書管理

証明書のライフサイクル

フェーズ説明自動化
発行CA から証明書を取得Let’s Encrypt + certbot
配布サーバーに証明書を配置cert-manager (K8s)
監視有効期限を監視アラート設定
更新有効期限前に自動更新自動更新スクリプト
失効侵害時に証明書を無効化CRL / OCSP
// 証明書の有効期限監視
import tls from "tls";

const checkCertExpiry = async (hostname: string, port: number = 443) => {
  return new Promise<{ daysRemaining: number; expiryDate: Date }>((resolve, reject) => {
    const socket = tls.connect(port, hostname, { servername: hostname }, () => {
      const cert = socket.getPeerCertificate();
      const expiryDate = new Date(cert.valid_to);
      const now = new Date();
      const daysRemaining = Math.floor(
        (expiryDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24),
      );

      socket.end();
      resolve({ daysRemaining, expiryDate });
    });

    socket.on("error", reject);
  });
};

// 定期的な証明書チェック
const monitorCertificates = async () => {
  const hosts = ["api.example.com", "auth.example.com", "db.example.com"];

  for (const host of hosts) {
    const { daysRemaining, expiryDate } = await checkCertExpiry(host);

    if (daysRemaining < 30) {
      await sendAlert({
        severity: daysRemaining < 7 ? "critical" : "warning",
        message: `Certificate for ${host} expires in ${daysRemaining} days (${expiryDate.toISOString()})`,
      });
    }
  }
};

セキュリティヘッダー

通信の保護にはHTTPセキュリティヘッダーも重要です。

import helmet from "helmet";

app.use(helmet());

// 個別のヘッダー設定
app.use(helmet.hsts({
  maxAge: 31536000,        // 1年間HTTPS強制
  includeSubDomains: true,
  preload: true,
}));

app.use(helmet.contentSecurityPolicy({
  directives: {
    defaultSrc: ["'self'"],
    scriptSrc: ["'self'"],       // 外部スクリプト禁止
    styleSrc: ["'self'", "'unsafe-inline'"],
    imgSrc: ["'self'", "data:", "https:"],
    connectSrc: ["'self'", "https://api.example.com"],
    frameSrc: ["'none'"],        // iframe禁止
    objectSrc: ["'none'"],
  },
}));

// 追加のセキュリティヘッダー
app.use((req, res, next) => {
  res.setHeader("X-Content-Type-Options", "nosniff");
  res.setHeader("X-Frame-Options", "DENY");
  res.setHeader("Referrer-Policy", "strict-origin-when-cross-origin");
  res.setHeader("Permissions-Policy", "camera=(), microphone=(), geolocation=()");
  next();
});

通信保護の全体像

経路保護方式
ブラウザ → APITLS 1.2+ (HTTPS)
API → API(内部)mTLS
API → データベースTLS + 認証
API → 外部サービスTLS + APIキー
CI/CD → 本番mTLS + VPN

まとめ

ポイント内容
TLS通信の暗号化、TLS 1.2以上を使用
mTLSクライアントも証明書で認証、サービス間通信に必須
証明書管理自動更新と有効期限監視が重要
セキュリティヘッダーHSTS, CSP 等でブラウザの防御を強化

チェックリスト

  • TLSハンドシェイクの流れを理解した
  • mTLSの仕組みと用途を把握した
  • 証明書のライフサイクル管理を理解した
  • セキュリティヘッダーの設定方法を把握した

次のステップへ

次は演習です。ここまで学んだデータ保護の知識を使って、包括的なデータ保護設計を行いましょう。


推定読了時間: 40分