Проверка дали даден потребител притежава домейн с JavaScript

Техниката, която използваме, е тази, използвана от Google, Microsoft и други, за да проверят дали имате някакви права върху домейн. Така че, макар да не е безпогрешно, поне сме в добра компания!

Кодът в тази статия е TypeScript, но същият метод ще работи на повечето езици.

Преглед

Всички методи за проверка, които съм виждал за тях, разчитат на това, че потребителят може да модифицира сайта по някакъв начин - което има смисъл, тъй като вие проверявате дали имат контрол върху сайта, който се опитват да използват.

Повечето от тях изглежда са се спрели на използването на някаква форма на запис в DNS - специален запис, който могат да проверят, че действително съществува.

Бързо DNS въведение

Това е много кратко; за (малко) по-пълно въведение в DNS вижте моята друга публикация.

Системата за имена на домейни се състои от записи, даващи информация на компютри, които имат достъп до интернет. Има доста различни видове записи. Най-основният се нарича A запис, A за адрес. По същество се казва „този текст — foobar.example.com — сочи към този IP адрес“.

Има редица запазени адреси, които имат определено значение. Един полезен адрес е 127.0.0.1 - това винаги означава "този компютър". Символичното име за него е localhost.

Планът

Искаме да проверим дали потребителят може да променя DNS записите за този домейн, но не с нещо особено разрушително или сложно – колкото по-сложно го правим, толкова по-вероятно е грешката на потребителя да се промъкне.

Най-простият начин - генерирайте произволен поддомейн и ги накарайте да създадат A запис, сочещ към 127.0.0.1.

Генериране на псевдоним

Има много различни начини да направите това. Избрах да използвам модула Node uuid и да взема първите 8 знака. 8 беше избрано, защото беше достатъчно произволно за нашите цели и защото беше първата „бучка“ във v4 UUID.

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

Проверка на псевдонима

С помощта на Node dns module можем да разрешим създадения от нас псевдоним; ние добавяме домейна на потребителя след него, което прави alias поддомейн.

Обикновените dns методи са базирани на обратно извикване; той също така предоставя dnsPromises набор от API, които са базирани на Promise. Ще използваме „този метод за разрешаване“ за удобство.

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`);
  }
}

Очакваме резултатът от търсенето да бъде единичен запис, 127.0.0.1 - ако е така, тогава го нарекохме проверен. И накрая, ние се уверяваме, че данните отразяват това, което току-що намерихме.

Изпълнение на проверки във фонов режим

Вече имаме функция, която можем да използваме за проверка на домейни. Последният етап е да се изпълнява периодично във фонов режим, а не при поискване.

Изпълнението, което използвах, е по-долу. Не съм включил помощните функции (като getAllSites), но кодът трябва да е разбираем и без тях.

startBackground използва DOMAIN_VERIFY_PERIOD_SECONDS от средата, ако е дефинирана, или 300 секунди, ако не е. След това използва setInterval, за да насрочи verifySites. setInterval приема милисекунди като аргумент, така че първо го конвертираме.

verifySites просто получава текущия списък със сайтове и изпълнява verifySite на всички тях.

И накрая, stopBackground ще отмени функцията за интервал, ако е планирано да се изпълнява.

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;
  }
}

И това е – тези функции са достатъчни, за да започнете да проверявате домейни във фонов режим. Уведомете ме, ако го използвате!