惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

H
Hackread – Cybersecurity News, Data Breaches, AI and More
www.infosecurity-magazine.com
www.infosecurity-magazine.com
C
Cisco Blogs
C
Cybersecurity and Infrastructure Security Agency CISA
P
Palo Alto Networks Blog
Security Latest
Security Latest
AWS News Blog
AWS News Blog
V
Vulnerabilities – Threatpost
C
Cyber Attacks, Cyber Crime and Cyber Security
N
News | PayPal Newsroom
S
Secure Thoughts
NISL@THU
NISL@THU
Application and Cybersecurity Blog
Application and Cybersecurity Blog
G
GRAHAM CLULEY
T
Troy Hunt's Blog
Recent Commits to openclaw:main
Recent Commits to openclaw:main
B
Blog RSS Feed
Latest news
Latest news
N
News and Events Feed by Topic
O
OpenAI News
IT之家
IT之家
Hacker News: Ask HN
Hacker News: Ask HN
H
Help Net Security
博客园_首页
MyScale Blog
MyScale Blog
Security Archives - TechRepublic
Security Archives - TechRepublic
Simon Willison's Weblog
Simon Willison's Weblog
Microsoft Azure Blog
Microsoft Azure Blog
P
Privacy International News Feed
Hacker News - Newest:
Hacker News - Newest: "LLM"
Cloudbric
Cloudbric
SecWiki News
SecWiki News
S
Security Affairs
L
LINUX DO - 热门话题
A
Arctic Wolf
T
Tor Project blog
博客园 - 聂微东
T
Tenable Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
爱范儿
爱范儿
G
Google Developers Blog
I
InfoQ
量子位
The Register - Security
The Register - Security
小众软件
小众软件
Apple Machine Learning Research
Apple Machine Learning Research
美团技术团队
H
Hacker News: Front Page
Recorded Future
Recorded Future

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Несколько собак и другие наши заблуждения об адресах электронной почты
PatientZero · 2026-06-18 · via Все публикации подряд на Хабре

Несколько собак и другие наши заблуждения об адресах электронной почты

15 мин

10

Поначалу некоторые из этих «вымыслов» могут показаться очевидными или неважными. Откровенно говоря, это не так уж далеко от истины. Однако позвольте мне нарисовать подробную забавную картину, демонстрирующую, что даже скучная электронная почта может неожиданным образом противоречить нашим ожиданиям.

Мы рассмотрим множество пограничных случаев, споткнёмся об маленькие препятствия и обнаружим, что некоторые технически корректные детали не всегда поддерживаются даже в больших системах наподобие Gmail (и, честно говоря, на то есть веские причины).

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

Мы можем легко принять, казалось бы, здравое решение, которое неожиданно вызовет проблемы. Кроме того, многоопытные разработчики (и старые системы) могут иметь ожидания, ранее бывшие корректными, но больше не работающие. Итак, без лишних предисловий, перейдём к вымыслам…

«Адреса электронной почты можно валидировать при помощи регулярных выражений»

Давайте сначала разоблачим самое простое заблуждение. Я далеко не первый человек, говорящий это онлайн, и определённо не буду последним. Но мне кажется, что повторять это полезно, потому что даже после тысяч постов, твитов и комментариев на Reddit этот антипаттерн упорно отказывается умирать даже в 2026 году.

Решение на основе регулярных выражений может вызвать инфаркт у вас, вашего бизнеса и клиентов тремя основными способами:

  1. Его достаточно затратно выполнять, при этом оно не обеспечивает практически никакой выгоды

    • Честно говоря, этот пункт раньше был болезненнее, но теперь мы приучились кидать простые запросы в огромные кластеры прожорливых GPU.

  2. Regex сложны, их магией владеют немногие, а реализации движков регулярных выражений несогласованы. Очень легко совершить ошибку, не осознавая этого.

    • В этом посте мы рассмотрим пару примеров, потенциально приводящих к ошибкам.

  3. Даже если вы скопипастили одно из «хороших» regex, мир продолжает эволюционировать. Валидное сегодня завтра превратится в легаси

    • Интернет замусорен советами, актуальными двадцать лет назад. А ещё он замусорен регулярными выражениями, которые были хороши двадцать лет назад, потому что легаси вечно.

