- 対象: 自社ドメインを管理している Web 担当者・サイト運営者・制作会社の方
- 解決すること: 「管理画面の持ち主が曖昧」「ロックや CAA の状況を知らない」「DNS を書き換えられても気づけない」状態をなくします
- 方法: アカウント・ロック・更新・CAA・DNSSEC・監視の 7 項目を、コマンドで確認しながら設定します
なぜドメインとDNSを守るのか
WAF や HTTPS は、訪問者が正しいサーバーに届いて初めて効きます。DNS を書き換えられると、サーバーに侵入されなくても、訪問者やメールを別の場所へ誘導されます。
JPRS は 2014 年、国内組織が運用する複数の .com サイトで、登録情報の不正書き換えによるドメイン名ハイジャックの被害が出たとして注意喚起しました。米国 CISA も 2019 年の緊急指令 ED 19-01 で、政府機関に「DNS レコードの監査」「DNS アカウントのパスワード変更」「多要素認証の追加」「CT ログの監視」を指示しています。
手順0: 現在の状態を記録する
後で変化を判断できるよう、自社ドメインの正しい状態を残します。
DOMAIN="example.com"
for t in NS A AAAA MX TXT CAA DS; do
echo "== $t"
dig +short "$DOMAIN" "$t"
done | tee "dns-$DOMAIN-$(date +%Y%m%d).txt"
設定1: 管理アカウントの持ち主と認証を確認する
ドメイン管理会社と DNS サービスの両方について確認します。
| 確認項目 | 望ましい状態 |
|---|---|
| アカウントの持ち主 | 会社が管理している(退職者や前任の制作会社の個人名義でない) |
| ログイン認証 | 多要素認証を有効化し、パスワードは使い回さない |
| 連絡先メール | そのドメイン以外のアドレスも登録している |
連絡先が管理対象のドメインだけだと、失効時などに案内を受け取れません。
設定2: APIトークンの権限を絞る
自動化に使う API トークンは、管理画面のパスワードと同じ重さで扱います。Cloudflare なら Global API Key を使わず、対象ゾーンと権限を限定したトークンを発行し、接続元 IP の制限と有効期限も設定します。
証明書の DNS 認証用にトークンをサーバーへ置くと、そのサーバーの侵害が DNS の書き換えに直結します。ファイル権限は 600 にしてください。
設定3: 移転ロックを有効にする
ICANN の説明では、clientTransferProhibited は移管を、clientUpdateProhibited は登録情報の更新を、clientDeleteProhibited は削除を拒否する状態です。移管ロックだけではネームサーバーの変更は止まらない点に注意してください。
.com の状態は RDAP で確認できます。
curl -fsS "https://rdap.verisign.com/com/v1/domain/example.com" \
| jq -r '.status[], (.nameservers[].ldhName)'
client transfer prohibited がなければ、管理画面でロックを有効にします。ほかの gTLD の接続先は IANA の一覧にあります。
.jp は JPRS WHOIS で確認します。
whois -h whois.jprs.jp example.jp
指定事業者変更ロックは、汎用・都道府県型で移転と指定事業者変更の申請を、属性型(.co.jp など)・地域型で指定事業者変更の申請を止めます。レジストリロックは登録情報変更・移転・廃止などの申請も制限します。
JPRS WHOIS の「ロック状態」は、ロックを設定している場合に表示されます。どちらのロックも申し込みは管理指定事業者経由で、取り扱いのない事業者もあります。
管理画面で解除できるロックは、アカウントを奪われると解除されるおそれがあります。重要なドメインはレジストリ側のロックも相談してください。
設定4: 自動更新と期限確認を二重にする
自動更新を有効にし、カード期限切れで止まる場合に備えて残り日数も確認します。
EXP="$(curl -fsS "https://rdap.verisign.com/com/v1/domain/example.com" \
| jq -r '.events[] | select(.eventAction=="expiration") | .eventDate')"
echo "期限: $EXP / 残り $(( ($(date -d "$EXP" +%s) - $(date +%s)) / 86400 )) 日"
.jp は whois の結果で期限を確認します。表示項目はドメインの種別によって異なります。
設定5: CAAで証明書を発行できるCAを絞る
CAA は「このドメインの証明書を発行してよい CA」を指定する DNS レコードです(RFC 8659)。設定がなければ、検証さえ通ればどの公開 CA でも発行できます。
先に、今使っている CA を洗い出します。漏れると証明書の更新が止まります。
curl -fsS "https://crt.sh/?q=%25.example.com&output=json" \
| jq -r '.[].issuer_name' | sort | uniq -c | sort -rn
Let's Encrypt だけを使っている場合の例です。
example.com. 3600 IN CAA 0 issue "letsencrypt.org"
example.com. 3600 IN CAA 0 issuewild ";"
issuewild ";" はワイルドカード証明書の発行を禁止します。CA は対象の名前から親へたどり、最初に見つかった CAA を使います。CNAME の場合は行き先の CAA が適用されます。
Cloudflare の Universal SSL を使っている場合、CAA を追加すると必要な CAA が自動で足されます。管理画面には出ないので dig example.com caa +short で確認してください。ワイルドカードも使うため、この場合は issuewild ";" を入れないでください。
さらに絞るなら、Let's Encrypt の CAA 文書にある accounturi(ACME アカウントの限定)と validationmethods(検証方式の限定)を使えます。アカウント URL は sudo certbot show_account(1.23.0 以降)で確認します。
example.com. 3600 IN CAA 0 issue "letsencrypt.org;accounturi=https://acme-v02.api.letsencrypt.org/acme/acct/123456789;validationmethods=http-01"
アカウントを作り直すと更新できなくなるので注意してください。CA/B フォーラムの Ballot SC098v2 では、これらのパラメータの処理要件が 2027 年 3 月 15 日に発効します。
DNS を奪われると CAA も書き換えられるので、それ以外の経路での発行を減らす設定です。
設定6: DNSSECを有効にする
DNSSEC は DNS の応答に署名を付け、途中での偽装を検出できるようにします。DNS サービス側で署名を有効にし、表示された DS レコードを管理会社で登録します(Cloudflare Registrar 利用時は自動)。
dig +short DS example.com
dig +dnssec example.com A @1.1.1.1 | grep -E '^;; flags' # flags に ad があれば検証成功
注意が必要なのは移転時の順序です。古い DS を残したまま DNS サービスを切り替えると、サイトもメールも引けなくなります。Cloudflare の手順では、DS を削除して TTL 切れ(多くは 24〜48 時間)を待ち、ネームサーバーを変え、その TTL 切れも待ってから新しい DS を登録します。
正規の管理画面から書き換えられると署名も正規のものになるため、設定 1〜3 の代わりにはなりません。
設定7: DNSと証明書の変化を毎日監視する
Cloudflare 利用者は、まず CT Monitoring を有効にします(全プラン対応・既定はオフ)。ほかの環境でも crt.sh で確認できます。
#!/usr/bin/env bash
# /usr/local/bin/domain-watch.sh 変化時のみメール
set -u
DOMAIN="example.com"
MAILTO="ops@example.net" # 別ドメインの宛先
RESOLVER="1.1.1.1"
ALLOWED_CA="O=(Let's Encrypt|Google Trust Services)" # 設定5の発行者
STATE_DIR="/var/lib/domain-watch"
BASE="$STATE_DIR/$DOMAIN.txt"
WORK="$(mktemp -d)"; trap 'rm -rf "$WORK"' EXIT
mkdir -p "$STATE_DIR"
# DNS: 基準ファイルとの差分(応答不能も差分になる)
for t in NS MX TXT CAA DS; do
dig +short +time=5 +tries=2 "@$RESOLVER" "$DOMAIN" "$t" | sort | sed "s/^/$t /"
done > "$WORK/dns.txt"
[ -f "$BASE" ] || cp "$WORK/dns.txt" "$BASE"
diff -u "$BASE" "$WORK/dns.txt" > "$WORK/report.txt"
# CT: 直近2日の許可外発行者
SINCE="$(date -u -d '2 days ago' +%Y-%m-%dT%H:%M:%S)"
for q in "$DOMAIN" "%25.$DOMAIN"; do
if ! curl -fsS --retry 2 --max-time 90 "https://crt.sh/?q=$q&output=json" -o "$WORK/ct.json"; then
echo "CT 取得失敗: $q"
continue
fi
jq -r --arg s "$SINCE" '.[] | select(.not_before >= $s)
| [.not_before, .issuer_name, (.name_value | gsub("\n"; ","))] | @tsv' "$WORK/ct.json"
done | sort -u | grep -vE "$ALLOWED_CA" >> "$WORK/report.txt"
if [ -s "$WORK/report.txt" ]; then
mail -s "[domain-watch] $DOMAIN に変化あり" "$MAILTO" < "$WORK/report.txt"
fi
sudo chmod 755 /usr/local/bin/domain-watch.sh
echo '20 6 * * * root /usr/local/bin/domain-watch.sh' | sudo tee /etc/cron.d/domain-watch
送信には mail が必要です。正規の変更後は基準を作り直します。
sudo rm /var/lib/domain-watch/example.com.txt && sudo /usr/local/bin/domain-watch.sh
不審な変化を見つけたときの初動
-
digの結果、通知メール、管理画面の変更履歴を保存する - 管理画面に入れなければ、管理会社に確認と凍結を依頼する
- パスワード、多要素認証、API トークンを更新する
- ネームサーバーと各レコードを手順 0 の記録と照合して戻す
- 身に覚えのない証明書は発行元の CA に連絡する(失効できるのは CA だけです)
- メールの誤配送や入力被害を確認し、利用者への告知を判断する
まとめ
どれか 1 つで全部は防げません。まず設定 1 と 3 を確認し、CAA と DNSSEC は移転の予定を確かめてから進めてください。
関連記事
本記事のような設定の抜けは、Web サイトを 9 つの守りでまるごと守るサイトドックでまとめて対策できます → https://sitedock.jp