存在しない名前への問い合わせに対して、DNSSECは「ありません」という応答だけでなく、その答えが正しいことを検証できる証拠も返します。 その証拠に使われるNSECとNSEC3を理解するには、まず「名前の階層」と「名前の並び順」を別のものとして考えるのがポイントです。
先に要点:名前の階層は「どの名前がどの名前の下にあるか」を表し、Closest EncloserやNext Closer Nameを決めます。 正規順序は「名前を一列に並べる順番」で、NSECの範囲を決めます。NSEC3では、名前をハッシュ化した後の並び順が範囲になります。 階層の深さと並び順は、別の軸です。
DNSの否定応答には、主に名前そのものが存在しない NXDOMAIN と、名前は存在するが問い合わせた種類のレコードがない NODATA があります。
署名は存在するRRsetに対して行われるため、空の否定応答そのものを署名するのではなく、ゾーン内の名前の存在や不在を示すNSEC/NSEC3レコードを署名して証拠にします。
ドメイン名の階層は、右端のラベルから左へたどります。サブドメイン関係を矢印で示すと、gov.example. → b.gov.example. → a.b.gov.example. の順です。
つまり b.gov.example. は gov.example. の直下にあり、a.b.gov.example. は b.gov.example. の直下にあります。左側にラベルを一つ加えるごとに、より具体的な名前になります。
example.
└── gov.example. ← 既存の名前
└── b.gov.example. ← 存在しない
└── a.b.gov.example. ← 問い合わせた名前(存在しない)
この例では gov.example. が最も深い既存の祖先名、つまり Closest Encloser(最近接祖先名)です。
その一段下で問い合わせ名の祖先でもある b.gov.example. が Next Closer Name(次近接名)です。
Next Closer NameはClosest Encloserにラベルを一つ足した名前であり、問い合わせ名へ向かう階層上の位置で決まります。
実際には問い合わせ名から左端のラベルを順に取り除き、既存の名前に初めて行き当たるところをClosest Encloserとして見つけるイメージです。 この手順は名前の階層についてのもので、正規順序やハッシュ値の大小を使ってClosest Encloserを選ぶわけではありません。
DNSの正規順序は、ドメイン名を比較するための決まった規則です。画面上で文字をどちら向きに読むかとは別の話で、名前を右端(ルートに近い側)のラベルから比較し、同じなら左へ進みます。 各ラベルの中では左から文字(正確にはオクテット)を比較します。大文字・小文字は同一視し、ラベルがすべて一致したまま一方の名前が終われば、ラベル数の少ない方が先になります。
次の名前だけがあると仮定したとき、正規順序は次のようになります。
| 順序 | ドメイン名 | 比較の見方 |
|---|---|---|
| 1 | example. | 共通の親より先に続くラベルがなく、短い名前が先です。 |
| 2 | a.example. | example の左にあるラベル a で比較します。 |
| 3 | b.example. | a.example. と example の左側のラベルを比べると、a より b が後になります。 |
| 4 | a.b.example. | b.example. と右側のラベル b.example. が一致します。一方の名前がここで終わるため、ラベル数の少ない b.example. が先です。 |
| 5 | ba.example. | b.example. と example の左側のラベルを比べます。b は ba の先頭と共通し、短いラベル b が先になります。 |
| 6 | z.example. | 左側のラベル z は b より後です。 |
この並びでは b.example.(2ラベル)が a.b.example.(3ラベル)より前です。
その一方で、a.b.example. は b.example. の子でもあります。
このように、階層上の親子関係と正規順序上の前後関係は別の情報です。
NSECリソースレコードには、所有者名(Owner Name)、次のドメイン名(Next Domain Name)、型ビットマップ(Type Bit Maps)があります。 Owner Nameは、そのNSECレコードが置かれ、かつ存在を示す名前です。Next Domain Nameは、Owner Nameの次に正規順序で並ぶゾーン内の名前です。 最後の名前の次は最初の名前に戻り、ゾーン内の名前が一周する鎖になります。
| フィールド | 意味 |
|---|---|
| Owner Name | このNSECレコードが属する既存の名前。NSECの所有者名自体が、その名前の存在を示します。 |
| Next Domain Name | Owner Nameの次に正規順序で並ぶ既存の名前。Owner NameからNext Domain Nameまでの間に、他のゾーン内の名前がないことを示します。区間の端点は含みません。 |
| Type Bit Maps | Owner Nameに存在するレコード型の一覧(ビットマップ表現)。問い合わせ型が一覧にないことを、NODATAの証拠として使います。 |
ゾーンファイルでの形は次のとおりです(TTL・クラス等は省略)。
owner.example. IN NSEC next.example. A NS SOA RRSIG NSEC DNSKEY
正規順序の表にある名前だけが存在するものとして、次のNSECレコードがあるとします。
ba.example. IN NSEC z.example. A RRSIG NSEC
Owner Nameの ba.example. は存在し、次の z.example. までの正規順序上の区間には、ゾーン内に登録された他の名前がありません。
そのため、その区間内にある問い合わせ名(例:c.example.)が存在しないことの証拠になります。
Type Bit Mapsに A があればOwner NameにはAレコードがあり、なければ、他の条件も満たす必要がありますが、A問い合わせへのNODATA証明に使えます。
NXDOMAIN では、問い合わせ名が存在しない証拠だけでは不十分です。Closest Encloser直下のワイルドカード名も存在しないことを証明し、ワイルドカードから応答が生成されていないことを示します。
NSECでは、問い合わせ名を覆う証拠とワイルドカード名を覆う証拠という複数の証明要素が必要です。ただし、同じNSECレコードが両方の名前を覆う場合は、1つのレコードで両方を証明できます。
一方、既存名への NODATA では、その名前のNSECとType Bit Mapsが主な証拠になります。
NSEC3は名前をハッシュ化し、Base32hexで表したハッシュ値を並べて鎖を作ります。 範囲判定で比べるのは元のドメイン名ではなく、同じハッシュ方式・salt・反復回数で計算したハッシュ値です。 ハッシュ値の順番から、元の名前のラベル数や親子関係を読み取ることはできません。
NSEC3にもOwner Name、Next Hashed Owner Name、Type Bit Mapsがあります。Owner Nameの左端ラベルは、元の名前をハッシュ化してBase32hexで表した値です。 つまり、Owner Nameは「このハッシュに対応する名前がNSEC3鎖に登録されている」ことを示します。Opt-Outでは一部のDSなし委任のハッシュを鎖から省略できるため、親ゾーンに存在する名前すべてがOwner Nameに現れるわけではありません。 Next Hashed Owner Nameは、ハッシュ値の正規順序で次に並ぶNSEC3鎖に含まれる名前のハッシュです。 Type Bit Mapsは、Owner Nameのハッシュ元となった名前に存在するレコード型を示します。
| フィールド | 意味 |
|---|---|
| Owner Name | <Base32hexハッシュ>.<ゾーン名>.。左端のラベルはNSEC3鎖に登録された名前のハッシュで、その名前の存在を示します。Opt-Outで省略されたDSなし委任は、Owner Nameに現れないことがあります。 |
| Next Hashed Owner Name | ハッシュ値の正規順序で次に並ぶ、NSEC3鎖に含まれる名前のハッシュ。Owner Nameのハッシュから次のハッシュまでの間に、他の鎖上のハッシュがないことを示します(Opt-Outでは、省略されたDSなし委任がこの間に存在する場合があります)。区間の端点は含みません。 |
| Type Bit Maps | Owner Nameのハッシュ元となった名前に存在するレコード型の一覧。問い合わせ型がないことをNODATAの証拠として使います。 |
ゾーンファイルでの形は次のとおりです(各パラメーターの詳細は省略)。
<Base32hexハッシュ>.example. IN NSEC3 1 0 0 - <次のハッシュ> A RRSIG
NSEC3レコードには、Type Bit Maps以外にハッシュアルゴリズム、フラグ、反復回数、saltもあります。ここでの例の 1 0 0 - は、それぞれSHA-1、フラグなし、反復回数0、saltなしを表す簡略例です。
ここで保たれるのは、元のドメイン名の順序ではなく、計算済みハッシュ値の順序です。
NSEC3ではハッシュ値をBase32hexで表して所有者名に使いますが、Base32hexの文字は値の小さい順に
0〜9、A〜V と並んでいます。
NSEC3の所有者名は <ハッシュ>.<ゾーン名>. の形で、同じゾーン内では右側のゾーン名部分が共通です。DNS名の正規順序でその共通部分を比べた後、ハッシュのラベル内を左から比較すると、Base32hex文字列を比較した順序は符号化前のハッシュ値をバイト列として比較した順序と一致します。
| Base32hex文字 | 表す5ビット値 | 順序の例 |
|---|---|---|
0〜9 | 0〜9 | 8 < 9 < A |
A〜V | 10〜31 | A < B < … < V |
Base32hexはハッシュのビット列を先頭から5ビットずつ文字に対応させます。したがって、二つのハッシュ値で最初に異なる5ビットのまとまりがあれば、その値に対応する文字の大小も同じになります。
NSEC3のSHA-1ハッシュは160ビットで、ちょうど32文字のBase32hexになります。例えば先頭のまとまりが 01001 の値は 9、01010 の値は A となり、9 が A より前に並ぶのは、元の5ビット値の大小と一致します。
つまり、ハッシュ計算そのものがドメイン名の正規順序を保つわけではありません。ハッシュ化で元の順序との対応は失われますが、その後のBase32hex表記は、ハッシュ値同士の順序を保ちます。 NSEC3の所有者名にある符号化済みのハッシュと、Next Hashed Owner Nameに格納されたバイナリのハッシュを同じ順序で比較できるのはこのためです。
名前の階層で決める: Closest Encloser = gov.example. Next Closer Name = b.gov.example. NSEC3の証拠で行う: gov.example. → ハッシュが存在することを確認 b.gov.example. → ハッシュがNSEC3の区間に入ることを確認 *.gov.example. → ワイルドカード名が存在しないことを確認
ここからClosest Encloserのハッシュ一致を使う説明は、Opt-Outを使わない基本ケースです。Opt-Out時にClosest Encloserのハッシュが省略されている場合は、NSEC3で存在を証明できる最も近い祖先を使います。詳細は次章で説明します。
つまり、Closest EncloserとNext Closer Nameは名前の階層から決めます。
その後、NSEC3ではClosest Encloserのハッシュが存在すること、Next Closer NameのハッシュがNSEC3の範囲で覆われることを確認します。
NXDOMAINではさらにClosest Encloser直下のワイルドカード名(この例では *.gov.example.)が存在しないことも証明します。
Next Closer Nameをハッシュ順序で探したり、ハッシュ値のラベル数を見たりするわけではありません。
したがって、NSEC3のNXDOMAIN証明も一つのレコードだけで完了するとは限りません。通常は、Closest Encloserのハッシュが登録済みである証拠、Next Closer Nameのハッシュを覆う証拠、ワイルドカード名のハッシュを覆う証拠を組み合わせます。 これらは複数の証明要素であり、範囲が重なる場合は同じNSEC3レコードが複数の役割を満たすこともあります。NODATAでは、通常、対象名のハッシュと一致するNSEC3レコードのType Bit Mapsが主な証拠です。
親ゾーンにNSレコードがありDSレコードがない委任名は、親ゾーンには存在しますが、親から子ゾーンのDNSKEYへ信頼の連鎖がつながっていません。 子ゾーンがDNSKEYやRRSIGを公開していても、親ゾーンにDSがなければ、その鍵を親の信頼の連鎖で認証できません。このため、検証では子ゾーンを「secure」ではなく「insecure」と扱います。 ここでいう「DSのない委任」は、子ゾーンが実際に署名しているかどうかではなく、親ゾーンにDSがあるかどうかを指します。 通常のNSEC3ではその委任名のハッシュも鎖に含めますが、大規模ゾーンでこのような委任が多数ある場合、Opt-Outを使うと、それらのハッシュをNSEC3鎖から省略できます。
省略された名前は、別の名前のNSEC3レコードが持つ範囲の中に入ります。そのNSEC3レコードのOpt-Outフラグは、「この範囲には、鎖に登録されていないDSなしの委任が含まれている可能性がある」ことを示します。 フラグが委任名そのものに付くのではなく、その名前のハッシュを覆う側のNSEC3レコードに付く点に注意してください。
Opt-Outで実際のClosest Encloserのハッシュが鎖から省略されていると、NSEC3ではその名前の存在を直接証明できません。 その場合、実際のClosest Encloserではなく、NSEC3で存在を証明できる最も近い祖先をClosest Provable Encloser(最近接証明可能祖先名)として使います。Next Closer Nameも、このClosest Provable Encloserから問い合わせ名へ向かって一つラベルを加えた名前として求めます。 RFC 5155では、Opt-Out zoneではClosest EncloserとClosest Provable Encloserが異なる場合があると定義しています。
親ゾーンの名前空間とNSEC3ハッシュ鎖(模式図)
親ゾーン:
signed.example. NS + DS ← DSで子のDNSKEYを認証できる。ハッシュ鎖に含める
child.example. NS, DSなし ← 親から子のDNSKEYを認証できない。Opt-Outで省略可能
ハッシュ値の順序:
... H(signed.example.)
───── Opt-Outフラグ付きNSEC3の範囲 ─────
(この間に H(child.example.) があっても鎖には載せない)
→ 次に登録されているハッシュ ...
この仕組みで検証できるのは、主に委任先の DSがないことです。リゾルバーは委任先へのDS問い合わせに対する証拠として、問い合わせ名のハッシュを覆うOpt-Outフラグ付きNSEC3を使い、その委任を「DNSSECで保護されていない(insecure)」と扱えます。 これは子ゾーンのDNSKEYやRRSIGを検証して「secure」と認定することとは異なります。DS不在の証明により、検証状態を「insecure」と判断する処理です。
Opt-Outの範囲は、未署名委任のハッシュだけでなく、委任ではない名前のハッシュや実在しない名前のハッシュも含み得ます。そのため、Opt-Out付きの範囲だけでは、任意の名前が存在しないこと(NXDOMAIN)や、範囲内の名前が必ず未署名委任であることは証明できません。 通常のNXDOMAIN証明で必要になるNext Closer Nameやワイルドカード名の不在証明と、DS不在の証明は異なるということです。
このページで扱う unsigned.optout.mismatch の例では、存在するDSなし委任 unsigned を覆うOpt-Outフラグ付きNSEC3レコードの Next Hashed Owner Name を、その委任名 unsigned のハッシュに書き換えています。
NSEC3レコードはOwner Nameのハッシュに対応する名前が存在することを示し、Next Hashed Owner Nameは鎖上で次にある名前のハッシュを示します。その間には、(Opt-Outで省略された委任を除き)鎖に登録されたハッシュがありません。
したがって「Owner NameからNext Hashed Owner Nameまでの範囲」は、両端の名前自体を含む区間ではなく、二つの端点の間にある値を指します。端点はそれぞれ鎖上に存在する名前を表し、範囲内の不在名とは区別されます。
今回は上端のNext Hashed Owner Nameを unsigned のハッシュと同じ値にしたため、そのハッシュは端点に一致し、範囲内には入りません。このためDS不在の証拠として使えなくなります。
ここで壊しているのは証明範囲であり、NSEC3署名そのものではありません(変更後のレコードは再署名されています)。