Предупреждение о личном мнении: при валидации ввода важнее помогать пользователю, чтобы ему сложнее было совершать простые ошибки. Она должна быть достаточно строгой, чтобы упростить жизнь пользователя, но только ненамного. Не пользуйтесь валидацией ввода для защиты от своих пользователей; применяйте её для защиты пользователей от самих себя. В этом отношении есть вполне разумный аргумент об использовании regex-тестов для улучшения UX, о котором стоит поразмыслить. UX важен и я не отрицаю этого. Однако, попробую стать на мгновение адвокатом дьявола: возможно, риски перевешивают выгоду. В году 2026-м от Рождества Христова можно разумно ожидать, что пользователи умеют вводить собственный почтовый адрес или, что ещё лучше, автоматически вводят его из операционной системы, браузера, клавиатурного приложения или менеджера паролей.

Возможно даже, что плохо реализованная валидация форм отфильтровала больше людей, чем их собственная необходимость в поддержке. Следовательно:

  1. Не валидируйте адреса электронной почты. Если же это необходимо, то используйте простое клиентское regex, чтобы помочь пользователям избежать распространённых ошибок и опечаток.

    • Пытайтесь обеспечить как можно большую свободу. Используйте что-то типа ^[^@]+@[^@\s]+$, просто проверяющее, что пользователь ввёл «something@something»

  2. Если валидация есть в API или обработчике форм, то для согласованности с фронтендом используйте то же самое регулярное выражение.

    • Это возвращает нас к пункту «не используйте валидацию ввода для защиты себя от пользователей». По умолчанию защищайте себя санацией ввода, а не отказом от него.

  3. Верифицируйте адрес, не беспокойтесь о его валидации.

    • Отправьте письмо, пусть пользователь нажмёт на подтверждающую ссылку или введёт код верификации.

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

Итак, с самым простым разобрались, а теперь давайте перейдём к специфическим пунктам, из-за которых обработка почты становится забавной и сбивает с толку!

«Адрес электронной почты должен быть валидным, а сервисы электронной почты поддерживают любой валидный адрес»

Возможно, вас это шокирует, но Интернет состоит из ПО. И не существует какого-то единого стандартного ПО; на самом деле, оно очень часто расходится с нашими ожиданиями.

У ПО серверов электронной почты, от крупных сервисов наподобие Gmail до опенсорсных проектов наподобие Postfix, уровни поддержки официальных правил форматов электронной почты варьируются. Поддержка SMTPUTF8 появилась в Postfix ещё примерно в 2015 году, но её включили по умолчанию только спустя несколько лет. С другой стороны, Dovecot по-прежнему не поддерживает его даже в 2026 году. И это касается не только опенсорса: Gmail ограничивает список символов при создании адреса, однако, похоже, поддерживает отправку в UTF8.

Ниже мы подробнее рассмотрим SMTPUTF8 и RFC 6531. Но давайте взглянем на другой пример, в котором мы можем напрямую противоречить ограничениям всех соответствующих RFC. Посмотрим на RFC 5321 Section 4.5.3, где определяются предельные значения длин.

4.5.3.1.1.  Локальная часть

   Максимальная общая длина имени пользователя или другой локальной части равна 64
   октетам.

Это достаточно простое ограничение и его вполне легко понять! Но отдельные его части могут быть незнакомы для некоторых читателей:

  • «локальная часть» (local-part)

    • Если не усложнять, то локальная часть — это всё, что идёт перед символом @.

    • (В этом примере рассмотрим простой случай, но ниже снова вернёмся к локальной части)

  • октет

Следовательно, адрес электронной почты entirelytoomanycharactersinthisemailwhatisevenhappeningblahblahdonttrythisathome@gitpush–force.com, имеющий 80-байтную локальную часть, должен стопроцентно считаться невалидным. Так что, естественно, работать не должен.

Но помните, я говорил, что поведение ПО расходится с ожиданиями? На самом деле, вы сможете отправить мне письмо на этот адрес. Ваш провайдер разрешит его (скорее всего) без возражений, а мой провайдер без проблем доставит его в мои входящие.

«Адрес электронной почты может состоять только из символов ASCII»

Наверно, это заблуждение сильнее распространено в англоязычном мире, но мне любопытно: если вы живёте не в англосфере, то ожидаете ли, что в адресах обязательно должна использоваться латиница ASCII?

