タグ: SSL Page 1 of 2

ネイキッドドメインでSSL証明書を取得したかどうかを確認するための手順

概要

筆者はletsencryptでワイルドカード証明書を取得していますが、

*.example.com

の証明書を取得して、

example.com

の、「サブドメインが付かないドメインそのもの」 (hoge.example.com / foo.example.com / supercalifragilisticexpialidocious.example.com等を 含まない ドメイン)、いわゆるネイキッドドメインでの証明書を取っていないかどうかの確認手順です。

重要な仕様

割と躓きやすい仕様ですが、

*.example.comは、example.comを含みません。

ワイルドカード証明書は「何か」.example.comにマッチする証明書。つまり、この「何か」が空文字にならないルールです。

筆者が過去の記事でletsencryptで証明書を作成する際

certbot certonly --manual \
    --preferred-challenges dns \
    --server https://acme-v02.api.letsencrypt.org/directory \
    -m あなたの有効なメールアドレス@example.com \
    -d "*.example.com" \
    -d "example.com"

と、

  • -d "*.example.com"
  • -d "example.com"

のように、ワイルドカードとネイキッドドメインの両方を含めているのはまさにその理由です。

これを忘れたことによるありがちなエラー

*.example.com だけ取得

https://example.com にアクセス

証明書エラー

「え、ワイルドカードなのに!?」

という事態が多く発生します。

ネイキッドドメインの証明書を取得したか

以下の2つのコマンドを用います。

Let's encryptを利用している場合

非常に簡単です。

sudo certbot certificates

と実行するだけ。

 Certificate Name: example.com
    Serial Number: UID]
    Key Type: ECDSA
    Identifiers: *.example.com example.com

のように、Identifiersの欄に.*がついているとついていないドメインがついていればOKです。

Let's encryptを利用していない場合

openssl x509 -in /path/to/certs/directory/cert.pem -noout -text | grep -A1 "Subject Alternative Name"

と実行。(証明書の場所は合わせます)

X509v3 Subject Alternative Name: 
DNS:*.example.com, DNS:example.com

のように、DNSの欄に.*がついているとついていないドメインがついていればOKです。

以上、実務で予め両方の証明書を取得していれば「なんてことない」仕様ですが、これを取得していない場合のSSL再取得の手間はかなり手間です。

Matomo、強制SSL接続の実施

環境

  • Matomo 5.7.0
  • PHP-FPM8.3
  • Ubuntu 24.04

を利用中、Matomoの管理画面で

安全な SSL 接続のみで Matomo を使用することをお勧めします。 Http で安全でないアクセスを防ぐには、Matomo config / config.ini.php ファイルの General セクションに force_ssl = 1 を追加します。

と警告があったので、これに対処したときのメモです。

そもそもの問題として:SSL化することの意義

通信の盗聴と改ざんの防止(中間者攻撃対策)

HTTP(暗号化されていない接続)を利用している場合、ブラウザとサーバー間の通信内容はすべてプレーンテキスト(平文)で流れます。

  • ログイン情報の保護:
  • 管理画面にログインする際のユーザー名やパスワードがネットワーク上で丸見えになります。
  • 解析データの保護:
  • 収集しているアクセスログやレポートデータが第三者に盗み見られるリスクがあります。
  • 改竄防止:
  • 通信の途中でJavaScriptを注入されるなどの改竄を防ぎ、Matomoの正確な動作を保証します。

Cookie(セッション情報)の安全な送信

Matomoは管理者のログイン状態を保持するためにCookieを使用します。

  • セッションハイジャックの防止:
    • HTTPSを強制しない場合、ログイン状態を証明する「セッションID」が暗号化されずに送信される可能性があります。これが攻撃者に盗まれると、パスワードを知らなくても管理者としてログイン(なりすまし)されてしまいます。
  • Secure属性の有効化:
    • SSLを強制することで、ブラウザに対して「このCookieはHTTPS通信の時のみ送信する」という指示(Secure属性)が正しく機能するようになります。

ブラウザのセキュリティ仕様への対応

近年のWebブラウザ(Chrome, Firefox, Safariなど)は、セキュリティを重視しており、HTTP接続に対して厳格な制限を設けています。

  • SameSite属性の制限: 多くのブラウザでは、Cookieの SameSite=None 属性を利用する場合、Secure 属性(=HTTPS)が必須となっています。SSLを強制しないと、Matomoのトラッキングコードが正しく動作しなかったり、ログインが維持できなかったりする不具合の原因になります。
    -「保護されていない通信」の警告: HTTPでアクセスするとブラウザのアドレスバーに警告が表示され、ユーザーや管理者に不安を与えます。

リファラ(参照元)情報の保持

Webサイトの計測において重要な「どこから来たか」という情報(リファラ)は、「HTTPSのページからHTTPのページへ移動する際」にはセキュリティ上の理由でブラウザによって削除されることが一般的です。

もしMatomoのサーバーがHTTPで動作していると、HTTPS化されたサイトからの流入元データが取得できず、解析精度が著しく低下します。

筆者のサイトでは既に、apacheのバーチャルホスト設定で

<VirtualHost _default_:80>
ServerName hoge.example.com
 RewriteEngine On
        RewriteCond %{HTTPS} off
        RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
</VirtualHost>

としていますが、念には念を入れます。

大前提

「ドメインに即した証明書を取得していること」

