Checking if a user owns a domain

arafel

Paul Walker

Posted on April 3, 2021

Checking if a user owns a domain

The technique we’re using is the one used by Google, Microsoft and others to verify that you've got some authority over a domain. So while it's not foolproof, at least we're in good company!

The code in this article is TypeScript, but the same method would work in most languages.

Overview

All of the verification methods I've seen rely on the user being able to modify the site in some way - which makes sense, since you're checking if they have control over the site they're trying to use.

Most of them seem to have settled on using some form of DNS entry - a special record which they can check actually exists.

Quick DNS intro

This is very brief; for a (slightly) fuller introduction to the DNS, see my other post.

The Domain Name System consists of records giving information to computers accessing the internet. There are quite a few different types of record. The most basic one is called an A record, A for address. It essentially says "this text - foobar.example.com - points to this IP address".

There are a number of reserved addresses which have particular meanings. One useful address is 127.0.0.1 - that always means "this computer". The symbolic name for it is localhost.

The plan

We want to check the user can modify the DNS entries for that domain, but not with anything particularly disruptive or complicated - the more complicated we make it the more likely it is that user error will creep in.

The simplest way - generate a random subdomain and have them create an A record pointing to 127.0.0.1.

Generating an alias

There are many different ways to do this. I chose to use the Node uuid module and take the first 8 characters. 8 was chosen because it was random enough for our purposes, and because it was the first 'lump' in the v4 UUID.

siteDetails["alias"] = uuid().substr(0, 8);

Enter fullscreen mode Exit fullscreen mode

Checking the alias

Using the Node dns module we can resolve the alias we created; we append domain after it, making alias a subdomain.

The plain dns methods are callback based; it also supplies a dnsPromises set of APIs which are Promise based. We’ll use that resolve methodfor convenience.

import dns from "dns";
const dnsPromises = dns.promises;

type Site = {
  alias: string; // Alias we'll be verifying
  domain: string; // Domain the user gave us
  verified: boolean; // Is it verified yet
}

async function verifySite(site: Site) {
  try {
    const res = await dnsPromises.resolve(site.alias + "." + site.domain);
    const valid = ((res.length == 1) && (res[0] == "127.0.0.1"));
    site.verified = valid;
  } catch (err) {
    console.error(`Error ${err} doing site ${site.id} verification`);
  }
}

Enter fullscreen mode Exit fullscreen mode

We’re expecting result of the lookup to be a single entry, 127.0.0.1 - if it is then we called it verified. Lastly, we make sure the data reflects what we just found.

Running checks in the background

We now have a function which we can use to verify domains. The last stage is to have it run periodically in the background, rather than on-demand.

The implementation I used is below. I haven’t included the utility functions (like getAllSites, but the code should still be understandable without those.

startBackground uses DOMAIN_VERIFY_PERIOD_SECONDS from the environment if it’s defined - if it isn’t it defaults to 300 seconds (5 minutes). It then uses setInterval to schedule verifySites. setInterval takes milliseconds as an argument, so we convert it first.

verifySites simply gets the current list of sites and runs verifySite on all of them.

Lastly, stopBackground will cancel the interval function if it’s been scheduled to run.

import { getAllSites } from "./queries";

let domainCheckId: NodeJS.Timeout | null = null;

export async function verifySites() {
  const sites: Site[] = await getAllSites();
  sites.forEach(site => verifySite(site));
}

export function startBackground(): void {
  const SECOND = 1000;
  const period: number = parseInt(process.env.DOMAIN_VERIFY_PERIOD_SECONDS || "300");
  console.log(`Starting domainCheck, period ${period} seconds`);

  domainCheckId = setInterval(verifySites, SECOND * period);
}

export function stopBackground(): void {
  if (domainCheckId) {
    clearInterval(domainCheckId);
    domainCheckId = null;
  }
}

Enter fullscreen mode Exit fullscreen mode

And that’s it - those functions are enough to start verifying domains in the background. Let me know if you use it!

💖 💪 🙅 🚩
arafel
Paul Walker

Posted on April 3, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Checking if a user owns a domain
javascript Checking if a user owns a domain

April 3, 2021