В 2012 году (это и совсем недавно, и очень давно по меркам мира технологий), возникла интернализация электронной почты, реализованная в наборе RFC, соответствующих различным частям стека почты. В частности, RFC 6531 определяет расширение SMTPUTF8, позволяющее использовать в локальной части адреса электронной почты с символами вне ASCII. Довольно удивительно то, что ещё всего 14 лет назад большинство населения Земли не могло указать в адресе почты собственное имя на собственном языке.

Международные символы теоретически работали и допускались даже до 2012 года, например, в рамках Punycode и RFC 3490 — хака кодирования, позволявшего представлять символы Unicode в ASCII. Но в те времена это возможно было только для имени домена, а локальная часть всё ещё была ограничена ASCII.

«Адрес электронной почты должен быть человекочитаемым»

Затронув латиницу, можно углубиться дальше. При интернализации локальная часть определяется в виде потока октетов. По сути, мы помещаем байты, не соответствующие валидному символу стандартного Unicode. Можно добавить в адрес почты �, и он останется валидным. Но этот символ нечитаем ни в одном человеческом языке.

«У адресов электронной почты всегда есть домен второго уровня (SLD) и домен верхнего уровня (TLD)»

Вспомните знакомые нам адреса, с которыми мы работаем повседневно. Остров @icloud.com в море @gmail.com, почта провайдеров, @employer.com и @school.edu. Все они соответствуют привычному паттерну: нечто-точка-нечто.

Существует три типа валидных адресов, не соответствующих ему; приведу их в порядке убывания важности.

  1. Адреса, имеющие поддомены, а значит, и несколько точек в домене.

    • Надеюсь, это вас не удивит. Если вы не живёте в США или Канаде, то, вероятно, видели домены вида .co.uk, .co.au.

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

  2. Адреса без точки

    • В реальном мире этот пункт важен для адресов, например, интранета, где каждая машина сети имеет имя хоста. Этот случай важно учитывать ПО для работы с почтой, но, вероятно, для большинства из нас он не актуален.

    • Строго говоря, кто-нибудь в ICANN, Verisign и так далее может зарегистрировать адрес наподобие admin@net, но давайте не вдаваться в такие фантазии.

  3. Адреса, в которых вместо имени домена используется IP-адрес. В RFC 5321 Section 4.1.3 напрямую указывается поддержка такого случая, он называется «литералы адреса».

    • Этот случай встречается очень редко, но вполне допустим в реальном мире.

    • Кстати, вы можете написать мне и на ben@[50.169.39.178], если такой формат поддерживает ваш почтовый клиент.

      • К сожалению, у веб-клиента Gmail, похоже, есть с этим проблемы. Но я думаю, что если вы будете использовать клиент наподобие Thunderbird, то это всё же сработает через Gmail.

      • Веб-клиент Gmail отправляет письма, но при моём тестировании он, похоже, вырезает квадратные скобки [], определённые в разделе «Address Literals» RFC, и сообщения отправляются на безымянный хост.

«У электронных адресов всегда есть "нормальный" TLD»

Мой личный адрес почты находится не в .com или .net, а в домене .email. И это очень сильно мне нравится, ведь я дурачок, любящий всякие приколы и странности просто из-за их прикольности и странности.

Однако мне пришлось создать аналог на домене .net, потому что оказалось, что множество компаний выполняет логику валидации, которая, похоже, не допускает домены, не входящие в привычный список: .com, .net, .org, .edu и так далее.

«В адресе электронной почты может быть только один символ @»

Примечание: с развенчанием этого заблуждения у меня возникли трудности; возможно, я неправильно понял RFC. Я добавил этот раздел на случай, если кто-нибудь подскажет, что же я упускаю! Как я уже говорил, со мной можно связаться по адресу ben@[50.169.39.178], если его поддерживает ваш клиент, и добавил, что у Gmail как будто есть с ним проблемы. Но в данном случае Gmail просто отказывается отправлять письмо.

Так что, если Gmail не поддерживает его в явном виде, то, вероятно, и вам тоже не стоит. Но теоретически, согласно RFC 5322 Section 3.2.4, это допустимо:

   Строки символов, содержащие символы, не входящие в допускаемые в атомах,
   можно представить в формате закавыченной строки, где
   символы окружены символами кавычек (DQUOTE, ASCII-значение 34).

   qtext           =   %d33 /             ; Печатные символы
                       %d35-91 /          ;  US-ASCII, исключая
                       %d93-126 /         ;  "\" и символ кавычки
                       obs-qtext

   qcontent        =   qtext / quoted-pair

   quoted-string   =   [CFWS]
                       DQUOTE *([FWS] qcontent) [FWS] DQUOTE
                       [CFWS]

