SSL証明書の設定と管理

中級 | 25分 で読める | 2025.01.10

公式ドキュメント

SSL/TLSとは

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

Let’s Encrypt

ポイント: Let's Encryptは無料でSSL証明書を発行する認証局です。Certbotを使えば証明書の取得から自動更新まで簡単に設定できます

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 "提示された値"

実践メモ: TLSv1.2以上のみを有効にし、古いTLS1.0/1.1は必ず無効化してください。OCSP Staplingも有効にすると、証明書検証のパフォーマンスが向上します。

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
<VirtualHost *:80>
 ServerName example.com
 Redirect permanent / https://example.com/
</VirtualHost>

<VirtualHost *:443>
 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
</VirtualHost>
# モジュール有効化
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証明書は90日で期限切れになります。certbotの自動更新が正しく動作しているか、定期的に確認してください

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

参考リソース

← 一覧に戻る
PR
PR
PR
PR