Как стать автором
Обновить

Комментарии 27

Да, проверять регуляркой действительно нужно.

Странно, я постоянно читаю, что проверять регуляркой НЕ нужно https://habr.com/post/175375/

Ну, как-минимум наличие домена у адреса электронной почты проверять то уж точно нужно :)
Без него как-бы никуда. Например, видел ошибки когда точку путают с запятой.

А смысл? Если юзер указал некорректный домен, то отправленное письмо не придёт никуда — оно вообще не отправится дальше вашего собственного SMTP, который вас банить не должен. Подозреваю, что проблему вызвала попытка отправлять почту через SMTP gmail или подобного сервиса… ну, тут скорее ССЗБ и проблема решается использованием собственного SMTP, а не валидацией MX.

НЛО прилетело и опубликовало эту надпись здесь

Это незначительное улучшение UX (которое легко может стать ухудшением, если ожидание результатов MX-запроса будет блокировать отправку формы юзером). К реальной валидации email данная фича имеет очень слабое отношение.

Есть проблема, что Google быстро начнет банить ваш собственный SMTP за рассылку похожих писем. Собственно я сам с таким столкнулся.

Аналогично. Я вообще подумывал попробовать устанавливать соединение на 25-м порту, либо — пытаться сделать одинаковые письма «разными» :)
А скорее всего — по хорошему нужно делать и то, и другое…

Google обычно банит не сам, а используя информацию сторонних сервисов вроде spamhaus.org. При корректной настройке своего SMTP (включая reverse DNS, SPF, etc.) и рассылке легальных писем а не реального спама обычно это проблема решаемая.

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

Тема обсасывалась уже не единожды. Единственно верный способ проверить валидность email-а — это отправить на него письмо и не получить отлуп от почтового сервера. Остальные способы — лишь некоторая вероятность того, что email валидный.
и не получить отлуп от почтового сервера
И получить от пользователя желаемую реакцию, например, переход по url-у.
А отсутствие отлупа не означает отсутствия ящика. Например, корпоративные сервера нередко настраивают так, что они принимают всю входящую почту подряд, потом сортируют по ящикам, а лишнее удаляют или отправляют в дефолтный ящик.
В Symfony, кстати, валидацию email по проверке MX и A-записей объявили устаревшим, по причине того, что это не является надежным способом проверки, поскольку зависит от состояния сети и некоторые действительные сервера отказываются отвечать на эти запросы.
Стоит так же упомянуть что страница мануала для getmxrr четко говорит: Эта функция не должна использоваться для проверки адреса., что как бы намекает.
Мы проверяем не то, что существует конкрентный email, а то, что домен, у которого данный email был прописан — он в принципе имеет mail сервер для обработки писем…
Отвечают не сами сервера, а DNS-ы. И если вы не отрезолвисились по DNS на сервер MX — стало быть никакого письма вы отправить не сможете точно.

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

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


Без mb_chars не будет downcase для русских символов работать. Скорее всего после этого преобразование в обычную рубишную строку обратно.
Это ясно. Я просто не помню зачем мне была нужна кодировка utf-8. Правда так хуже точно не будет :)
на самом деле наличие force_encoding скорее всего говорит о том что где то что то не так и лучше избавиться от проблем с кодировкой на архитектурном уровне.
А что только домены проверяются? Можно проверять и сам ящик без отправки письма на него?
Неплохая идея. Но тогда вам нужно будет устанавливать полноценное соединение через c почтовым серверов, обменяться приветствиями, потом заявить что «я хочу отправить письмо такому-то», и если получаете ответ «да, это возможно» — обрываете соединение, отправляете письмо стандартными средствами. Но это довольно небыстро по времени :)
Этот способ также некорректный.

Случай первый:
RFC 974 определяет порядок маршрутизации почты:

There is one other special case. If the response contains an answer
which is a CNAME RR, it indicates that REMOTE is actually an alias
for some other domain name. The query should be repeated with the
canonical domain name.

В случае, если домен является алиасом, ваш код посчитает, что это невалидный домен.

Случай второй:
Раздел 5 RFC5321 определяет корректное поведение отправителя почты:
The lookup first attempts to locate an MX record associated with the
name. If a CNAME record is found, the resulting name is processed as
if it were the initial name. If a non-existent domain error is
returned, this situation MUST be reported as an error. If a
temporary error is returned, the message MUST be queued and retried
later (see Section 4.5.4.1). If an empty list of MXs is returned,
the address is treated as if it was associated with an implicit MX
RR, with a preference of 0, pointing to that host.
If MX records are
present, but none of them are usable, or the implicit MX is unusable,
this situation MUST be reported as an error.

То есть в случае отсутствия MX-записи доставка нормальным образом по стандарту происходит по A-записи домена.