Чтение RFC иногда бывает скучным занятием; по сути, здесь говорится, что любой символ ASCII от 33 до 126, за исключением 34 (самого символа кавычки) и 92 (символа обратной косой черты), может быть закавыченной строкой в локальной части адреса электронной почты.

То есть, строго говоря, ben"@“lolwat@gitpush--force.com должен быть валидным адресом, но я не смог найти клиента, позволяющего указать его, а писать скрипт для отправки по SMTP мне было слишком лениво.

«Точки в имени пользователя/локальной части опциональны»

Когда я был Очень Крутым Подростком™, я зарегистрировал почту X.Darth.Monkey.X@gmail.com, потому что именно так в те времена поступали Очень Крутые Подростки™, имеющие приглашение в бету Gmail. Со временем мне стало лениво и я перестал использовать точки при вводе своего адреса. xdarthmonkeyx@gmail.com работал точно так же. И он выглядел чуть менее кринжово, когда вышла из моды эстетика стиля phpbb ранних 2000-х.

Постепенно люди стали считать, что это обычное, ожидаемое поведение. Однако RFC 5321 и другие почтовые RFC оставляют кучу свободы реализации локальной части на сервере. В том числе это касается и точек. Оказалось, что разрешение опускать точки ни в коем случае не общепринятое!

«Есть не так много уникальных имён доменов электронной почты»

Есть ли у вас доступ к базе данных в продакшене с адресами электронной почты пользователей? Если да, то высока вероятность того, что ваш работодатель обязан заблокировать вам доступ к базе данных. Но пока он этого не сделал, попробуйте следующий запрос:

SELECT COUNT(DISTINCT SPLIT_PART(email, '@', 2)) FROM users;

Какой результат вы ожидаете получить своим очень подозрительным запросом, который оставит след в логах Postgres и заставит администратора базы данных позадавать вам кучу вопросов? Очевидно, не пару доменов. Наверняка там будут gmail, outlook, icloud, proton, yahoo и так далее. Может быть, несколько чудиков наподобие этого ben@gitpush–force.com.

Ну, значит, пару десятков поставщиков услуг электронной почты, максимум сотню? Нет. Если у вас большая база пользователей, то вы, вероятно, получите ТЫСЯЧИ уникальных имён хостов.

Если откровенно, многие из них — это просто ребрендинг крупных поставщиков наподобие Gmail или Outlook. Например, я учился в Балтиморском университете и преподавал в Общественном колледже округа Балтимор. Поэтому у меня есть несколько адресов .edu! Но даже эти два очень престижных и хорошо финансируемых учебных образования не имеют собственной почтовой инфраструктуры.

> dig mx ubalt.edu +noall +answer
ubalt.edu.              350     IN      MX      10 ubalt-edu.mail.protection.outlook.com.
> dig mx ccbc.edu +noall +answer
ccbc.edu.               3398    IN      MX      20 nospam.ccbc.edu.
ccbc.edu.               3398    IN      MX      0 ccbc-edu.mail.protection.outlook.com.

На самом деле, они оба пользуются услугами outlook.com.

Наверно, это будет просто любопытным фактом: аналогично тому, что мы узнали о точках в Gmail, локальная часть адреса может реализовываться по-разному в зависимости сервера или провайдера. Я был свидетелем того, как команды пытались встроить в свою логику допущения о провайдере. Но это неравный бой: количество комбинаций почтовых серверов, провайдеров, локалей, конфигураций и версий бесконечно. А ваше время — нет.

«Адрес почты не может заканчиваться точкой»

Конкретно это больше особенность DNS, нежели почты. В DNS . обозначает корневую зону. Если вы работали с DNS или маршрутизацией трафика, то можете уже знать об этом. А если даже не знаете, то, скорее всего, уже сталкивались с этим, не осознавая. В dig корневая зона «точка» по умолчанию включена в вывод! Посмотрите сами: это видно, когда я прошу у dig запись A для gitpush--force.com; он показывает в ответах gitpush--force.com. (с точкой в конце).

dig gitpush--force.com +noall +answe 
gitpush--force.com.     273     IN      A       104.21.60.65
gitpush--force.com.     273     IN      A       172.67.192.184

