Почему валидации email регуляркой недостаточно. Проверка MX-записей с примерами на PHP и Ruby

в 0:12, , рубрики: cms, email-рассылки, mx record, php, ruby, ruby on rails, управление проектами

Уж сколько раз твердили Миру… Существует давний и, вероятно, нескончаемый спор о том, какой именно регуляркой правильно и нужно проверять поле email пользователя.
Да, проверять регуляркой действительно нужно. Но ведь наши продукты работают в Сети. Так почему бы не использовать её настоящую мощь?

К тому-же нередко бывают ситуации, когда пользователи реально ошибаются при вводе адреса email (в том числе и в домене). Ну или, в поле email вводят любую возможную «Хабракадабру», что легко пролетит через regexp, но никак не может быть почтой, потому что даже домена такого не существует в природе :)
Кстати, на этом вот нюансе мы буквально только что подзалетели: суть в том что на сайте, поднятом на одной, довольно популярной CMS-ке у нас почему-то прекратили идти email-уводемления.
Причиной, как выяснилось, стало попадание адреса рассыльщика в спам.
Причин было несколько:
1. CMS довольно популярная, а, стало быть, и регистрирующихся ботом-спамеров по неё немало. И что интереснее — в настройках можно (и многие так, к слову, и делают) — отключают проверку email. В этом случае сюда можно (и так большинство ботов и делает) вводить любую белиберду
2. Тексты писем не были переписаны со стандартных.

Итого: спамеры массово лезли регистрироваться, кидали скрипту левые email-ы, куда мы пытались отправлять письма. Фильтр же спама видел что с нашего email-а идёт ряд писем, с текстами, что он уже видел много раз с других email-адресов, и при этом немалое их количество валится на несуществующие email-адреса.
В общем почтовый адрес периодически подпадал под спам.

Посему опыту, соответственно, можно и нужно утверждать что проверка наличия домена в Интернете, а также — наличия на нём почтового сервиса (MX-записей для домена) — это то, что по идее должно существовать и работать в системах регистрации пользователей.
Собственно суть проверки довольно проста: при регистрации, на стадии валидации данных пользователя мы отщепляем домен от email-а, и смотрим что там есть по MX-ам.
Сложно? На самом деле нет. Но позволяет существенно снизить нагрузку на почтовые службы. И, кстати, гораздо реже попадать в спам-листы (ведь отсылка большого числа писем на несуществующие почтовые адреса — один из признаков спама).

На PHP, как ни странно сделать это довольно просто:

$email ="11@sdlkfjsdl.co.uk";
$domain = substr(strrchr($email, "@"), 1);
$res = getmxrr($domain, $mx_records, $mx_weight);
if (false == $res || 0 == count($mx_records) || (1 == count($mx_records) && ($mx_records[0] == null  || $mx_records[0] == "0.0.0.0" ) ) ){
//Проверка не пройдена - нормальные mx-записи не обнаружены
	echo "No MX for domain: $domain";
}else{
//Проверка пройдена, живая MX-запись на домене есть, и почта на нём работает
	echo "It seems that we have qualify MX-records for domain: $domain";
}

Поясню по довольно «монструозному» if-у. Дело в том, что в документации к функции getmxrr были комментарии с упоминаниями про не совсем корректное его поведение. И хотя на php7.1 мне их обнаружить не удалось — лишняя проверка — не лишняя :)

На ruby это делается схожим образом:

domain = invite.email.split('@').last.mb_chars.downcase.to_s.force_encoding("UTF-8")
#На случай, если домен русскоязычный. Точнее уже не совсем помню зачем преобразовывал в UTF-8, но видимо нечто вылетало

mail_servers = Resolv::DNS.open.getresources(domain, Resolv::DNS::Resource::IN::MX)
if mail_servers.empty?
   #Нет MX-серверов. Нечего и пытаться сюда слать письма
   false
else
   true
end

При этом уточню, что подобная проверка поля email может не только довольно серьёзно сказаться на качестве информации в базе данных вашего проекта (и снизить риск попадания рассыльщиков уведомлений в спам), но и повлечь за собой снижение нагрузок. Ведь отправка писем из скрипта — довольно не быстрый на практике процесс.

Автор: ZiNTeR

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js