が必要です。(設定メモ

さっくりとした手順

  1. matomoの.confファイルのバックアップを取ります。
  2. matomoの.confファイルの編集を行います。
  3. 設定を反映します。

matomoの.confファイルのバックアップ

  • バックアップディレクトリへコピー
sudo cp -pi /path/to/matomo/config/config.ini.php /path/to/backup/directory/conf_backup/config.ini.php.$(date +%Y%m%d)

`

.confの格納場所は自分の環境に合わせます。(筆者環境/home/www-data/matomo)

  • バックアップ確認
diff -u /path/to/backup/directory/conf_backup/config.ini.php.$(date +%Y%m%d) /path/to/matomo/config/config.ini.php

差分が無ければバックアップ成功です。

  • 再掲:なぜこの方法をとるのか?

「バックアップとオリジナルの両方があることが一発で分かるから」です。

cp -pi として、 ls -lを行うよりも確実です。

そして、この時点で差分があった場合は「これから修正するファイルを間違えた」というまたとない確認手段になります。(もしくはリアルタイムで改竄を受けているか、不思議なことが起こっているか)

matomoの.confファイルの編集

/path/to/matomo/config/config.ini.phpを、任意の方法で(教義・信仰に沿ったエディタで)編集します。

 [General]

セクションの下、trusted hostsあたりの直下に

force_ssl = 1

を追記して保存します。

  • 編集確認
diff -u /path/to/backup/directory/conf_backup/config.ini.php.$(date +%Y%m%d) /path/to/matomo/config/config.ini.php

以下のような結果が得られれば成功です。

 [General]
 salt = "UUID"
 trusted_hosts[] = "Your Domain"
+force_ssl = 1

このように、diffの元と先をcpの逆にすることは、編集したところが+で表示される視覚的な安心感を生みます。

設定反映

  • apache構文確認
sudo apache2ctl configtest

SyntaxOKを確認します。それ以外のエラーが起きていたら、編集時に誤りがあるので確認しましょう。

  • apache再開前確認
systemctl status apache2.service

active(running)を確認します

  • apache再開
sudo systemctl restart apache2.service
  • apache再開確認
systemctl status apache2.service

active(runnning)を確認します

  • php-fpm再開前確認
systemctl status php8.3-fpm.service 

active(running)を確認します

  • php-fpm再開
sudo systemctl restart php8.3-fpm.service 
  • php-fpm再開確認
systemctl status php8.3-fpm.service 

active(runnning)を確認します

設定反映確認

  1. 設定を行ったmatomoサイトに管理者権限でログインします。
  2. 設定 > システムの確認 > 完全なシステムチェックレポートを表示する をクリックします。
  3. 強制 SSL 接続にチェックが入っていれば設定完了です。

Matomo連携時にハマったこと。(連携手順とSSL設定)

概要

オープンソースのWeb解析、

WordPressとMatomoを別々のサーバーで運用し、wp-matomo(現在の名称は「Connect Matomo」)を使用して連携させる手順を整理しました。

この設定により、WordPress側で収集したデータを別サーバーのMatomoへ送信し、WordPressの管理画面内でアクセス統計を確認できるようになります。

環境

Matomo側

  • Ubuntu 24.04
  • Apache 2.4
  • Mod Security v2

WordPress専用サーバ

  • Connect Matomoプラグイン

さっくりとした手順

  1. Matomo サーバでセキュリティトークンを発行します。
  2. WordPressのプラグインを設定します。

1. Matomoサーバー側での準備

まず、データの受け皿となるMatomo側で「接続許可証(トークン)」と「サイトID」を確認します。

  1. Matomoにログインします。
  2. サイトの追加: まだWordPressサイトを登録していない場合は、[管理(歯車アイコン)] > [ウェブサイト] > [管理] から「新しいウェブサイトを追加」でWordPressサイトを登録します。この際表示される 「サイトID」(通常は1, 2…といった数字)をメモしておきます。
  3. Authトークンの発行:
    [管理(歯車アイコン)] > [個人] > [セキュリティ] を開きます。
    ページ下部の 「認証トークン(Auth tokens)」 セクションで「新しいトークンを作成」をクリックします。
    パスワードを入力し、用途(例: WP-Connection)を入力して作成します。
    表示された 長い英数字の文字列(トークン) をコピーして控えておきます。

注意
※注意
トークンは一度しか表示されません。必ずこのタイミングでコピーしてください。

2. WordPress側での設定

次に、WordPressにインストールしたプラグインにMatomoの情報を入力します。

  1. 設定画面を開く: WordPress管理画面の [設定] > [WP-Matomo](または Connect Matomo)を開きます。
  2. 接続設定(Matomoへ接続o)タブ:
  • Matomo モード: 「自己ホスト (HTTP API, デフォルト)」を選択します。
  • Matomo URL: MatomoがインストールされているサーバーのURLを入力します(例: https://analytics.example.com/)。末尾に / を含めてください。
  • 認証トークン: 先ほどMatomo側でコピーしたトークンを貼り付けます。
  1. 変更を保存: 「変更を保存」をクリックします。
  • 接続に成功すると、上部に緑色のメッセージが表示され、「Select site」 ドロップダウンからMatomoに登録したサイト名が選べるようになります。

3. トラッキングの有効化

接続しただけでは計測が始まらない場合があるため、以下の設定を確認します。

  1. トラッキングを有効化タブ をクリックします。
  2. トラッキングコードを追加: 「デフォルトのトラッキング」を選択します。これにより、WordPressの各ページに自動で計測タグが挿入されます。
  3. 変更を保存 をクリックして完了です。

よくあるトラブルと確認事項

別サーバー構成でうまく動かない場合は、以下をチェックしてください。

-URLの正確さ: Matomo URLが httphttps か、またサブディレクトリ(/matomo/ など)にインストールしていないか再確認してください。

  • ファイアウォール: WordPressサーバーからMatomoサーバーへの通信(ポート80または443)が許可されているか確認してください。
  • キャッシュ: WordPress側でキャッシュプラグインを使用している場合、設定反映後に一度キャッシュをクリアしてください。

自分がドハマリしたポイント

接続に失敗し、その接続ログが出てこない事態も発生しました。

その答えは、筆者が設定したapacheの.confファイルに入りました。

SSL設定を、筆者の標準の

SSLProtocol             all -SSLv3 -TLSv1 -TLSv1.1 -TLSv1.2

から、以下の形で修正。

SSLProtocol -ALL +TLSv1.2 +TLSv1.3

これにより通信ができるようになりました。これには以下の理由があります。

1. 通信プロトコルの不一致(ハンドシェイクの失敗)

当初の設定: SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1 -TLSv1.2 (意訳:TLS 1.3 のみを許可し、それ以外をすべて禁止する)

修正後の設定: SSLProtocol -ALL +TLSv1.2 +TLSv1.3 (意訳:TLS 1.2 と 1.3 の両方を許可する)

WordPressサーバーがMatomo APIにアクセスする際、内部ではPHPの cURL や openssl ライブラリが使用されます。もしWordPress側のOSやライブラリのバージョンが少し古い場合、あるいは最新であってもクライアント側の設定が TLS 1.2 までしか対応していない場合、Matomoサーバーが「TLS 1.3以外は受け付けない」という設定になっていると、共通の通信言語が見つからず、接続が即座に遮断されます。

2. PHP / cURL / OpenSSL の依存関係

WordPress(PHP)が外部サーバーと通信する際、バックグラウンドではシステム標準のOpenSSLライブラリが動いています。

TLS 1.3 を利用するには、OpenSSL 1.1.1以上が必要です。

もしWordPress側のサーバーOSが少し前の世代(例:Ubuntu 18.04やCentOS 7など)であったり、PHPのコンパイル環境が古い場合、TLS 1.3でのハンドシェイクを完遂できず、TLS 1.2での接続を試みようとします。

このとき、Matomo側が -TLSv1.2(1.2禁止)としていると、WordPress側は「接続できるプロトコルがない」と判断し、エラーを出します。

3. 「接続ログが出ない」という現象の理由

Apacheのアクセスログ(access.log)には、HTTPリクエストが成功または失敗した記録が残ります。しかし、SSL/TLSのハンドシェイク(暗号化の交渉)は、HTTPリクエストが送られる前段階で行われます。

TLS 1.2が禁止されている場合: 通信路を確立する前の「挨拶(ハンドシェイク)」の時点でサーバー側が「そのプロトコルは使えません」と接続を切断します。

こういうとき、以下のコマンドが有効です。

openssl s_client -connect analytics.example.com:443 -tls1_2

もし、このコマンドを実行して CONNECTED(00000003) と表示され、その後に証明書の情報がズラッと出てくれば、「インフラ(OS/ネットワーク/Apache)の土台は完璧である」という証明になります。

逆にここでエラーが出るなら、WordPressの設定(プラグイン)をいくらいじっても解決しないということが一瞬で分かります。

そのため、Apacheは「Webサイトへのアクセス」として認識する前に通信が終了してしまい、通常のアクセスログには一行も記録されないという事態が起こった話。

厳格な設定が裏目に出たという話でした。

「インターネット接続をする際のApache設定」の常時SSL化とログ設定の手順(長文)

なぜ2020年代から(或いはその5年前から)常時SSL化が必須と言えるのか?

セキュリティの確保

  • 通信の暗号化
    • SSL/TLSの技術により、Webブラウザ~サーバ間の通信が暗号化。第三者による盗聴や改ざんを防ぎます。
  • 入力情報の保護
    • ログイン情報、個人情報、問い合わせフォームの内容など、機密性の高い情報のやりとりが安全になります。

ユーザーの信頼性向上

  • ブラウザの警告表示への対処
    • 2020年代より、Chrome/Safari/Edgeと言った主要なブラウザは従来のhttp通信を「保護されていない通信」としてブラウザに表示するようにしました。これは読者、アクセス者に対してユーザの不安を招きます。
  • 攻撃者にとっての絶好の餌を与えない
    • HTTP通信の状態であるというのは、攻撃者にとっての「オイシい」獲物です。ログイン情報やセキュリティの脆弱性などをより好んで狙うという状況を防ぎます。

(筆者には余り関係ないが) SEOへの影響

  • Googleを筆頭とする各種検索エンジンは、2014年以降、SSL対応を検索上位の評価要因に含めています。
  • HTTPS化されたサイトはSEOで有利になります。

Web標準としての定着

  • HTTPS by default機能
    • Chrome/Edge/ChromiumなどはHTTPSを標準接続とする仕様を導入。これにより、HTTPサイトはますます排除される傾向になります。

企業・サービスの信頼性維持

  • SSL化はもはや企業サイトの基本マナーとされています。
    • 未対応のままでは信頼性を損なう可能性があります。

余談 (私怨かつ直接的な動機)未対応企業への溜飲

  • 強烈なパワハラを喰らい去ることになった会社が「辞めてから10年経ってもHTTP通信のまま」というIT企業の存在は、「あんたは未だにHTTPS化をしていないわけ?」という精神的な勝利に繋がっています。
    • 器が小さいとかみみっちいと言われていても、かなり重要な動機です。

ログが重要な理由

トラブルシューティングが容易になる

  • 最も重視する項目です。先般の「記憶に頼るな。ログを信じろ」の全ての根拠です。
    • 特定のサイトでエラーが発生した場合、該当サイトのエラーログだけを確認できるため、原因特定が迅速になります。
  • バーチャルサイトごとにログがないと以下を招きます。
    • ノイズが多いことによる調査効率の低下。
    • 不審なアクセスの迅速な検知の非効率化。

セキュリティサービスとの連携

  • WAF連携
    • 筆者が用いているModSecurityの連携は、ひとえに「この、エラーログ・アクセスログ」を根拠にセキュリティの強化や利便性強化を図っています。
  • 攻撃のトレンドを知る
    • どのような攻撃があるからサーバはパフォーマンスが落ちているのか? を知る上でもこれは重要です。

サイトごとのアクセス解析が可能/容易になる

  • 複数のドメインやサービスを1台のサーバで運用している場合、ログを分けることで書くサイトのアクセス状況を個別に把握できます。
    • 例えば「example.com」と「sample.jp」のPV数や人気ページを比較したいとき、ログが分かれていないというのは分析が困難になります。

セキュリティ監査に対応しやすい

  • サイトごとにログを分けることで、不正アクセスや改竄の痕跡を個別に追跡可能です。
    • 仮に、何らかの法的なインシデントに巻き込まれたとしても、そのログを元に、迅速な対応が可能になります。

読者への回答

「これだけ長いのに何故2つに分けないのか?」は

  • SSL化
  • ログの設定

は、先に示した「加害者にならないための具体的手順」の1つ。「この両者は不可分であり、インターネットで公開する上では必須」だからです。

  • SSL化という2020年代セキュリティの標準
  • ログの可視化というインターネット黎明期から続くトラブルシューティングと解析

は絶対に必要な項目です。次ページから具体的な手順です。

Google Geminiによるコード修正。(SSL証明書の有効期限確認)

このスクリプトの改善案をGoogle Geminiに言ったところ、更に完成度が上がりました。

概要

Web運用において、「サーバのSSL証明書更新」をチェックすることは大切です。

適切に更新されていない/更新が遅れたまま放置すると

  • ブラウザで「危険なサイト」と認識される
  • それによるレピュテーションリスク
  • HSTSを厳密に設定した場合はサイトそのものへのアクセス不可

など、リスクは甚大です。

そこで、

  • OpenSSLコマンドでドメインに設定されている証明書の期限
  • 並びに残り何日か

を表示するRubyスクリプトを準備しました。

スクリプト内容

  • ssl_checker.rb
#!/usr/bin/env ruby

require 'openssl'
require 'socket'
require 'date'
require 'uri'
require 'timeout'

# 色付け用の定数
COLOR_RED = "\e[31m"
COLOR_YELLOW = "\e[33m"
COLOR_GREEN = "\e[32m"
COLOR_RESET = "\e[0m"

# URLの最終的な到達先を取得するメソッド
def get_effective_url(url)
  # curlを使ってリダイレクトを追いかけ、最終的なURLを取得する
  # -s: サイレント, -L: リダイレクト追従, -I: ヘッダのみ, -o /dev/null: ボディ破棄, -w '%{url_effective}': 最終URLを出力
  effective_url = `curl -sLI -o /dev/null -w '%{url_effective}' "#{url}"`
  effective_url.empty? ? nil : effective_url
end

# 証明書の有効期限を取得するメソッド
def get_certificate_expiry_date(url)
  uri = URI.parse(url)
  hostname = uri.host
  port = uri.port || 443 # ポートがなければ443を使う
  ssl_socket = nil
  tcp_client = nil

  begin
    Timeout.timeout(5) do
      tcp_client = TCPSocket.new(hostname, port)
      ssl_context = OpenSSL::SSL::SSLContext.new
      ssl_socket = OpenSSL::SSL::SSLSocket.new(tcp_client, ssl_context)
      ssl_socket.hostname = hostname
      ssl_socket.connect

      cert = ssl_socket.peer_cert
      expiration_date = DateTime.parse(cert.not_after.to_s)
      days_remaining = (expiration_date - DateTime.now).to_i

      return expiration_date, days_remaining
    end
  rescue Timeout::Error
    return nil, "サーバーへの接続がタイムアウトしました。"
  rescue => e
    return nil, e.to_s
  ensure
    ssl_socket&.close
    tcp_client&.close
  end
end

# 結果を表示するメソッド
def print_result(url, expiration_date, days_remaining)
  if expiration_date
    formatted_date = expiration_date.strftime("%Y/%m/%d")
    
    # 残り日数に応じて色を選択
    color = if days_remaining < 14
              COLOR_RED
            elsif days_remaining < 30
              COLOR_YELLOW
            else
              COLOR_GREEN
            end

    puts "サイト #{url} の有効期限は #{formatted_date} です。#{color}残り #{days_remaining} 日です。#{COLOR_RESET}"
  else
    puts "#{COLOR_RED}サイト #{url} の証明書取得に失敗しました: #{days_remaining}#{COLOR_RESET}"
  end
end

# メイン処理
def main
  # コマンドライン引数があるかどうかで処理を分岐
  domains_to_check = if ARGV.empty?
                       # 引数がない場合は、対話式で入力を受け付ける
                       print "チェックしたいサイトのドメインを入力してください(例: example.com): "
                       [gets.chomp]
                     else
                       # 引数がある場合は、それらを全てチェック対象とする
                       ARGV
                     end

  # 各ドメインをチェック
  domains_to_check.each do |domain|
    # 対話モードで空エンターされた場合などをスキップ
    next if domain.nil? || domain.strip.empty?
    
    # http/httpsから始まらない場合はhttpsを付与
    initial_url = domain.start_with?('http') ? domain : "https://#{domain}"
    
    puts "Checking: #{initial_url} ..."
    final_url = get_effective_url(initial_url)

    if final_url.nil?
      puts "#{COLOR_RED}サイト #{initial_url} にアクセスできませんでした。#{COLOR_RESET}"
      next
    end
    
    expiration_date, days_remaining = get_certificate_expiry_date(final_url)
    print_result(final_url, expiration_date, days_remaining)
  end
end

# メイン処理を呼び出し
main

スクリプト実行例

ruby ssl_checker.rb

※Rubyで動かすためスクリプトに実行権は持たせる必要はありません

  1. 対話式でドメインを入力する
  2. 証明書の有効期限と残り期日を表示する
  3. ruby script.rb www.hoge.comなど、ドメインを引数にしても同様の効果が得られる
  4. 更新が近づくと
    • 30日以内:黄色
    • 15日以内:赤

とアラートも出してくれるようにしています。

  • 対話式
ruby ssl_checker.rb 
チェックしたいサイトのドメインを入力してください(例: example.com): www.yahoo.co.jp
Checking: https://www.yahoo.co.jp ...
サイト https://www.yahoo.co.jp/ の有効期限は 2026/05/14 です。残り 310 日です。
  • 引数にした場合
ruby ssl_checker.rb yahoo.co.jp www.msn.com
Checking: https://yahoo.co.jp ...
サイト https://www.yahoo.co.jp:443/ の有効期限は 2026/05/14 です。残り 310 日です。
Checking: https://www.msn.com ...
サイト https://www.msn.com/ の有効期限は 2025/10/05 です。残り 89 日です。

スクリプト使用例

筆者は/etc/update-motd配下に

ruby /path/to/script/ruby/ssl_checker.rb ryza.jp

と記入することで、ログインのたびに

Checking: https://atelier.reisalin.com ...
サイト https://atelier.reisalin.com/ の有効期限は 2025/08/15 です。残り 37 日です。

と次の更新のタイミングを読みやすくしています。

Ubuntu24.04で、ワイルドカードSSL証明書をLet’s Encrypt(certbot)で発行する手順メモ。

筆者にとってこの作業は当たり前すぎていたため、メモを残すのを失念していました。

概要

Webサイトを公開する際に必須と言っていいSSL証明書を、Let's Encrypt(certbot)を用いて無料で発行する手順です。

今回用意するのはワイルドカード証明書です。

「*.example.com」という証明書を取得することで、

  • aaa.example.com
  • bbb.example.com
  • supercalifragilisticexpialidocious.example.com

など、サブドメインすべてに有効な証明書を1回で発行できます。

実施環境

  • Ubuntu 24.04

前提条件

  • ワイルドカー証明書を発行したいドメインに対してDNSの操作が行えること
    • できない場合は本手順そのものを無視してください。
  • 備考として、別サーバでも配布しやすいように、オリジナルの/etc/letsencrypt/live/ではなく、任意の作業ディレクトリに証明書一式を保存する運用を行っています。
  • 別サーバへの配布の際は注意してください。

さっくりとした手順

  1. certbotをインストールします。
  2. certbotでワイルドカード証明書を発行します。
  3. 証明書を発行します。
  4. 所定の位置に格納します。

certbotインストール

※すでにcertbotがインストール済の場合は、この手順はスキップして構いません。

  • snapによるインストール
sudo snap install --classic certbot
  • コマンド化
sudo ln -s /snap/bin/certbot /usr/bin/certbot
  • コマンド確認
which certbot

/usr/bin/certbotを確認します。

root昇格

性質上、rootで実行します。操作は慎重に行ってください。

sudo su -

作業ディレクトリ作成

  • ディレクトリ作成
mkdir /hoge/example.com_ssl$(date +%Y%m)

任意のディレクトリを指定します。$(date +%Y%m)オプションをつけているのは、Let's Encryptの有効期限が90日間と短いため、定期的に更新するためです。

  • ディレクトリ移動
cd /hoge/example.com_ssl$(date +%Y%m) && pwd

指定したディレクトリにいることを確認します。

ワイルドカード証明書の手動発行

※ ドメイン名やメールアドレスの打ち間違いは特に注意してください。このコマンドをコピー&ペーストし、自分のドメインやメールアドレスで間違いないように確認後に発行してください。

certbot certonly --manual \
    --preferred-challenges dns \
    --server https://acme-v02.api.letsencrypt.org/directory \
    -m あなたの有効なメールアドレス@example.com \
    -d "*.example.com" \
    -d "example.com"
  • --manualモードは非自動化のため、証明書更新のたびに手動でTXTレコード登録が必要です。自動化したい場合はDNS API対応のプラグインを検討してください(例:certbot-dns-cloudflare)。
  • certonly は証明書の取得だけを行い、Webサーバ設定などには手を加えないモードです。

以下のように実施します。

  1. コマンド発行後、TXTレコードの登録指示があります。
  2. 管理しているDNSサーバにて、指示があったTXTレコードを登録します。
  3. 以下のコマンドで結果が返ってくるまでしばらく待ちます。
nslookup -type=TXT _acme-challenge.example.com

→ 結果が返ってきたらEnter。証明書が作成されます。

証明書一式を作業ディレクトリにコピー

  • 証明書を作業ディレクトリにコピー
cp -pi /etc/letsencrypt/live/example.com/fullchain.pem ./example.com.crt.$(date +%Y%m)

※root権限のためsudoを用いないことに注意

  • 秘密鍵を作業ディレクトリにコピー
cp -pi /etc/letsencrypt/live/example.com/privkey.pem ./example.com.key.$(date +%Y%m)

(通例、証明書はroot権限でしか読めないように制限されています。読み取り時は注意してください)

証明書の整合性を確認

  • 90日の有効期限であることを確認します。

以下、自分が発行したドメインに基づく証明書や秘密鍵に読み替えます。

openssl x509 -noout -dates -subject -in example.com.crt.$(date +%Y%m)
notBefore=May 17 04:35:55 2025 GMT
notAfter=Aug 15 04:35:54 2025 GMT

のように90日間であることを確認します。

  • 確認1. 証明書から公開鍵データを確認
openssl x509 -pubkey -in example.com.crt.$(date +%Y%m) -noout | openssl md5
  • 確認2. 秘密鍵から公開鍵を取得
openssl pkey -pubout -in example.com.key.$(date +%Y%m) | openssl md5

→ 確認1/確認2で出てきた公開鍵のハッシュ値が一致していればOKです。

  • 証明書のチェーンを確認
openssl crl2pkcs7 -nocrl -certfile example.com.crt.$(date +%Y%m) | openssl pkcs7 -print_certs -outform PEM | awk 'BEGIN {c=0;} /BEGIN CERTIFICATE/ {c++} { print > "cert" c ".pem"}' && openssl verify -CAfile cert2.pem cert1.pem
openssl verify -CAfile cert2.pem cert1.pem
cert1.pem: OK

となることを確認します。

確認後、

exit

でrootから抜けます。

証明書の配置

※Let's Encryptは3ヶ月(90日)しか有効期限がありません。

そこで、証明書更新の際にApacheの設定ファイルを修正することなく行えるように、証明書/秘密鍵ファイルにシンボリックリンクを張り、ファイル名.202507等とすることで「最後に発行したのはいつか」を確認します。

ディレクトリを作成します。

sudo mkdir /etc/certs

証明書を格納するディレクトリです

sudo mkdir /etc/private

秘密鍵を格納するディレクトリです

ディレクトリに証明書と秘密鍵を格納します。

  • SCPやSFTPでアップロードして対象ディレクトリに配置する
  • Let's Encryptなどで作成したファイルをそれぞれ対象ディレクトリにコピー/移動する

など、適当な方法を用います。(割と効率的なのがcatでファイル内容を開き、それを別サーバに貼り付ける方法です)

秘密鍵格納後、

sudo chmod 600 /etc/private/hoge.sample.com.key.202507

として、アクセス権を厳密にします。

上記、取得した*.example.comの証明書と秘密鍵を

  • /etc/certs (証明書)
  • /etc/private (秘密鍵)

に格納したとして手順を進めます。

証明書のシンボリックファイルを作成します。

cd /etc/certs && pwd

/etc/certsにいることを確認

sudo ln -sf hoge.sample.com.crt.202507 hoge.sample.com.crt
ls -l hoge.sample.com.crt

リンクの向き先がhoge.sample.com.crt.202507であることを確認します。

lrwxrwxrwx 1 root root     31  7月  2 14:35 hoge.sample.com.crt -> hoge.sample.com.crt.202507

秘密鍵のシンボリックファイルを作成します。

cd /etc/private && pwd

/etc/privateにいることを確認

sudo ln -sf hoge.sample.com.key.202507 hoge.sample.com.key
ls -l hoge.sample.com.key

リンクの向き先がhoge.sample.com.crt.202507であることを確認します。

後は、お使いのWebサーバに適用していきます。この方式であれば、

SSLCertificateFile 【/etc/certs/hoge.example.com.crt】
# SSL証明書を指定します
SSLCertificateKeyFile 【/etc/private/hoge.example.com.key】
# 秘密鍵を指定します

とすることで、.confファイルをいじることなく更新作業を行えます。

以下、Redmineでの設定例です。

https://atelier.ryza.jp/projects/zettel/knowledgebase/articles/20

Let’s Encrypt仕様変更によるSSL Staplingの無効化

新たなSSL証明書を導入したサーバにて、以下のエラーを確認。こちらの解決のメモです。

前提

  • Ubuntu 24.04
  • Apache 2.4
  • Let's Encryptによるワイルドカード証明書を発行

エラー内容

サイトのエラーログに以下を発見しました。

[Thu May 22 14:03:51.270340 2025] [ssl:error] [pid 929:tid 929] AH02218: ssl_stapling_init_cert: no OCSP URI in certificate and no SSLStaplingForceURL set [subject: CN=*.ryza.jp / issuer: CN=E6,O=Let's Encrypt,C=US / serial: 058A8F3B6C32F08B41E5E56BEAD92451D34A / notbefore: May 21 12:08:57 2025 GMT / notafter: Aug 19 12:08:56 2025 GMT]
[Thu May 22 14:03:51.270358 2025] [ssl:error] [pid 929:tid 929] AH02604: Unable to configure certificate atelier.ryza.jp:443:0 for stapling 
  • 意味:
  • ApacheがSSL証明書 (CN=*.ryza.jp) のOCSP Staplingを初期化しようとしましたが、証明書内にOCSPレスポンダのURI(OCSP URI)が見つかりませんでした。また、Apacheの設定で SSLStaplingForceURL も指定されていません。
  • OCSP Staplingとは:
  • SSL証明書の失効状態を効率的に確認するための仕組みです。ウェブサーバーが事前にCA(認証局)に問い合わせて証明書の有効性を確認し、その応答(OCSPレスポンス)を証明書と一緒にクライアントに提供することで、クライアント側の確認負荷を軽減し、表示速度を向上させます。

なお、Apacheの.confファイルには以下の項目があります。

SSLUseStapling On
SSLStaplingCache "shmcb:logs/ssl_stapling(32768)"

つまり、ApacheでSSL Stapling設定されているにもかかわらず、OCSPレスポンスが働いていないことを意味します。

状況確認

現行の証明書の状況

openssl x509 -in /path/to/your/certificate.pem -noout -ocsp_uri

結果は何もなし。証明書内にこれがないことで、先のエラーが発生したようです。

過去にLet's Encryptで発行した証明書の状況

2024年9月に発行したLet's Encryptの証明書で

openssl x509 -in /path/to/your/certificate.pem.202409 -noout -ocsp_uri

を実施したところ、http://e6.o.lencr.orgと返ってきました。

導き出される状況

  • 証明書発行時に何らかのエラーが発生して、このURIが欠落した
  • 筆者は他に複数のドメインを利用し、いずれもLet's Encryptで証明書を発行しています。1つだけならいざ知らず、現行全てのドメインでの証明書でこのURLが欠落しているのは一時的なエラーではないと考えました。

そこで、もう一つの可能性として「Let's Encryptの仕様が変わったのでは?」と情報を探していきます。

答えを発見

Removing OCSP URLs from Certificates from Let's Encrypt

Let’s Encrypt will be removing OCSP URLs from certificates on May 7, 2025 as part of our plan to drop OCSP support and instead support certificate revocation information exclusively via CRLs.
Let's Encryptは、2025年5月7日にOCSPのサポートを停止し、代わりにCRLを介してのみ証明書の失効情報をサポートする計画の一環として、証明書からOCSP URLを削除します。

状況そのもののアナウンスを見つけました。この、OCSPのURLが消えた証明書は、いずれも、2025年5月18日以降に発行したものです。完全に時期的に一致。

対処

ここが分かれば、後は設定変更です。Ubuntuサーバで設定を変更します。

  • 設定ファイルバックアップ
sudo cp -pi /etc/apache2/sites-available/virutal.conf /path/to/backup/directory/virutal.conf.$(date +%Y%m%d)

設定ファイルは自分の環境に合わせます。任意のバックアップディレクトリを指定します。

  • ファイルバックアップ確認
diff -u /path/to/backup/directory/virutal.conf.$(date +%Y%m%d) /etc/apache2/sites-available/virutal.conf

差分がなければバックアップ完了です。

  • ファイル編集

任意の方法で(要管理者権限)以下のように編集します。

# 2025年5月のLet's Encryptの方針によりOCSPが廃止されたため
SSLUseStapling Off
#SSLStaplingCache "shmcb:logs/ssl_stapling(32768)"
  • 編集確認
diff -u /path/to/backup/directory/virutal.conf.$(date +%Y%m%d) /etc/apache2/sites-available/virutal.conf
-SSLUseStapling On
-SSLStaplingCache "shmcb:logs/ssl_stapling(32768)"
+# 2025年5月のLet's Encryptの方針によりOCSPが廃止されたため
+SSLUseStapling Off
+#SSLStaplingCache "shmcb:logs/ssl_stapling(32768)"
  • 整合性確認
sudo apache2ctl configtest

Syntax OKを確認します。

  • Apache再起動前確認
systemctl status apache2.service

active(running)を確認します。

  • Apache再起動
sudo systemctl restart apache2.service

※reloadより確実なrestartを採用しています

  • Apache再起動後確認
systemctl status apache2.service

active(running)を確認します。

修正確認

設定を反映させたサイトを適当に閲覧し、エラーログにて、冒頭のエラーがないことを確認しました。

サイトのSSL強化。(HSTSプリロード)

概要

サイトのセキュリティを高めるため、HSTSプリロードを設定したときのメモとなります。

環境

  • OS: Ubuntu 20.04
  • Apache 2.4系
  • Let's Encryptで取得した証明書を利用

前提条件

  1. インターネット上に公開されたサイトであること。(非ローカル環境)
  2. 公開するWebサイトドメイン全てに適切な証明書が設定されていること。
  3. Webサーバの設定で、httpアクセス全てをhttpsに変換する設定になっていること。
  4. 必要なモジュール(header、SSL等)がインストールされていること。

ここでは、hoge.example.com というサイトに対してHSTSプリロードを設定します。

注意点

  • サブドメイン全てに対して適用されます。(hoge.example.comの場合はwww.example.comはもちろん、example.comも対象となることを注意してください)
  • https化されていないサイトに対してもhttps接続を強制します。つまり、他にhttps化されていないシステムに対してもです。
  • プリロードリストに登録された場合、解除はとても難しくなります。相応の運用が試されることに注意してください。

さっくりとした手順

  1. HSTSを有効にします。
  2. httpdサービスを再起動します。
  3. HSTSサイトにドメインを登録します。

実施手順

apacheのバーチャルサイトファイルを修正します。

  • 修正後のファイル

※ドメインやDocumentRoot等は書き換えてください。また、サブドメインごとに複数のサイトがある場合、その全てを修正します。

<VirtualHost _default_:80>
servername hoge.example.com
 RewriteEngine On
        RewriteCond %{HTTPS} off
        RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
</VirtualHost>
<VirtualHost _default_:443>
    servername hoge.example.com
    ErrorLog /var/log/apache/error.log
    CustomLog /var/log/apache/access.log combined

DocumentRoot /var/www/html/hoge

    <Directory /var/www/html/hoge>
        Options -MultiViews
        AllowOverride All
        Require all granted
    </Directory>

  SSLEngine on
    Protocols h2 http/1.1
    Header set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"

SSLCertificateFile /etc/certs/hoge.example.com.crt
SSLCertificateKeyFile /etc/private/hoge.example.com.key

</VirtualHost>

SSLProtocol             all -SSLv3 -TLSv1 -TLSv1.1 -TLSv1.2
SSLCipherSuite          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
SSLHonorCipherOrder     off
SSLSessionTickets       off
  • 主な差分
-    Header always set Strict-Transport-Security "max-age=63072000"
+    Header set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"

-SSLProtocol             all -SSLv3 -TLSv1 -TLSv1.1
+SSLProtocol             all -SSLv3 -TLSv1 -TLSv1.1 -TLSv1.2
  • HSTSのプリロードを有効化しています。
  • ついでに弱いSSL(TLSv1.2)を無効化します。

apacheサービスを再起動します。

sudo apache2ctl configtest
# Syntax OKを確認します
sudo systemctl restart apache2.service

systemctl satus apache2.service
# active(running)を確認します。

HSTSプリロードサイトへの登録

  1. https://hstspreload.org/ にアクセスします。
  2. 上記で設定したドメイン名を入力して登録を進めていきます。(サブドメインは含みません)
  3. エラーがなければStatus: laplacemine.com is pending submission to the preload list.と表示されます。
  4. 2~3週間ほど待って正式に登録されるのを待ちます。

設定確認

  1. https://www.ssllabs.com/ssltest にアクセスします。
  2. HSTSを設定したサイトのドメイン名を入力して診断します。
  3. Strict Transport Security (HSTS) が「YEえs」となっていれば成功です。

Let’s Encryptの証明書整合でハマったこと。(ECDSA方式での整合性確認)

概要

更新サイクルが3ヶ月と短いものの、無料で利用できるということで愛用しているLet's Encrypt。
デフォルトの暗号化形式がRSAからECDSA方式に変わったようで確認方法でハマりました。

なんとか解決したのでメモを残します。

従来の確認方法

RSA方式での証明書は、以下の

openssl x509 -in /etc/certs/hoge.example.com.crt -noout -modulus | md5sum
# SSL証明書ファイル

openssl rsa -in /etc/private/hoge.example.com.key -noout -modulus | md5sum
# 秘密鍵ファイル

を実行し、それぞれのハッシュ値が合致していることで証明書と秘密鍵の整合性の確認を取ることができています。

ECDSA方式に変わったことでの問題

2023年4月時点で、Let's Encryptのデフォルト暗号化形式がRSA→ECDSA方式に変わっていました。

openssl rsa -in /etc/private/hoge.example.com.key -noout -modulus | md5sum
# 秘密鍵ファイル

を実行すると

139945929635648:error:0607907F:digital envelope routines:EVP_PKEY_get0_RSA:expecting an rsa key:crypto/evp/p_lib.c:469:

といったエラーが出ます。

これでは証明書と秘密鍵の整合性が確認できません。

しばらくGoogleと格闘し、なんとか解決策を見つけました。

https://security.stackexchange.com/questions/73127/how-can-you-check-if-a-private-key-and-certificate-match-in-openssl-with-ecdsa

ECDSA方式でも証明書と秘密鍵の整合性が確認できるコマンド

まず、鍵がECDSA方式であることを確認。

  • 確認コマンド
openssl ec -in /etc/private/hoge.example.com.key -text -noout
# 秘密鍵のパスを指定します
  • 確認結果
(略)
ASN1 OID: prime256v1
NIST CURVE: P-256
# 上記が表示されればECDSA方式であると確認できます。

次に、以下のようにして公開鍵を抽出してハッシュ値を割り出します。

openssl x509 -pubkey -in /etc/certs/hoge.example.com.crt -noout | openssl md5
(stdin)= ハッシュ値
# SSL証明書ファイル

openssl pkey -pubout -in /etc/private/hoge.example.com.key | openssl md5
(stdin)= ハッシュ値
# 秘密鍵ファイル

### 2つのハッシュ値が合っていれば証明書と秘密鍵の整合性は取れています

以上、ECDSA方式でも整合性を確認することができました。

余談

この、「鍵を用いて鍵を取り出す」って、『ライザのアトリエ3』で無垢の鍵から秘密の鍵を抽出しているようだなと益体もないことが脳裏をよぎりました。

homebrewを用いないUbuntuへのmkcertのインストール。

前回、homebrewによるローカル証明書(mkcert)を見つけましたが、それよりも楽な方法がありましたので、メモとして残します。

actix-webでSSL/TLS通信をしてみたよ

https://qiita.com/yoshiyasu1111/items/f22a5fb640651fd22b6c

環境はUbuntu20.04です。

必要パッケージをインストール

apt install libnss3-tools

インストール

curl -s https://api.github.com/repos/FiloSottile/mkcert/releases/latest | grep browser_download_url | grep linux-amd64 | cut -d '"' -f 4 | wget -qi - \
    && mv mkcert-v*-linux-amd64 mkcert \
    && chmod a+x mkcert \
    && mv mkcert /usr/local/bin/

ローカル認証局作成

 mkcert -install

これにより、管理者ユーザでもmkcertコマンドを使えるようになったのが大きいです。

Page 1 of 2

Powered by WordPress & Theme by Anders Norén