BIMIを使ってメールにロゴを表示する方法 (受信編)
はじめに
ウェブメールやメーラーなどでどのようなプロセスでBIMIのロゴが表示されるのかを説明します。
ここまでの詳細はウェブメールの開発者でもなければ必要のない内容なのですが、どのように表示されるのかを知ることで、送信者としてもロゴが表示されないときの原因究明の助けになればと思います。
本記事は3部作の受信編になります。
受信したときのBIMIの処理の流れ
受信側の処理は、
- BIMIの認証
- DNSからBIMIレコードの取得
- ロゴの取得・検証
- 証明書の取得・検証
のような流れで進められます。
BIMIの認証
BIMIの認証の基本の流れ
BIMIの認証の手順については詳しくは、draft-brand-indicators-for-message-identificationの7.1 Authentication Requirementsに詳しく書かれています。
箇条書きで書かれているので、図にしてみるとこんな感じです。
順番にBIMIとしてFAILになる条件が書かれています。
最後まで残った場合、BIMIとしてPASSということになります。
従ってBIMIの認証がPASSする条件としては、以下の全てを満たす場合となります。
- Fromヘッダが1つのみである
- 以下のいずれかを満たす
- Fromヘッダのドメインに対してDMARCがPASSする
- ARCがPASSする
- 信頼できるリストやローカルポリシーなどで認められる
- Fromドメインやその組織ドメインのDMARCのポリシーが次のいずれかである
- p=rejrct
- p=quarantine; pct=100 (pct=100は省略可)
- Fromドメインやその組織ドメインのDMARCのサブドメインのポリシーがsp=noneではない
基本的にはDMARCの処理と同様なのですが、通常のDMARCよりかなり厳しく設定されていることがわかります。
BIMI実装のためのガイダンス
基本的な仕様だけでもBIMIをPASSするには厳しい設定が必要なのですが、この仕様とは別にBIMI実装のためのガイダンス (General Guidance for Implementing Branded Indicators for Message Identification (BIMI))(以下General Guidance)というドキュメントがあり、こちらは更に厳しい内容を要求しています。
5.2.1. BIMI processing requirements
- ヘッダFromの組織ドメインに対してDKIMとSPFの両方が有効
- (DMARCはFrom:のドメインに対してSPFかDKIMのどちらかが有効であればよい)
- 強いSPFが必要
- -allを必須として?allは許可しない
- 極端に広いアドレス空間を含む物は許可しない
- など
- SMTPはTLS経由であること
- フィードバックループ登録やその他の登録方法
- DNSの許可リストや他の方法でのDomain Reputation
ただし、「どこまでやるかは受信側の裁量による」とも書かれています。
BIMIレコード
BIMIレコードの取得手順
BIMIの認証がPASSしたら、次は、BIMIのレコードをDNSから取得します。
BIMIにはDKIMのようにセレクターがあります。
draft-brand-indicators-for-message-identification1の
7.2 Assertion Record Discoveryによると、次のように書かれています。
- BIMI-Selectorヘッダが指定されていれば、その値をセレクターとする。なければ「default」
- 「セレクター._bimi.Fromドメイン」のtxtレコードを取得する
- 「v=現行のBIMIのバージョン」で始まっていなければそのレコードは捨てる
- レコードセットが空の場合は「セレクター._bimi.組織ドメイン」を調べる
- 「v=現在のBIMIのバージョン」で始まっていなければそのレコードは捨てる
- 残ったレコードが複数ある、または、なければ、BIMIとして処理しない
- 残ったレコードが1つだけであれば、それをBIMIのAssertionとして使用する
複雑ですが、要するに、
サブドメインの場合、例えば、
From: alice@sub.example.jp
であれば、
- default._bimi.sub.example.jpのv=BIMI1で始まるTXTレコードを探す
- なければ default._bimi.example.jpのv=BIMI1で始まるTXTレコードを探す
という手順になります。
また、
From: alice@sub.example.jp
BIMI-Selector: v=BIMI1; s=myselector;
のようにセレクターが指定されているときは、
- myselector._bimi.sub.example.jpのv=BIMI1で始まるTXTレコードを探す
- なければ myselector._bimi.example.jpのv=BIMI1で始まるTXTレコードを探す
という手順になります。
BIMIレコードの書き方
BIMIレコードは次のようにDNSに記述します。
default._bimi.example.jp TXT “v=BIMI1; l=https://.../ロゴ.svg; a=https://.../証明書”
証明書を使用しない場合、「a=」の証明書は省略可能です。
例えば、qualitia.co.jpのBIMIレコードは次のようになっています。
# dig +short default._bimi.qualitia.co.jp txt
"v=BIMI1;
l=https://www.qualitia.co.jp/doc/logo/qualitia.svg;
a=https://www.qualitia.co.jp/doc/logo/qualitia.pem"
ロゴを出したくない場合
ロゴを出したくない場合は、明示的に以下のように書くことができます。
default._bimi.example.jp TXT “v=BIMI1; l=; a=;”
この場合、Authentication-Resultsヘッダは、
Authentication-Results: bimi=declined;
というふうになります。
ロゴの取得
BIMIのDNSレコードが取得できたら、次はロゴを取得します。
ロゴを取得し表示するまでの検証プロセスは、
7.3 Indicator Discoveryあたりに書かれています。
図にしてみるとこんな感じです。
ロゴの取得については、HTTPSである必要があり、オレオレ証明書などは使用できないようになっています。
また、ロゴはSVGなので、SVGの検証プロセスがあります。
ロゴのフォーマットについての詳細は受信編を参照してください。
ロゴの表示
General Guidanceの6.1. Image Displayにロゴの表示について次のように書かれています。
- ロゴの縦横比については定義されていない
- しかし、正方形や円の領域に表示することを想定
- 1:1の縦横比で表示することを推奨
受信側としては、1:1の縦横比で正方形や円の領域に表示するのがいいようです。
2022/11/11追記
長方形のロゴを登録したところ、gmailで表示されなかったという話を聞きました。
白い正方形の背景を追加して、ロゴを正方形にしたところ表示されるようになったそうです。
このとき、VMCの発行料を再度支払うことになったらしいので、もし可能であれば、SVGが原因で表示されない場合VMCの再発行をおまけしてもらうなどの交渉を事前にしておいた方がいいかもしれません。
HTTPリダイレクト数の制限
General Guidanceの5.5. Limited use of HTTP Redirectsに次のように書かれています。
- ロゴやevidence document(現状はVMC証明書)を取得するとき、HTTPリダイレクトを辿らなかったり、リダイレクト回数を制限してもよい
- 送信者はリダイレクトしないようにするか、回数が少なくなるように制限すべき
VMC証明書
これまでのプロセスで、送信者のドメインがなりすまされていないことは確認できます。また、送信者が指定した正しいロゴを表示する、ということもできそうです。
では、送信者が似たようなドメインを使用して、ロゴを詐称した場合はどうでしょうか。
ロゴは送信者が指定したものをそのまま表示するので、なりすましが成立してしまいます。
そこで、送信者(のドメイン)がロゴを所有者していることを証明するのが、VMC証明書です。
VMC証明書とは
VMC証明書とは、簡単に言えばSSLのEV証明書のようなもので、
- ドメイン名が正しいこと
- 会社名が正しいこと
- 実在していること
- ロゴが正しいこと
を証明します。
VMC Evidence Documentの検証
VMCの検証方法は、
draft-fetch-validation-vmc-wchuangの5.3 Validation of VMC Evidence Documentに書かれています。
- a=タグをもとにVMCを取得する。URIはHTTPSでなければならない (MUST)
- TLS1.2以上であること (SHOULD)、RFC8446で指定されたプロトコルで接続すること (MUST)、TLSの証明書は信頼できるroot CAまでたどれること (MUST)。でなければ検証はエラー
- 証明書に有効なVMCチェインが1つだけ含まれていなければエラー
- 5.1での発行者とプロファイルの検証が失敗すればエラー
- 5.2のドメインの検証が失敗すればエラー
- 検証されたVMCの[LogoType]拡張からSVGロゴを取り出す
- オプションとして6のロゴとl=タグから取得したロゴが異なっていればエラーにしよい(MAY)
- その他のVMCの仕様に合うかどうか確認して、合わなければエラー
証明書の発行者とプロファイルの検証
上の4で出てきた部分です。証明書が正しいものかどうかを以下の手順で行います。
5.1 Issuance and Profile Verification
- 以下を確認
- 証明書がrootまでたどれること
- root CAが信頼できること
- root以外の中間証明書が含まれていることを確認
- 証明書のチェインに含まれる証明書の有効性を確認
- 証明書のチェインに含まれる証明書が取り消されていないことを確認
- CT(証明書の透明性)ログをつかって正しく署名されているか確認
- 証明書がid-kpBrandIndicatorforMessageIdentification拡張キーを持つVMCであることを確認する
ドメインの検証
これは5の部分です。
5.2 VMC Domain Verificationに難解な書き方で書かれているのですが、図にすると、こんな感じです。
いや、それでも難解です。
SSLの証明書と同様にVMCの証明書もSANにFromのドメインを書く必要があります。
このとき、セレクターがなければドメインをそのまま書けばいいのですが、セレクターがある場合は、「セレクター._bimi.ドメイン」をSANに書く、というのが意図のようです。
example.jpでセレクタがない場合は
example.jp
example.jpでセレクタがある場合は
selector._bimi.example.jp
ということです。
VMC証明書を見てみる
実際の証明書の中身を見てみることにします。
証明書を取得
# dig +short default._bimi.qualitia.co.jp txt
"v=BIMI1;
l=https://www.qualitia.co.jp/doc/logo/qualitia.svg;
a=https://www.qualitia.co.jp/doc/logo/qualitia.pem"
# wget https://www.qualitia.co.jp/doc/logo/qualitia.svg
VMC証明書の中身
opensslコマンドで証明書の中身を見てみます。
# openssl x509 -text -noout -in qualitia.pem
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
04:f3:e3:a7:0b:10:07:cf:59:2c:3d:5d:65:72:63:5a
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=US, O=DigiCert, Inc., CN=DigiCert Verified Mark RSA4096 SHA256 2021 CA1
Validity
Not Before: Jan 21 00:00:00 2022 GMT
Not After : Jan 20 23:59:59 2023 GMT
Subject: businessCategory=Private Organization/jurisdictionC=JP/serialNumber=0100-01-084915, C=JP, ST=Tokyo, L=Chuo-ku/street=3-11-10 NIHOMBASHIKAYABACHO, O=QUALITIA CO., LTD., CN=QUALITIA CO., LTD./1.3.6.1.4.1.53087.1.3=JP/1.3.6.1.4.1.53087.1.4=6495403
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:df:22:fd:da:ef:43:d6:48:7c:d0:32:ec:24:7e:
....略....
45:ff
Exponent: 65537 (0x10001)
X509v3 extensions:
....略....
X509v3 Subject Alternative Name:
DNS:qualitia.co.jp
....略....
1.3.6.1.5.5.7.1.12:
image/svg+xml0#0!0...+............4c....ey.M..C.4y0..~...zdata:image/svg+xml;base64,H4sIAAAAAAAACo1V227bRhB9tr5iyz4V2F3tjUtSsBw0RpsEcIAABfxaKBQjsmVFVaIoO4/Jr/Sxn9C/8Y/0zFCyZSMFapvkcGd45szsmfXlq7s/WjFU213TreeJ1SYR1brsls16NU/2/SeVJ6+uJpffKSXeVOtqu+i77Uz8uOw+VuJd2+53PS8J5zU+luKX2zfip7tNt+3Fh3a/Uu/WQvPi7ZhjJqI2RrzeN+1SmB+EUArwu2F1TsIl4uNiV33Ydp+atponfbO+V5tdIprlPHn4+vfDl78evuLvn19tMrkQKGG9myd1329m0+nhcNAHr7vtauqMMVNgJ2PI7K5t1r9/K9AWRTFlbyKGpjq87u7miRFG+FDofLwfM812m0UJTptttau2Q5WAft/0bXX1537RNn2z0GWnf9tcTsfVyeW2KnuBQtp58v3P/JOIQ7Ps63kyAou6alZ1f3qd4pvNoq8Fin1vc6u9dCHTaalcqqM0KuS6UNbpTEWnc+W9jrUqSht0ITkkWFiBPsysztkqjcSvtqd7DZCsVZl2ylptJxclAD3erXSEbDMdpEeoohvgrx0Z0nnOYiJwmZU8Y/icuicWNk21q1WuQ6ko2ijrgex0oBSZRMJrjouRvNKCccbPfLAgHYHNfgt3CpfFrQxYDfRitCMIecKaXNRIVdtC+1YhkUqpdB0VLuB4ZSmY2qfz0qN5AQBAVAVI0ZPgB4XMKnJ0Qe1J1cl1HVIilGaA8/QN1yefSoVI3hdMKMeOmbG9HmXkVC/Ww9GqaUdzqjQAK0U7yUWF2FJRWfSdiscl9eisqY2Ti2uwQcqCtrsgSjmkIx8zvxBRZrhz4Hdri4i2eiRwnDc7XTV2NgzYKaTAVqfEoqD94u5l/MQ1KB918ZYhP3Ma1jfmxYFSTMQ9JpgyPYociE8SDxExTK5r71fdWmy6Zt1jep3LSUzRay9c4LrPbGcgC+HSUXxP9jEm89SCc5sYiCMm28+pZvn/J/rYRZ8zK4ivpczav7U+tJBQkHmNqYltRvuGk7Bm/TkwVCHQhjoIweMto3klUWEMxwBJARR/c4b/+cUkcSCr7ttQN64wY184syPFe0zwkWdNQ5f/J9ObM/wXma19NsR2oPMF+gk8QqnEsAE1pUEMRA0WHUs84SlJPyOjhicvaRahpwL8DQ0BRTrScqm8cgSDiJSfBDfwcfcsKx0gjoljoCKVFkqcUpA92kgLdArAqCkd+kPT5Ufpsg/YEenQBjobgJEBveAnYd1ynS87kKF+g7OxZDEZmdLsIDyTkbcPc1Dz8WZpZxx3xNCNLBIjW6jRESEV6QRyhj2SPPQxzbPjM8Dm3G/DPXWk5FN+pkX/za4m/wL+pu1ttgcAAA==
CT Precertificate SCTs:
....略....
普通のSSL証明書のような情報に加えて、画像データが入っていることがわかります。
画像部分をdecodeしてみる
画像データがBase64されているので、戻してみます。
# base64 -d < 画像部分.txt | gzip -d
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 23.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.2" baseProfile="tiny-ps" id="レイヤー_1"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 349.8 349.8"
xml:space="preserve">
<title>qualitia.co.jp</title>
<rect fill="#FFFFFF" width="349.8" height="349.8"/>
<path d="M181.3,247.5c-25.6,0-48.9-12.7-62.8-33.6h-9c14.9,25.6,41.9,41.3,71.8,41.3c0,0,0.1,0,0.1,0h48.7l-7.2-11.1
c12.3-7.1,22.7-17.4,30.1-30.1h-9C230.1,234.9,206.8,247.5,181.3,247.5"/>
<path d="M31.9,155.2h-8.4c-7.5,0-13.4,2.4-17.7,7.1C1.9,166.5,0,171.7,0,178v1c0,6.3,1.9,11.5,5.7,15.7c4.3,4.7,10.2,7.1,17.7,7.1
h8.4h19.3l-3.4-5.3c0.6-0.6,1.3-1.1,1.9-1.8c3.8-4.2,5.7-9.4,5.7-15.7v-1c0-6.3-1.9-11.5-5.7-15.7C45.3,157.6,39.4,155.2,31.9,155.2
M9.1,178.5c0.2-11.3,6.8-17.1,14.8-17.1h7.5c8,0,14.6,5.8,14.8,17.1c-0.2,11.3-6.8,17.1-14.8,17.1h-7.5
C15.9,195.6,9.3,189.8,9.1,178.5"/>
<path d="M170.7,155.2V196c0,3.2,2.6,5.7,5.7,5.7h34.4v-8.1h-30.5c-0.9,0-1.6-0.7-1.6-1.6v-36.9H170.7z"/>
<rect x="215.6" y="155.2" width="8.1" height="46.6"/>
<polygon points="228.5,163.3 246.8,163.3 246.8,201.7 254.9,201.7 254.9,163.3 273.3,163.3 273.3,155.2 228.5,155.2 "/>
<rect x="278" y="155.2" width="8.1" height="46.6"/>
<path d="M138.8,164.3l9,16.3H134l-4.4,8h22.6l7.2,13.1h9.3l-24.9-44.1c-2.2-3.9-7.8-3.9-10,0l-24.9,44.1h9.3L138.8,164.3z"/>
<path d="M324.9,157.6c-2.2-3.9-7.8-3.9-10,0L290,201.7h9.3l20.6-37.4l9,16.3h-13.8l-4.4,8h22.6l7.2,13.1h9.3L324.9,157.6z"/>
<path d="M111.9,155.2h-8.1v25.6c0,4.9-1.5,8.4-4.5,10.4c-2.5,1.7-6.4,2.5-11.7,2.5h-2.8c-5.3,0-9.2-0.8-11.7-2.5
c-3-2-4.5-5.5-4.5-10.4v-25.6h-8.1v25.6c0,7.5,2.2,13,6.6,16.4c4,3,9.9,4.6,17.7,4.6h2.8c7.8,0,13.7-1.6,17.7-4.6
c4.4-3.4,6.7-8.9,6.7-16.4V155.2z"/>
<path d="M179,102.3c28.5,0,53.2,16.7,64.9,40.7h8.4c-12.2-28.4-40.4-48.4-73.3-48.4c-32.8,0-61.1,20-73.3,48.4h8.4
C125.8,118.9,150.5,102.3,179,102.3"/>
</svg>
SVGの画像データになりました。
このように、VMC証明書の中にはロゴデータそのものがエンコードされて格納されています。