@dv=1;salts=[(s=salts.domainverification.org;ids=[342c208d-0523-4d22-b7dd-32952dbeace2]);(s=example.com;ids=[90797a69-205b-4a35-88fe-8a186392ea15])]
Specification
This document specifies version 1 of the Domain Verification protocol.
1. Terminology
The following terminology is used throughout this document:
Service Provider |
An entity that provide products and services attached to domain names. Examples include SEO tools (e.g. Google Search Console), Social Media tools (e.g. Facebook Business Manager), Search Engine listings (Google Business Profile). |
DNS Provider |
An entity that provide DNS services – including registrars (e.g. GoDaddy) and standalone DNS services (e.g. CloudFlare). |
User |
An entity using a service provided by a Service Provider |
Domain Owner |
An entity with ownership and responsibility over a domain name |
Verifiable Identifier |
An identifier that a Service Provider can verify through automated means. For example: by sending a verification link to an email address; sending an SMS verification code to a phone number. |
Domain Verification Record |
A DNS TXT record for the Domain Verification protocol, identified as such by starting with the string |
Association Record |
A DNS TXT record that associates an authorised party with a domain name. |
Salt Reference Record |
A DNS TXT record that lists Salt Store locations and Salt IDs, so that salts can be looked up. |
2. Introduction
2.1. Problem
Domain verification is performed by hundreds of service providers, but it’s a complex process for users, requiring:
-
Access to website source files; or
-
Access to DNS records
Service providers issue instructions for users to verify domain names but these instructions are often beyond the technical capabilities of the layperson. This causes significant onboarding friction, customer support requests and can pose risk to the stability of the website, email or other services operating on the domain name.
2.2. Goal
The goal of the Domain Verification protocol is to make it as easy to verify a domain name as it is to verify an email address or telephone number.
2.3. Solution
The Domain Verification protocol uses DNS TXT records to create associations between a domain name and an authorised party. Authorised parties are identified using verifiable identifiers. Domain name registrars could automatically create Domain Verification records upon domain name registration, clearing a simple path for domain registrants to automatically verify domain names.
3. Privacy
By its nature, the Domain Verification protocol makes it possible to confirm an association between a verifiable identifier (e.g. email address/telephone number) and a domain name. The following steps have been taken to ensure this association is as private as possible:
3.1. SHA-256 Digest
Verifiable identifiers are only ever stored as SHA-256 digests, importantly these digests are not stored in a DNS TXT record using a standardised DNS name, where they could be harvested to facilitate hash breaking attempts. The digest is used as a DNS label within the DNS name – requiring a DNS query for each attempt to break the hash.
4. Verifiable Identifiers
This specification makes no determination about what should be considered a verifiable identifier – this protocol is intended for email addresses and telephone numbers, but could be used for any unique identifier that is verifiable.
4.1. Email Addresses
Email addresses must be trimmed of white space and downcased before being used as verifiable identifiers.
4.2. Telephone Numbers
Telephone numbers must be in E.164 format (e.g. +441234567890) before being used as verifiable identifiers.
5. DNS Names
The Domain Verification protocol uses DNS names to create and verify associations between authorised parties and domain names. Associations can be "hidden" or "secret":
5.1. Hidden Association
To create/verify a hidden association between the authorised party at user@example.com
and domain name dvexample.com
we use a DNS name with a base 36 encoded SHA-256 digest of the email address as the first label in the DNS name, and _dv
as the second, like this: 4i7ozur385y5nsqoo0mg0mxv6t9333s2rarxrtvlpag1gsk8pg._dv.dvexample.com
A hidden association can only be verified by those who know where to look - they must know the domain name and email address, and suspect an association between the two.
5.2. Secret Association
To create/verify a secret association between the authorised party at user@example.com
and domain name dvexample.com
we use a DNS name with a base 36 encoded SHA-256 digest of the salted email address as the first label in the DNS name, and _dv
as the second.
It is not sufficient to know only the email address and domain name to verify a secret association, the salt must also be known.
Assuming a salt of 0E)W2!CohH2=?jF*5Sdjia4s(pnypXQZ3Cy!Duco
, the DNS name required to create/verify this association would be 6afvgus1jp324e7j06htlzy6zpn7iji80gihjp7cx1iaeb0nju._dv.dvexample.com
Secret associations require a salt to be set and a method to retrieve it.
5.2.1. Salt References
To enable secret associations for dvexample.com
, a Salt Reference Record must be created/retreived using the DNS name _dv.dvexample.com
6. Record Contents
Domain Verification records are written in CompactData, a JSON-like data serialisation language designed for efficient DNS storage. All records must start with @dv=X;
, where X is a version number.
6.1. Association Record
The following keys are valid in an association record:
@dv |
Required and must be the first key. This key identifies the DNS TXT record as a Domain Verification record. The value sets the protocol version number, the current protocol version number is 1. |
h |
Required. The [optionally, salted and] hashed verifiable indentifier. This enables a Domain Verification library to ensure the resource record is intended for the DNS name queried and not a wildcard resource record, or otherwise incorrectly created record. |
s |
Required. Service Types this associated party is authorised for. See Permissions. |
p |
Optional. Service Providers this associated party is authorised for. See Permissions. |
sn |
Optional. Services this associated party is authorised for. See Permissions. |
d |
Optional. A description of the authorised party this association relates to. Since the only reference to the authorised party is a hashed verifiable identifier, it can be helpful to store descriptive text to make it easier to maintain your Domain Verification records in the future. It is not recommended to store the verifiable identifier in clear text, since although you could assume anyone that can query the record already knows the verifiable identifier, the security of DNS transport varies depending on resolver implementation and resource records are routinely stored in resolver caches. |
e |
Optional. Expiry date in YYYY-MM-DD format. This sets an association expiry date, this is useful when granting a third party authorisation for temporary access. |
6.2. Salt Reference Record
A Salt Reference record lists references for the salts used in secret associations for this domain:
-
@dv=1
– indicates this is a Domain Verification record and the version number -
salts=[…]
– an array of salts used for Domain Verification records on this domain:-
(s=salts.domainverification.org;ids=342c208d-0523-4d22-b7dd-32952dbeace2])
– a CompactData map containing two keys, one defining the Salt Store (s
) and another defining theids
of the salts used. -
(s=example.com;ids=[90797a69-205b-4a35-88fe-8a186392ea15])
– as above, but setting a different store and salt ID.
-
Using these references, we can lookup each salt with each salt store (see Salt Lookup).
6.3. Permissions
Permissions can be set by service type, provider and service name. Each permission is in addition to the last.
6.3.1. By Service Type
Permissions for service types are defined in an array using the key s
, like this s=[seo;marketing]
, with the following values recognised:
-
all
to grant permissions to all services. -
seo
to give permission for the domain to be verified for SEO-related services. -
marketing
to give permission for the domain to be verified for marketing services. -
email
to give permission for the domain to be verified for email services. -
storage
to give permission for the domain to be verified for online storage services.
6.3.2. By Service Provider
Permissions for service providers are in addition to any settings above and are defined in an array using the key p
, like this p=[provider1.com;provider2.com]
.
6.3.3. By Service Name
Permissions for service names are in addition to any settings above and are defined in an array using the key sn
, like this sn=[service1.provider3.com]
.
6.3.4. Example
For example, if a domain name owner wanted to give permission to their SEO agency to verify their domain, they could use the category seo
to give their SEO agency the ability to verify the domain on a service that identifies itself as SEO-related, and use the service name analytics.google.com
for Google Analytics. The Domain Verification record looks like this:
@dv=1;s=[seo];sn=[analytics.google.com]
6.4. Self Certification
It’s important to note that service providers certify themselves as a provider of a certain service type, or service name. Since the interests of domain owners and service providers are aligned – neither want someone to have incorrect permissions over a domain name – this is not expected to cause problems.
7. Record Lookup
Domain Verification records are found using DNS queries specifying the TXT
record type.
7.1. Association Lookup
A query must be made for the hidden association record first using the specified DNS name for a Hidden Association:
dig 4i7ozur385y5nsqoo0mg0mxv6t9333s2rarxrtvlpag1gsk8pg._dv.dvexample.com TXT +short
-> "@dv=1;s=[all]"
If the query fails or an invalid record is returned, a failover query must be made for a secret association record. Before we can query for a secret association record, we must first make a Salt Reference Lookup and subsequent Salt Lookup.
Once we’ve retrieved the salt, we can query for a secret association using the specified DNS name for a Secret Association:
dig 6afvgus1jp324e7j06htlzy6zpn7iji80gihjp7cx1iaeb0nju._dv.dvexample.com TXT +short
-> "@dv=1;s=[all]"
If multiple salts have been used for secret associations on this domain, we should repeat this process for each salt in the order defined in the array, until a valid Domain Verification record is found or not.
7.2. Salt Reference Lookup
A salt reference lookup is made like this:
dig _dv.dvexample.com TXT +short
-> "@dv=1;salts=[(s=salts.domainverification.org;ids=[342c208d-0523-4d22-b7dd-32952dbeace2])]"
For each salt reference returned in the Salt Reference Record, we must perform a Salt Lookup.
8. Salt Lookup
Licensed service providers can lookup salts in our salt store using the salt lookup service available at salts.domainverification.org
, other salt stores should follow the same simple format
9. Worked Example
In this worked example, we create associations between multiple authorised parties and the domain dvexample.com
9.1. Hidden Association
Let’s create a hidden association between the authorised party identified by the email address someone@example.com
and the domain name dvexample.com
and allow the authorised party to verify the domain for all marketing services and a service called hosting
by the provider serviceprovider.com
9.1.1. Creating the Record
For a hidden association, we only need to create one Domain Verification record, the DNS name is derived as indicated by the following pseudo code representation:
SHA-256("someone@example.com")
-> 72497f475e4f76d0b28f57c73a084ece576d170874eba3ee2609d9afe4b71aab
Base-36("72497f475e4f76d0b28f57c73a084ece576d170874eba3ee2609d9afe4b71aab")
-> 2ujmt78p82bjs6asang9sy569ykmm1dcg171ssnhgjrh9wlmsr
Suffix with "._dv.dvexample.com"
-> 2ujmt78p82bjs6asang9sy569ykmm1dcg171ssnhgjrh9wlmsr._dv.dvexample.com
By creating a DNS TXT record at the above location we create an association between the verifiable identifier someone@example.com
and the domain name dvexample.com
, to set permissions for this authorised party to verify the domain on all services in the marketing
category and a service named hosting
for the provider serviceprovider.com
we use the following content for the DNS TXT record:
@dv=1;s=[marketing];sn=[hosting.serviceprovider.com]
There’s more information about syntax for Permissions.
9.1.2. Performing the Lookup
To lookup a hidden association between the verifiable identifier someone@example.com
and the domain name dvexample.com
we query the same DNS name explained in the previous section:
dig 2ujmt78p82bjs6asang9sy569ykmm1dcg171ssnhgjrh9wlmsr._dv.dvexample.com TXT +short
-> "@dv=1;s=[marketing];sn=[hosting.serviceprovider.com]"
9.2. Secret Association
Let’s create a secret association between the authorised party identified by the email address anonymous@example.com
and the domain name dvexample.com
, and allow the authorised party to verify the domain for all services.
9.2.1. Creating the Records
For a secret association, we need to create two Domain Verification records. Firstly, we need to create a salt reference record. The contents of this record will depend on the tool used to create the record, the following example uses our tool to create a record.
We create the salt reference record at _dv.dvexample.com
with the following content:
@dv=1;salts=[(s=salts.domainverification.org;ids=[342c208d-0523-4d22-b7dd-32952dbeace2])]
A second record is created using a DNS name derived from a SHA-256 digest of the salted verifiable identifier.
Assuming the salt is:
0E)W2!CohH2=?jF*5Sdjia4s(pnypXQZ3Cy!Duco
We prefix the salt to the verifiable identifier, hash the string and base 36 encode the digest as the first DNS label. We use _dv
as the second label. The full DNS name is:
5zvt9zocn4iml3afmaesj1l1zid5d7mtgz8054r3kq2ilnhwed._dv.dvexample.com
The contents of the record are:
@dv=1;s=[all]
9.2.2. Using Mulitple Salt Stores
Sometimes your association records will be created by multiple parties and so you may need to store references for multiple salts. This is as simple as storing each in an array:
@dv=1;salts=[(s=salts.domainverification.org;ids=[342c208d-0523-4d22-b7dd-32952dbeace2]);(s=example.com;ids=[90797a69-205b-4a35-88fe-8a186392ea15])]
9.2.3. Performing the Lookups
The first step is to query the hidden association record (see Performing the Lookup), if that query fails or the returned record is invalid, the next step is to query the Salt Reference Record.
We query the salt reference record like this:
dig _dv.dvexample.com TXT +short
-> "@dv=1;salts=[(s=salts.domainverification.org;ids=[342c208d-0523-4d22-b7dd-32952dbeace2])
We take the salts
array and work through each object to make a Salt Lookup, we send a HTTP POST
to the address salts.domainverification.org
and pass a token
and saltID
:
curl https://salts.domainverification.org -d "token=#{YOUR_TOKEN_HERE}&saltId=342c208d-0523-4d22-b7dd-32952dbeace2"
-> {"salt":"0E)W2!CohH2=?jF*5Sdjia4s(pnypXQZ3Cy!Duco"}
Now we have the domain (dvexample.com
), the verifiable identifier (anonymous@example.com
) and the salt used for secret association records on this domain (0E)W2!CohH2=?jF*5Sdjia4s(pnypXQZ3Cy!Duco
), we can query for a secret association record using a DNS name derived as indicated by the following pseudo code representation:
Prefix "anonymous@example.com" with "0E)W2!CohH2=?jF*5Sdjia4s(pnypXQZ3Cy!Duco"
-> 0E)W2!CohH2=?jF*5Sdjia4s(pnypXQZ3Cy!Ducoanonymous@example.com
SHA-256("0E)W2!CohH2=?jF*5Sdjia4s(pnypXQZ3Cy!Ducoanonymous@example.com")
-> f807b5609eae64257bf4877652ea49fee40ac2451c152c12fa596ffeda647157
Base-36("f807b5609eae64257bf4877652ea49fee40ac2451c152c12fa596ffeda647157")
-> 66jpxxlzk088ws8q7pmwz72ck1626hlxd6txja5x01bfwhhnbr
Suffix with "._dv.dvexample.com"
-> 66jpxxlzk088ws8q7pmwz72ck1626hlxd6txja5x01bfwhhnbr._dv.dvexample.com
To verify the secret association and fetch permissions, we query the record in DNS:
dig 66jpxxlzk088ws8q7pmwz72ck1626hlxd6txja5x01bfwhhnbr._dv.dvexample.com TXT +short
-> "@dv=1;s=[all]"