SSL証明書の設定と管理

中級 | 25分 read | 2025.01.10

SSL/TLSとは

SSL/TLSは、クライアントとサーバー間の通信を暗号化するプロトコルです。HTTPS通信に必須であり、データの盗聴・改ざんを防ぎます。

Let’s Encrypt

Let’s Encryptは、無料でSSL証明書を発行する認証局です。Certbotを使って簡単に取得・更新できます。

Certbotのインストール

# Ubuntu/Debian
sudo apt update
sudo apt install certbot

# Nginxプラグイン
sudo apt install python3-certbot-nginx

# Apacheプラグイン
sudo apt install python3-certbot-apache

# CentOS/RHEL
sudo yum install epel-release
sudo yum install certbot python3-certbot-nginx

証明書の取得

# Nginx用(自動設定)
sudo certbot --nginx -d example.com -d www.example.com

# Apache用(自動設定)
sudo certbot --apache -d example.com -d www.example.com

# スタンドアロン(Webサーバー停止が必要)
sudo certbot certonly --standalone -d example.com

# Webroot(Webサーバー実行中に取得)
sudo certbot certonly --webroot -w /var/www/html -d example.com

自動更新の設定

# 更新テスト
sudo certbot renew --dry-run

# cronで自動更新(デフォルトで設定済みの場合が多い)
# /etc/cron.d/certbot
0 0,12 * * * root certbot renew --quiet

# systemdタイマーの確認
sudo systemctl status certbot.timer

ワイルドカード証明書

# DNS認証でワイルドカード取得
sudo certbot certonly \
  --manual \
  --preferred-challenges dns \
  -d "*.example.com" \
  -d example.com

# DNSにTXTレコードを追加
# _acme-challenge.example.com  TXT  "提示された値"

Nginx設定

# /etc/nginx/conf.d/ssl.conf
server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name example.com www.example.com;

    # 証明書ファイル
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    # モダンなTLS設定
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;

    # セッション設定
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 1d;
    ssl_session_tickets off;

    # OCSP Stapling
    ssl_stapling on;
    ssl_stapling_verify on;
    ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
    resolver 8.8.8.8 8.8.4.4 valid=300s;
    resolver_timeout 5s;

    # セキュリティヘッダー
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;

    root /var/www/html;
    index index.html;
}

Apache設定

# /etc/apache2/sites-available/example-ssl.conf

    ServerName example.com
    Redirect permanent / https://example.com/



    ServerName example.com

    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem

    # モダンな設定
    SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
    SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256
    SSLHonorCipherOrder off

    # HSTS
    Header always set Strict-Transport-Security "max-age=63072000"

    DocumentRoot /var/www/html

# モジュール有効化
sudo a2enmod ssl
sudo a2enmod headers
sudo a2ensite example-ssl.conf
sudo systemctl restart apache2

Node.js / Express

import https from 'https';
import fs from 'fs';
import express from 'express';

const app = express();

const options = {
  key: fs.readFileSync('/etc/letsencrypt/live/example.com/privkey.pem'),
  cert: fs.readFileSync('/etc/letsencrypt/live/example.com/fullchain.pem'),
};

// HTTPSサーバー
https.createServer(options, app).listen(443);

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

セキュリティチェック

SSL Labs テスト

# オンラインテスト
# https://www.ssllabs.com/ssltest/

# コマンドラインツール
# sslyze
pip install sslyze
sslyze example.com

# testssl.sh
git clone https://github.com/drwetter/testssl.sh.git
./testssl.sh/testssl.sh example.com

OpenSSLでの確認

# 証明書情報の確認
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null | openssl x509 -text -noout

# 有効期限確認
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null | openssl x509 -noout -dates

# 証明書チェーン確認
openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null

# プロトコル・暗号スイート確認
openssl s_client -connect example.com:443 -tls1_3

よくあるエラーと対処

証明書チェーンエラー

# 中間証明書が不足している場合
# fullchain.pemを使用しているか確認
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;

証明書の更新失敗

# ポート80が使用中
sudo lsof -i :80

# Webroot認証の確認
# .well-known/acme-challenge/ へのアクセスを確認
location ^~ /.well-known/acme-challenge/ {
    root /var/www/html;
}

Mixed Content警告

<!-- HTTPリソースをHTTPSに変更 -->
<img src="https://example.com/image.jpg" />

<!-- または相対パス/プロトコル相対URL -->
<img src="/image.jpg" />
<img src="//example.com/image.jpg" />

クラウドサービスでのSSL

AWS (ACM)

# AWS Certificate Manager で無料SSL証明書
# CloudFront/ALB/API Gatewayで使用可能
aws acm request-certificate \
  --domain-name example.com \
  --validation-method DNS

Cloudflare

# Cloudflare設定
SSL/TLS:
  encryption_mode: Full (strict)
  always_use_https: true
  min_tls_version: "1.2"

監視と更新通知

# 証明書期限監視スクリプト
#!/bin/bash
DOMAIN="example.com"
EXPIRY=$(echo | openssl s_client -servername $DOMAIN -connect $DOMAIN:443 2>/dev/null | openssl x509 -noout -enddate | cut -d= -f2)
EXPIRY_EPOCH=$(date -d "$EXPIRY" +%s)
NOW_EPOCH=$(date +%s)
DAYS_LEFT=$(( ($EXPIRY_EPOCH - $NOW_EPOCH) / 86400 ))

if [ $DAYS_LEFT -lt 30 ]; then
  echo "Warning: $DOMAIN certificate expires in $DAYS_LEFT days"
fi

関連記事

まとめ

SSL証明書はWebセキュリティの基本です。Let’s Encryptで無料証明書を取得し、自動更新を設定しましょう。定期的なセキュリティチェックも重要です。

← Back to list