Обычно отправка письма с точкой в конце адреса работает без проблем. Однако стоит отметить, что ben@gitpush–force.com и ben@gitpush–force.com. будут одним и тем же почтовым ящиком. Также стоит отметить, что это ещё один пример нарушения RFC5322, которое просто работает; Section 3.2.3 запрещает точки в конце.

«Почтовые адреса (не)чувствительны к регистру»

Имя хоста адрес электронной почты никогда не чувствительно к регистру. Однако для локальной части адреса всё немного запутанней. Прежде, чем я приступлю к подробностям, приведу ответ Mattie B на StackOverflow, в котором применён принцип Постела:

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

Давайте вернёмся к техническим определениям: в RFC 5321 есть конкретное утверждение:

Локальная часть     = Dot-string / Quoted-string
               ; МОЖЕТ быть чувствительной к регистру

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

Все почтовые серверы, конфигурации и провайдеры, имеющие вес в реальном мире, обрабатывают локальную часть, как нечувствительную к регистру, и на то у них есть очень веские причины (прошу прощения у всех пользователей Exim, по какой-то причине установивших caseful_local_part): ben@gitpush–force.com и BEN@gitpush–force.com в реальном мире просто должны указывать на один и тот же ящик; нет никаких причин поступать как-то иначе.

Значит, если «все важные» ящики нечувствительны к регистру, то нам об этом не стоит волноваться? Ну, не совсем. Конкретно это допущение становится причиной одной из самых распространённых ошибок, которые мне встречались в обработке адресов электронной почты. Чтобы разобраться, давайте ненадолго перейдём к практическому примеру.

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

create table users
(
    id    serial primary key,
    email varchar(254) unique not null
);

Но хотя ben@gitpush–force.com и BEN@gitpush–force.com представляют одного пользователя, этот уникальный не мешает мне случайно создать два аккаунта!

-- Это не вызовет никаких ошибок уникальных индексов
insert into users (email) values ('ben@gitpush--force.com');
insert into users (email) values ('BEN@gitpush--force.com');

Как этому препятствовать? Можно просто сказать «принудительно поднимаем адрес в верхний регистр» или даже поднять регистр самого индекса,

create unique index on users(upper(email));

Такой подход крайне распространён. Настолько, что, на мой взгляд, примерно >50% систем, обрабатывающих адреса ящиков, используют ту или иную его версию. К сожалению, это ловушка. Откровенно говоря, он работает в 99% случаев, но не защищён от дурака, а учитывая насколько важна надёжная коммуникация и насколько тесно мы сегодня связываем адреса электронной почты с идентификацией, это может поставить нас в шаткое положение.

Позвольте мне познакомить вас с чудесным миром выравнивания регистра Unicode. Если не усложнять, то принудительное приведение адреса почты к общему регистру может быть в некоторых языках разрушительной и необратимой операцией. Если бы меня звали Ben Weiß и у меня был бы адрес benweiß@gitpush–force.com, то если бы вы попытались преобразовать его в верхний, возникли бы проблемы, потому что не входящий в ASCII символ ß выравнивается не просто в другую букву, а в ДВЕ буквы, два символа ASCII: SS.

Однако принудительный перевод в нижний регистр тоже не решает проблемы. При опускании в нижний регистр турецкая İ выравнивается в обычный символ ASCII i. И должен вам сказать, что в мире МНОГО людей с именем İbrahim.

Что ещё забавнее, различные реализации toLower()/toUpper() выполняют выравнивание по-разному, поэтому вы будете получать разные баги в зависимости от языка программирования, версии, базы данных, системной локали и так далее (всё это будет очень весело и совершенно несложно отлаживать!).

Как же поступить правильно? У меня для вас плохие новости: идеального решения нет. Впрочем, существует общее «достаточно хорошее» решение: использовать citext в Postgres или COLLATE utf8mb4_general_ci в MySQL.

Почему же оно неидеально? Вернёмся к турецкому: допустим, есть два человека по имени İbrahim и по какой-то безумной причине их провайдер разрешил им зарегистрировать İbrahim@turknetmail.com и ibrahim@turknetmail.com. Строго говоря, это разрешается соответствующими RFC, однако маловероятно в реальном мире. Но оба они не пройдут проверку на уникальность и в Postgres, и в MySQL, потому что İ создаёт коллизию с i. К счастью, маловероятно, что это когда-то произойдёт, но раздражает то, что технически это возможно.

Допущения о субадресации с плюсом

Как можно понять из статьи, я человек, любящий изучать мелкие особенности электронной почты, поэтому при регистрации в новом сервисе почти всегда использую субадреса с меткой плюса. Для логина на веб-сайте какой-нибудь компании я использую почту ben+thatcompany@{myemailhost}.email. Я убеждаю себя, что делаю это ради конфиденциальности, чтобы в случае взлома базы данных почты этой компании или продажи моего адреса маркетологам я бы понял, где произошла утечка.

Но истинная причина в том, что мне просто нравится раздражаться, когда это ломает UX из-за программных ошибок или рассогласованностей. Когда я несколько лет назад создавал аккаунт United Airlines, при вводе «ben+united@{myemailhost}.email» я увидел сбой regex «Введите валидный почтовый адрес». Следует сказать, что символ + абсолютно валиден и допустим в локальной части, а сам этот адрес валиден и работает. В нём нет ничего странного или нового: впервые я начал делать так в 2011 году с появлением Gmail!

Давайте найдём информацию в RFC5321 Section 4.1.2RFC5322 Section 3.4.1 и RFC5322 Section 3.2.3:

В RFC5321 определяется следующий синтаксис команд…

   Локальная часть = Dot-string / Quoted-string
                  ; МОЖЕТ быть чувствительной к регистру


   Dot-string     = Atom *("."  Atom)

   Atom           = 1*atext

По сути, это означает «локальная часть может быть Dot-string или Quoted-string». Мы уже рассмотрели некоторые особенности Quoted-string (закавыченных строк), поэтому давайте перейдём к Dot-string. Она определяется в виде Atom *("." Atom). Это означает, что она допускает любое количество «Atom», разделённых одинарными точками. А Atom определяется, как 1*atext, то есть Atom может быть одним или более «atext».

Но что такое «atext»? RFC 5321 унаследовал это определение от RFC5322, поэтому давайте перейдём к нему, где и найдём ещё одно похожее определение локальной части…

   Локальная часть      =   dot-atom / quoted-string / obs-local-part

А вот определение «atext», где в явном виде разрешается символ +!

   atext           =   ALPHA / DIGIT /    ; Печатные символы
                       "!" / "#" /        ;  US-ASCII, исключая
                       "$" / "%" /        ;  специальные. Используются для атомов.
                       "&" / "'" /
                       "*" / "+" /
                       "-" / "/" /
                       "=" / "?" /
                       "^" / "_" /
                       "`" / "{" /
                       "|" / "}" /
                       "~"

United стала первой в длинном пополняющемся списке компаний, официально запретивших мой валидный и работающий адрес электронной почты. Последней пока в него попала Motorola, когда позволила мне сделать заказ с адресом ben+motorola@{myemailhost}.email, но на странице состояния заказа произошёл сбой с сообщением «Недопустимый адрес электронной почты»… при нажатии на ссылку, которую Motorola успешно доставила на мой адрес! (Кстати, мне нравится мой новенький блестящий Razr Fold).

Такая субадресация у моего поставщика услуг почты, а также в Gmail доставляет ben+whatever@gitpush–force.com в те же входящие, что и ben@gitpush–force.com. Но люди, знающие о субадресации, почему-то на удивление часто предполагают, что она универсальна и что можно сделать вид, что ben+whateer и ben всегда будут одними и теми же входящими. Некоторые хитрые рекламщики и маркетологи даже пытаются объединять адрес, исходя из этого допущения. Но если говорить в целом, по большей мере это просто особенность реализации Gmail.

В заключение…

Я уже выдохся, поэтому подведу краткий итог: мы исследовали некоторые пограничные случаи и неожиданное поведение электронной почты, которые, если честно, не нужно запоминать всем. Хотя каждое из этих «заблуждений» может показаться очевидным, бессмысленным или тривиально устранимым, всё их множество в целом создаёт сложную паутину ловушек. Из-за интернациональных символов, чувствительности к регистру, точек, специфичных для серверов фич и ограничений, неожиданных символов, странностей DNS и других тонкостей становится сложно делать допущения обо всём, что связано с электронной почтой.

И это возвращает нас к главному: не нужно слишком уж заморачиваться. Для уникального индекса используйте citext в Postgres или COLLATE utf8mb4_general_ci в MySQL. Для верификации отправляйте письмо с кодом/ссылкой подтверждения.

Ничего более сложного и не требуется.