Кроме того, почта может быть доставлена по SRV-записям, но предложенный Вами код этого конечно же не учитывает.

Вообще говоря, домены электронной почты не обязаны быть доступны постоянно. Стандарт предлагает такую стратегию доставки:
The sender MUST delay retrying a particular destination after one
attempt has failed. In general, the retry interval SHOULD be at
least 30 minutes; however, more sophisticated and variable strategies
will be beneficial when the SMTP client can determine the reason for
non-delivery.

Retries continue until the message is transmitted or the sender gives
up; the give-up time generally needs to be at least 4-5 days. It MAY
be appropriate to set a shorter maximum number of retries for non-
delivery notifications and equivalent error messages than for
standard messages. The parameters to the retry algorithm MUST be
configurable.


То есть чтобы убедиться, что домен мёртвый, его нужно наблюдать хотя бы 4-5 дней.

В общем, такой способ проверки по большому счёту — мусор. Сложившийся стандарт в индустрии по этому вопросу — отправить письмо с подтверждением адреса и до подтверждения не включать в списки рассылок.
Ну на самом деле, по крайней мере в современной практике получается так, что если не получилось отправить письмо прямо сейчас — значит почтовый сервис де-факто не работает.
Но вы натолкнули меня на мысль: отсутствие MX-записи по факту ещё не означает полный отлуп (хоть в немалом количестве случае это, скорее всего и будет), но является поводом призадуматься.
Поэтому да, логичнее не увидев MX-записи, посмотреть в A и в CNAME — если не будет и их — тогда это гарантированный отлуп.
Если есть CNAME — берём адрес или домен из него. и Повторяем цикл.
Как только по циклу дошли до ip-адреса — смотрим наличие 25-го порта. Если он открыт и представляется — тогда ок, почта есть.
Если нет — отлуп.
Хотя да, думаю что вы правы — это довольно геморройный уже получается с точки зрения написания способ проверки.
Но по крайней мере можно сказать одно: нет mx, cname и a-записей — это однозначный отлуп и почты наверняка нет.
А что это даёт?

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

И по итогу всё решит подтвердил ли юзер по ссылке свой адрес или нет.



Есть ещё вот какой неприятный момент: как вызывать эту функцию получения значений записей из DNS. Эта функция в PHP не имеет настраиваемых ограничений по таймауту и сколько она проработает в итоге зависит от платформы и конфигурации системного резолвера. Может полминуты запросто, если нэймсервер домена просто молчит. Возникает ситуация, что можно случайно или умышленно создать ситуацию, когда все воркеры PHP будут застопорены ожиданием какого-нибудь кривого или заблокированного РКН домена.

Это, конечно, можно решить, если такие запросы прогонять через очередь и их результаты кое-как кэшировать, однако это становится уже громоздко и всё ещё не нужно ни для чего.
Периодически я устраиваю проверку наличия адреса отправителя — больше половины спама валится с реальных адресов. Хуже того, часть из них даже с валидной DKIM подписью.

Из этого можно сделать вывод что для спаммеров не представляет никакого труда использовать реальные адреса, если нужно — возможно, угнанные, хакнутые, или просто созданные массово с нужной целью.

Что касается регулярок — помню был у клиента адрес типа ..xx|zz..@ — и он оказался работающим (попал в базу до того как email стал проверяться на фронтэнде и прошел валидацию через отправку кода).

Так что, как уже говорили раньше, единственный способ убедится в реальности адреса — это получить от пользователя подтверждение после отправки ему кода валидации или URL (причём последний должен быть с явным подтверждением после нажатия, или его могут случайно «подтвердить» антивирусы и/или антиспамы).

Единственное что можно улучшить — это интегрировать валидацию в процесс регистрации (или смены адреса), т.е. делать её немедленно после подтверждения email, и говорить пользователю если адрес получил отлуп. Но этот путь тернист, ибо ведет к потенциальной DoS, а также может дать сбой если сервер временно недоступен.
Интересно, что к функции getmxrr() есть примечание в документации:

This function should not be used for the purposes of address verification. Only the mailexchangers found in DNS are returned, however, according to » RFC 2821 when no mail exchangers are listed, hostname itself should be used as the only mail exchanger with a priority of 0.
У меня возникает мысль, что для рассылок нужно использовать соответствующие системы. SendGrip, MailChimp или любой другой подобный. Они, конечно, стоят денег, но при этом вы, как пользователь, не несёте риски, связанные с верификацией адреса клиента и попаданием своего пула адресов и доменов в черные списки. Интересно обсудить насколько это верный (идеологически и экономически) подход. Или всё-таки для проектов среднего масштаба придется строить свои велосипеды…
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации