Пользователь
0,0
рейтинг
30 мая 2014 в 00:12

Разработка → Никогда не проверяйте e-mail адреса по стандартам RFC перевод

Множество сайтов требуют от пользователя ввода адреса электронной почты, и мы, как крутые и щепетильные разработчики, всегда стремимся проверять формат введенных адресов строго по стандартам RFC. Благодаря этому наши приложения и сайты проверяют формат e-mail корректно и не имеют проблем с юзабилити, а мы сладко спим, потому что уверены, что все работает как надо.
Ага, как бы не так!
Приведенные выше аргументы звучат круто и железобетонно, но проблема здесь заключается в том, что в адресе почты могут находиться совершенно бессмысленные вещи, и, на деле, проверка адресов по стандартам RFC может, наоборот, все жутко запутать.
Почему так? Существует множество способов сформировать адрес почты, который будет одновременно и корректным и бредовым. Отчасти это происходит из-за того, что некоторые почтовые службы в целях обратной совместимости позволяют представлять адреса в форматах, которые давно устарели. Например это электронная почта существовавшая до появления DNS и до появления современного формата user@domain.tld: тогда использовались UUCP ”bang path” — адреса, которые представляли собой список всех узлов по маршруту ответственных за доставку.

Внутренности адреса почты


Адрес e-mail выглядит так:
mailbox@hostname

Тут mailbox может быть локальным аккаунтом пользователя, аккаунтом роли или маршрутизатором автоматизированной системы такой, например, как список рассылки, а в качестве hostname может быть использован любой узел, если о нем известно DNS-серверу, к которому обращается почтовик при доставке.
Кроме того, некоторые системы позволяют добавлять теги к адресу. Обычно это происходит в формате:
mailbox+tag@hostname

где тег и разделитель (обычно это "+", но qmail использует "-" по-умолчанию, хотя может быть сконфигурирован и иначе) игнорируются при доставке. Обычно это используется для фильтрации почты по папкам и автоматизации, но может быть использовано и для разделения введенных адресов по получателям и выявления злоупотреблений персональными данными.
Итак, в адресе в формате «mailbox@hostname», «mailbox» является пользовательским аккаунтом, приложением или аккаунтом системной роли, но может содержать и такие экстравагантные вещи, как информацию для дальнейшей маршрутизации или идентификаторы используемые для сортировки, автоматизации или отслеживания, а «hostname» — обычно доменное имя, но может являться и субдоменом, сервером, сервисом, ip-адресом или просто именем хоста.

Корректные имена ящика с точки зрения RFC


Специцификация одобряет довольно странные адреса, и было бы накладно поддерживать их все потому, что некоторые слишком сложны, и не слишком много людей обладают достаточными знаниями чтобы выделывать такие пируэты в нейминге. Поддержка таких адресов затруднит поддержку таких аккаунтов вашими сотрудниками, к тому же они почти никогда не используются в быту.
Ящик может содержать пробелы. Насколько я помню, доинтернетовский AOL разрешал пробелы в «Imya Polzovatelya», которые использовались еще и как почтовые ящики с вырезанными оттуда пробелами: «imyapolzovatelya@aol.com», однако ж согласно RFC вы можете использовать двойные кавычки вокруг ящиков содержащих пробелы:
"Alan Turing"@example.com   <== Это корректно, но поддерживать не стоит

Кстати говоря, по этой логике, ящик содержащий всего лишь пробел корректен:
" "@example.com <== Это корректно, но смотри выше

А вот еще один корректный адрес, он создан из допустимых для адреса символов:
!#$%&'*+-/=?^_`{}|~@example.com   <== Корректный адрес вряд ли достойный поддержки

Кстати, проверяйте апострофы, апострофы должны поддерживаться:
Miles.O'Brian@example.com  <== Стоит поддерживать

Апострофы не должны закавычиваться или эскейпиться, но когда вы сохраняете такие адреса в базу или передаете еще куда-то, убедитесь, что всё чики-пуки.
В Википедии есть еще куча примеров.
Нужна ли полная совместимость с RFC? Вам выбирать, но я не советую — пробелы и нестандартные символы в адресе довольно необычная штука и чаще всего являются просто опечаткой. Крупные e-mail провайдеры не разрешают использовать это примерно по тем же причинам; таким образом обычно достаточно дозволять буквы, цифры, точки, подчеркивания, дефисы, апострофы и плюсы.

Регистрозависимые адреса


Согласно RFC уникальность адреса определяется его регистрозависимой уникальностью, однако 99,9% провайдеров считают иначе и не позволяют регистрировать VasyaPetrov@example.com, если vasyapetrov@example.com уже зарегистрирован. Считайте, что имя почтового ящика регистронезависимо:
ALLEN@example.com
Allen@example.com
allen@example.com

Небольшая кучка систем использует полную проверку регистра, позволяя лишь адрес Allen@example.com и отбрасывая входящую корреспондецию всех остальных АлЛеНоВ, однако это не работает на практике, поскольку пользователь не привык различать регистр в адресах почты.
Должны ли вы тут сохранять совместимость с RFC? Конвертируя адреса в нижний регистр перед сохранением вы можете доставить проблем небольшому количеству пользователей (вы не сможете посылать им письма), но отослав миллионы e-mail я столкнулся с этим всего несколько раз.
Конвертация в адреса в нижний регистр является неплохой идеей в плане нормализации данных, так как домен всегда регистронезависим и должен быть в нижнем регистре. Если же вы решите сохранять адрес так, как он введен, добавьте поле, в котором будет хранить каноническую версию.

Нестандартные символы


Gmail тут отличился: в то время как стандарт включает в себя точку как стандартный символ, Gmail не делает различий между адресами ящиков с точками и без. Эти адреса указывают на один и тот же почтовый ящик:
first.last@gmail.com
firstlast@gmail.com
f.i.r.s.t.l.a.s.t@gmail.com

Обратите внимание, что Google Apps позволяет использовать Gmail на любом домене.
Основная проблема здесь заключается в поиске адреса в базе в том виде, в котором он был изначально введен, что может доставить немало геморроя как пользователю, так и службе поддержки, а также и программистам с тестировщиками. Тут то вам и пригодится вторая, канонiческая форма адреса, но об этом позже.

Расширенная форма названия ящиков с использованием тегов.


Как было сказано выше, большинство систем доставки электронной почты (MTA), включая sendmail, Postfix, qmail, Yahoo Plus и Gmail поддерживают расширенное название ящика. Это позволяет пользователю, добавляя тег, сортировать письма. Это может позволить мне насоздавать кучу аккаунтов на одном сайте или в приложении:
allen+one@example.com
allen+two@example.com

Но нужно ли вычищать теги из адреса ящика?
НЕТ! Будьте дружелюбны к своим пользователям, и пользователи проникнутся верой, что вы не осуществите хищение и сбыт их персональных данных с целью наживы. Даже если вы пытаетесь запретить регистрацию дополнительных аккаунтов с существующим ящиком, представьте себе, насколько просто в наше время тупо зарегистрировать еще один ящик чтобы снова зарегистрироваться у вас — не сложнее создания алиаса или папки(но об алиасах, папках и тегах, наоборот, мало кто знает).
Итак, еще раз. Создание второй, канонической, формы сохранения адреса в базе может неплохо прикрыть вашу за вас в случае неприятностей. Убедитесь, что вы ликвидировали из нее все теги, точки и т. д. и можете сравнивать с ней свежевведенные адреса.

Юникод и интернационализированные имена ящиков


Имена ящиков не поддерживают расширенные символы ASCII (8-bit) и символы Юникода. Это ограничение уходит своими корнями в спецификацию SMTP, во времена появления которого всего этого попросту не существовало; однако 8-битные значения, определенные локально, например из кодировок семейства ISO-8859-x, все-таки могут использоваться, но вы все равно никогда не узнаете, что же это за кодировка. Фактически, я видел 8-битные ящики только у спамеров.
В конце концов, вы ведь храните ваши данные в UTF-8, так? Значит вы в любом случае не сможете перевести их обратно в ту локаль, которая была использована, если вы ее не знаете.

Доменные имена


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

Поддомены

Некоторые адреса содержат ненужные поддомены: например, «email.msn.com» и «msn.com» являются одним и тем же почтовым доменом, кроме того, такие истории часто случаются в корпоративной среде (и это еще один хороший кандидат для каноникализации).

Интернационализированные домены (IDN)

IDN были созданы для того чтобы использовать местные символы Юникода в названиях доменов, кроме того, возможно создать домен и со специальными символами:
postmaster@→→→→→→→.ws

этот классно описывает круговорот воды в природе.
Как и HTTP, SMTP поддерживает лишь 7-битную кодировку, и для того чтобы справиться с этим несчастьем IDN конвертируются в Punycode, что позволяет имени домена конвертироваться в представление Юникод и обратно:
postmaster@xn--55gaaaaaa281gfaqg86dja792anqa.ws

Очень жаль, но существует возможность фишинга при использовании IDN. Юникод содержит несколько разных экземпляров некоторых символов ASCII. Это позволяет злоумышленнику создать сайт, название которого выглядит точно также как и оригинал из-за того, что некоторые символы в названии совпадают внешне, но не внутренне.
Это порождает несколько вопросов на которые следует ответить:
Должны ли мы дозволять IDN-адреса? Можем ли мы обеспечить саппорт пользователей службой поддержки (откуда у саппорта, например, клавиатуры с китайскими иероглифами?) Должны ли мы сохранять их в Юникоде или Punycode? Если мы сохраняем каноничные адреса, то в какой кодировке это делать? Поддерживает ли вообще наш почтовик (MTA) IDN, и в какой форме он ждет адреса при отправке писем?

IP Address syntax

Использование IP-адресов допустимо:
allen@[127.0.0.1]
allen@[IPv6:0:0:1]

Однако такие адреса выглядят подозрительно, и вряд ли им стоит доверять.

Временные почтовые адреса


Существует множество сервисов, которые предоставляют пользователям временные почтовые адреса. Обычно это используется для анонимности или для того чтобы регистрироваться на недоверенных сайтах.
Даже такие сервисы как Hotmail и Yahoo предоставляют алиасы, которые могут быть использованы примерно тем же способом, то есть уничтожены через некоторое время. Не существует единой техники выявления таких адресов — в конце концов именно для этого они и предназначены. Они используют большущий набор доменных имен с постоянной ротацией для того чтобы быть на шаг впереди тех, кто пытается пресечь их деятельность.

Белый список используемых возможностей


Адреса электронной почты могут быть чудовищно сложными, но, навскидку, 99,99% (а может, и больше) придерживаются простых принципов, а остальные слишком утомительно поддерживать.
Итак, вам вероятно следует воздержаться от поддержки адреса, если он содержит:
  • Зависимость от регистра
  • Пробелы
  • Кавычки или Эскейп-символы
  • Специальные символы кроме '._+-
  • Айпишники в поле домена
  • IDN

Конечно, это может создать проблемы некоторым пользователям, но и в этом случае они скорее всего попытаются использовать какой-нибудь другой адрес, который подойдет. Кроме того, это позволит вашему саппорту более качественно оказывать поддержку вне зависимости от пользовательской локали.
Еще я считаю, что вы должны поддерживать теги.
Если это необходимо, вы можете создать еще одно поле в базе с каноническим адресом, даже если считаете, что всю эту RFC-шную совместимость стоит поддерживать. Адрес в таком поле может быть:
  • Приведен в нижний регистр
  • Быть без тегов
  • Транскодирован из Юникода в ASCII
  • Без дублирующихся поддоменов

Несмотря на то, что этот совет может показаться слишком радикальным, это все равно лучше, чем слепо подчиняться стандартам. Как знать, может когда-нибудь такая упрощенная нотация станет новым стандартом?
Перевод: Allen Fair
@DeltaKilo
карма
4,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (99)

  • –23
    Согласен, соблюдать стандарты — пустая трата времени.

    Вообще всегда были подозрительны люди в яшиками не в формате imyafamiliya334356@mail.ru
    • +8
      Вообще всегда были подозрительны люди в яшиками не в формате imyafamiliya334356@mail.ru

      У людей просто нет фантазии, а все нормальные ники заняты )
      • +7
        Люди такие, и фантазии нет, и без таблички «сарказм» к ним лучше не ходить…
        • 0
          Сарказм не всегда бывает к месту.
          • +2
            Расскажите поподробнее
            • 0
              Подробно не сумею, но вот кратко — посмотрите в мой профиль и сравните со своим. На абсолютные значения кармы можно даже внимания не обращать, тут важно сколько людей там отметилось вообще. Итого можно сделать вывод — около 50% людей не любят сарказм при любом раскладе.
              • 0
                Да ну, фигня. Посмотрите в мой профиль :)
                • 0
                  Великий Гуро! Простите много мнящего о себе новичка! Я не вдохнул случайно ваш воздух? Извините, я так больше не буду.
                  • +1
                    Не сотвори себе кумира, юный падаван, ибо великий грех гордыня есть, тебя на Темную сторону приведет она.
              • +1
                Я подозреваю, что карма раздаётся нормальным распределением и в сумме, и для каждого в отдельности.

                Гораздо интереснее не цифры в профиле (да и при 6 десятках выводы делать вообще рано), а то, что минусы в карму обычно схватываю там, где в каментах солидно наплюсили. Так что про 50% людей вывод очень куцый.
  • +9
    всегда казалось что уж +tag то — это как раз — RFC — tools.ietf.org/html/rfc3696#section-3
    и, нигде не сказано что + обязательно должен именно как алиас поддерживатся

    по поводу резки поддоменов — … а как в общем случае быть уверенным что что user@vladivostok.domain.ru и user@domain.ru это один и тот же почтовый ящик? а если есть и user@moscow.domain.ru? а если там user@moscow.russia.domain.com?
    • +1
      Именно так сделано у IBM, где ru.ibm.com совсем не равно us.ibm.com.
    • 0
      Для тех кто помнит: admin@mail.ru и admin@corp.mail.ru — не одно и то же, хотя это не всегда было так. =)
  • +1
    Сообщество Symfony с Вами не согласно =)
    github.com/symfony/Validator/blob/master/Constraints/EmailValidator.php#L66
    • +1
      Там же:

       if ($constraint->strict && class_exists('\Egulias\EmailValidator\EmailValidator')) {
                  $strictValidator = new StrictEmailValidator();
                  $valid = $strictValidator->isValid($value, false);
      


      github.com/egulias/EmailValidator
      • –1
        Это только если $constraint->strict == true, в противном случае вступает в бой та самая регулярка, что я отметил.
  • +25
    Если вы все-же хотите поддерживать RFC822, то для этого есть perl regex:
    Не открывать
    (?:(?:\r\n)?[ \t])*(?:(?:(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*))*@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*|(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)*\<(?:(?:\r\n)?[ \t])*(?:@(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*(?:,@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*)*:(?:(?:\r\n)?[ \t])*)?(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*))*@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*\>(?:(?:\r\n)?[ \t])*)|(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)*:(?:(?:\r\n)?[ \t])*(?:(?:(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*))*@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*|(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)*\<(?:(?:\r\n)?[ \t])*(?:@(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*(?:,@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*)*:(?:(?:\r\n)?[ \t])*)?(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*))*@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*\>(?:(?:\r\n)?[ \t])*)(?:,\s*(?:(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*))*@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*|(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)*\<(?:(?:\r\n)?[ \t])*(?:@(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*(?:,@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*)*:(?:(?:\r\n)?[ \t])*)?(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*))*@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*\>(?:(?:\r\n)?[ \t])*))*)?;\s*)
    
    • +2
      ссылка на него есть в статье, кстати.
    • +5
      Регулярку точно писал человек? Не могу прочитать, из-за чего возникла необходимость такой вложенности…
      • +3
        В ссылке в статье есть комментарий автора, что он автоматизировал процесс слияния разных регекспов. Итак, еще раз:
        http://www.ex-parrot.com/~pdw/Mail-RFC822-Address.html
    • +24
      email.match /@/
      

      Вместо тысячи слов
      • 0
        @"><img/src="//owned
        
        • +11
          и? это уже другая проблема, к проверке email'a не относящаяся
          • 0
            К теме статьи напрямую не относится, но очень часто разработчики грешат тем, что «оптимизируют» экранирование, если уверены, что исходные данные чисты. E-mail или числа — как раз примеры таких данных, только вот и те, и другие могут быть проблемой (про e-mail уже написал, а числа могут быть отрицательными, т.е. -... и это иногда может быть дырой).

            Поэтому совет проверки входной строки на простое наличие там @ я лично считаю опасным. В общем случае.
            • +5
              Всякие поклонники Смарти, Django templates и т. п., конечно, со мной не согласятся… но, по моему мнению, шаблонизатор, который по умолчанию не эскейпит вывод, это полный треш. Проблемы @"><img просто не должно существовать. Надо заниматься бизнес-логикой, юзабилити, а про это просто не думать. Для меня полнейшая загадка, почему этого ещё не поняли после стольких XSS.
              • 0
                С этим целиком согласен, но даже в новомодном Laravel для PHP стандартный шаблонизатор использует синтаксис подобный Mustache/Handlebars, но заменив {{ }} на некранирующий вывод, а {{{ }}} — на экранированный.

                Печально, да. Народ до сих пор в потёмках. Да что там далеко ходить — habrahabr.ru/post/222453/#comment_7576571
              • 0
                В Django экранирование по-умолчанию вообще-то =)
                • 0
                  Согласен, про Django templates я неправ.

                  Видимо, сказалась моя к ним ненависть за то, что как-то меня заставили условие разворачивать по ДНФ, т. к. Django не поддерживают скобок в условиях.
        • +3
          символы тэгов выкашиваются на более низком уровне для абсолютно всех форм и везде, где пользователь может хоть что-то ввести
        • +1
          Как сказал ahmpro, это уже другая проблема. Такое экранировать при выводе, а не при вводе надо.
      • 0
        exim -bt :)
    • 0
      Это старый стандарт между прочим.
  • +55
    Есть противоположное мнение. Которое, во-первых, не накладывает стописяттысяч странных и непредсказуемых ограничений на пользователей (тем самым, потенциально уменьшая конверсию, как подход в статье, явно придуманный программистами, заботящимися о себе, а не о пользователе), а во-вторых – занимает не целую статью, а порядка одного приложения. Да и в реализации, как ни странно, попроще будет.

    Не проверяйте синтаксис почтового ящика вообще, а просто пошлите туда письмо и убедитесь, что оно было получено.
    • НЛО прилетело и опубликовало эту надпись здесь
      • +2
        Кстати, по поводу проверки почты регексами была довольно давно уже статья на хабре
        • 0
          И не одна:
          habrahabr.ru/post/55820/
          habrahabr.ru/post/175329
          habrahabr.ru/post/175375/
          Но, эта — лучшая. Она единственная, которая объясняет что плохо и почему.
          Кроме того, важно, что она рассеивает ложное чувство уверенности, что адреса почты — это просто и предупреждает грабли, с которыми может столкнуться такой самоуверенный гражданин.
      • +1
        Тоже лет 10 назад не удавалось зарегистрироваться на половине сайтов из-за того, что мой е-мейл начинался с цифры. (1xxxx@abc.com)
  • 0
    Интернационализированные Домены IDN
    Насколько я помню, поддержки IDN в почте еще нет нигде. Вики говорит вот так:
    Всё ещё недоступно в полном объёме применение международных доменных имен в электронной почте. Технический стандарт, благодаря которому это станет возможно, разрабатывается инженерной группой Интернета (IETF)

    Gmail тут отличился: в то время как стандарт включает в себя точку как стандартный символ, Gmail не делает различий между адресами ящиков с точками и без.
    Это же давно известно. Прекрасный способ фильтровать спам — делаем разные фильтры на письма с точками и без. В результате если везде светим адрес в виде «bla.bla», то если придет на «blabla» — 99% что спам :)
    • +2
      > В результате если везде светим адрес в виде «bla.bla», то если придет на «blabla» — 99% что спам :)

      А теперь может быть кто-то умный, прочитавший эту статью. А вы уже об этом не узнаете.
    • 0
      я пользую +spam для этих целей, но не всегда пропускает +, тогда юзаю точку. Но фильтр спама на гуглопочты и без этого справляется
    • +2
      Точки поддерживаются только в Gmail но не поддерживаются в Google Apps.
      В apps только символ плюса. Так ivan@domain.com и ivan+spam@domain.com являются одним ящиком. На месте уже можно разруливать метки встроенными фильтрами.
      • 0
        В apps можно настроить catch all адрес и фильтры в нем на любой вкус.
  • –13
    А вот еще один корректный адрес, он создан из допустимых для адреса символов:


    rambler так не думает

    image
    • +8
      Много кто так не думает:
      Крупные e-mail провайдеры не разрешают использовать это примерно по тем же причинам; таким образом обычно достаточно дозволять буквы, цифры, точки, подчеркивания, дефисы, апострофы и плюсы.
    • +19
      Надо ввести новое правило для хабраэтикета: не прочитал пост — не комментируй.
      • +1
        image
  • 0
    Ну да, обычно на форумах вопрос «какое регулярное выражение использовать для почты» начинается с элегантного однострочного RE до огромного, в несколько абзацев монстра.
    • –1
      до огромного, в несколько абзацев монстра

      Как в Perl модуле для проверки адреса в соответствии с RFC-822, например.

      Собственно, regexp:
      (?:(?:\r\n)?[ \t])*(?:(?:(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t]
      )+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:
      \r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(
      ?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ 
      \t]))*"(?:(?:\r\n)?[ \t])*))*@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\0
      31]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\
      ](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+
      (?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:
      (?:\r\n)?[ \t])*))*|(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z
      |(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)
      ?[ \t])*)*\<(?:(?:\r\n)?[ \t])*(?:@(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\
      r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[
       \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)
      ?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t]
      )*))*(?:,@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[
       \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*
      )(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t]
      )+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*)
      *:(?:(?:\r\n)?[ \t])*)?(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+
      |\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r
      \n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:
      \r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t
      ]))*"(?:(?:\r\n)?[ \t])*))*@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031
      ]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](
      ?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?
      :(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?
      :\r\n)?[ \t])*))*\>(?:(?:\r\n)?[ \t])*)|(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?
      :(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?
      [ \t]))*"(?:(?:\r\n)?[ \t])*)*:(?:(?:\r\n)?[ \t])*(?:(?:(?:[^()<>@,;:\\".\[\] 
      \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|
      \\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>
      @,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"
      (?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*))*@(?:(?:\r\n)?[ \t]
      )*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\
      ".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?
      :[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[
      \]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*|(?:[^()<>@,;:\\".\[\] \000-
      \031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(
      ?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)*\<(?:(?:\r\n)?[ \t])*(?:@(?:[^()<>@,;
      :\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([
      ^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\"
      .\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\
      ]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*(?:,@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\
      [\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\
      r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] 
      \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]
      |\\.)*\](?:(?:\r\n)?[ \t])*))*)*:(?:(?:\r\n)?[ \t])*)?(?:[^()<>@,;:\\".\[\] \0
      00-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\
      .|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,
      ;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|"(?
      :[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*))*@(?:(?:\r\n)?[ \t])*
      (?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".
      \[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[
      ^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\]
      ]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*\>(?:(?:\r\n)?[ \t])*)(?:,\s*(
      ?:(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\
      ".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)(?:\.(?:(
      ?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[
      \["()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t
      ])*))*@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t
      ])+|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?
      :\.(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|
      \Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*|(?:
      [^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".\[\
      ]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)*\<(?:(?:\r\n)
      ?[ \t])*(?:@(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["
      ()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)
      ?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>
      @,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*(?:,@(?:(?:\r\n)?[
       \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,
      ;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t]
      )*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\
      ".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*)*:(?:(?:\r\n)?[ \t])*)?
      (?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()<>@,;:\\".
      \[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)(?:\.(?:(?:
      \r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\[
      "()<>@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])
      *))*@(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])
      +|\Z|(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\
      .(?:(?:\r\n)?[ \t])*(?:[^()<>@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z
      |(?=[\["()<>@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*\>(?:(
      ?:\r\n)?[ \t])*))*)?;\s*)
      
      • +3
        Не жмите, там скроллить три экрана. ;`\
  • +16
    Не нравятся конторы, которые запрещают «+» в почтовых адресах и пытаются блокировать одноразовые почтовые службы вроде mailinator.com

    1. Я всё равно не скажу им свою реальную почту: если бы хотел, сказал бы сразу.
    2. Я всё равно найду способ не давать им свой адрес.
    3. Если совсем ничего не получится, я уйду, не регистрируясь.

    Борьба с необычными почтовыми адресами — это в чистом виде борьба с пользователями. Нафига пользоваться сайтом, который с тобой борется?
    • +6
      Подобные конторы хотят на вас заработать и для этого им нужен хотя бы ваш адрес почты. То, что вы предоставите им фальшивый адрес, равносильно для них тому, что вы просто уйдёте. Поэтому подобными сайтами изначально не стоит пользоваться.
      • +2
        Шок! Сенсация! Сайты хотят зарабатывать на пользователях :)
        Не стоит ими пользоваться.
        • +1
          Ну окай, тогда так: «не стоит пользоваться теми сайтами, что пытаются навязывать свои услуги».
          Нормальные сайты не требуют регистрации даже при покупке на них чего-нибудь.
      • 0
        только сейчас вот пробовала зарегистрироватся с адресом вида vikarti+site@domain.com на сайте где для использования подразумевается платная подписка. но видимо им деньги не нужны совсем и заработать на мне у них не получилось по причине собственной же лени в создании нормальной проверки.
  • –2
    Я вот так делаю:
    $email = "example@mail.com";
    $apos = strrpos($email, '@');
    
    if ($apos > 0 AND $apos < strlen($email) - 1) {
        print('Email ' . htmlspecialchars($email)  . ' is valid!');
    }
    else {
        print('Email ' . htmlspecialchars($email)  . ' is invalid!');
    }
    
    • 0
      Чтобы лишний раз не отправлять запрос на сервер использую такую проверку:

      /(.*)@(.{4,})$/
      

      А чтобы не словить XSS на клиенте данные уже с сервера приходят заэнкоженнные.
      • +4
        Так через эту регулярку даже "@@@@@" пройдет…
        • 0
          var pair = email.split('@');
          var valid = (pair.length == 2 && pair[0].length > 0 && pair[1].length > 0);
          • 0
            var valid = /[^@]+@[^@]+/.test(email);
            • 0
              test подстроку проверяет, как я понял, а не всю строку. Я перед написанием того комментария абсолютно такой же код пробовал.
    • –1
      hodor@hodor@hodor
      • –2
        Ну и? Если человек хочет ввести левый меил, то он его введет. И не важно, сколько собачек, запятых или точек будет в сообщении, оно ему никогда не придет, а у вас запись всё же останется.
        • 0
          Зачем тогда вообще проверять наличие собаки?
          • –2
            Хм, задумался. Наверно чтобы отсеять дураков.
            • 0
              Чтобы избежать ошибки, когда человек не в то поле ввел, например
      • 0
        Email @ is invalid!

        Что-то в этом есть. Где бы зарегать?

        Особенно радует, что $email = ""; — тоже валидный.
        • +1
          С последним погорячился.
        • 0
          Портанул видимо криво, на js всё ок:
          /**
          var email = "example@mail.com";
          var apos = email.lastIndexOf('@');
          
          if (apos > 0 && apos < email.length - 1) {
              console.log('Email ' + email  + ' is valid!');
          }
          else {
              console.log('Email ' + email  + ' is invalid!');
          }
          */
          
          ['example@mail.com', '@gmail.com', '@', 'example@', ''].forEach(function(email) {
          	var apos = email.lastIndexOf('@');
          	
          	console.log('Email ' + email  + ' is '+((apos > 0 && apos < email.length - 1) ? '':'in')+'valid!');
          });
          


          log:
          "Email example@mail.com is valid!"
          "Email @gmail.com is invalid!"
          "Email @ is invalid!"
          "Email example@ is invalid!"
          "Email  is invalid!"
          
          • +2
            «Email @ is invalid!» — вот это валидный email по этой проверке.
            • 0
              Повторюсь, порт на php видимо корявый. А если лень в консоль заглянуть, то пример: js.do/code/38843
              • +3
                Минусовать собеседника — моветон.
                js.do/dovg/38846
                Email Email example@ is invalid! is valid!
                Email Email example@ is invalid! is valid! — тоже кстати валидный email по вашей проверке.
                • –7
                  Вы статью читали? У вас емеил просто содержит пробелы, так что по этой проверке он валиден. Минус считаю заслуженным.
  • +2
    Из-за таких мнений, что, регистрозависимость можно игнорировать и принудительно приводить имя ящика к нижнему регистру, в большом количестве случаев невозможно пользоваться ящиком на bitmessage-email гейте :-(
    • +2
      Угу. Ещё умиляет мнение людей, которые читают RFC, а видят там фигу. Вот объясните мне, идиоту, откуда взялось мнение о регистронезавсимости адресов? RFC как бы говорит чётко и недвусмысленно: The local-part of a mailbox MUST BE treated as case sensitive. Therefore, SMTP implementations MUST take care to preserve the case of mailbox local-parts. In particular, for some hosts, the user "smith" is different from the user "Smith".
      • +1
        Стандарт конечно допускает, но если локальная политика запрещает создание таких ящиков, то где здесь проблема? Это хорошая тактика против фишеров. Потому что сходу отлитичить smith@example.com и smIth@example.com (upper «i») в подписанном сообщении (т.е где визуально показано, что адрес легитимный) не представляется возможным.

        Вообще статья про то, что стандарты должны адаптироваться со временем.
        • 0
          Проблема в том, что отправитель не может и не должен быть осведомлён о том, какая на сервере получателя локальная политика, посему регистр он обязан соблюдать.
    • 0
      Можно сохранять адрес e-mail «as is», но при проверке есть ли уже пользователь с данным e-mail, выполнять регистронезависимый поиск (т.к. в 99% случаев регистр действительно не имеет значения).
      Ну и с точки зрения пользователя лучше использовать общепринятые стили адресов, а не искать самый экзотический вариант, валидный по RFC.
  • 0
    !#$%&'*+-/=?^_`{}|~example.com <== Корректный адрес вряд ли достойный поддержки


    Тот, кто в своё время активно пользовался UUCP протоколом, смотрит на такие адреса с бооольшим недоверием.
  • +3
    Добавлю, что доменное имя не обязательно должно содержать точку, что порой удобно при тестировании: user@localhost
    • 0
      а кстати, с вводом новых tld, вполне возможно отсутствие точки и в глобальном виде.
      info@ibm
  • 0
    $emails = ['admin@host.com', 'admin.2@test.gov.com.net', 'путин@президент.рф', 'admin admin@host.com', '@host.com', 'test+next@123.123.123.123', '123312@', '@', 'admin@localhost', '!$%^*@host.com'];
    
    foreach ($emails as $email) {
         $valid = filter_var($email, FILTER_VALIDATE_EMAIL);
         if ($valid) {
              echo "OK: '" . $email . "' is valid <br/>";
         } else {
              echo "ERROR: '" . $email . "' is invalid <br/>";
         }
    }
    /*
    'admin@host.com' - OK
    'admin.2@test.gov.com.net' - OK
    'путин@президент.рф' - ERROR
    'admin admin@host.com' - ERROR
    '@host.com' - ERROR
    'test+next@123.123.123.123' - ERROR
    '123312@' - ERROR
    '@' - ERROR
    'admin@localhost' - ERROR
    '!$%^*@host.com' - OK 
    */
    

    Пользуюсь таким вариантов. В целом, сплю спокойно, хотя два последних варианта настораживают.
  • +1
    А как юзать теги в адресе гугловской почты. Отправил письмо на ящик с тегом, оно пришло как обычное, никаких тегов в списке писем не появилось.
    • 0
      Фильтры настраивать надо. Если бы теги добавлялись автоматом — представляете, как можно ящик загадить? :)
  • +1
    Товарищ автор!
    Вот, на самом деле, такое же люди как вы, которые считают, что совсем не обязательно поддерживать RFC и достаточно только [a-z0-9.-] меня всё чаще и чаще огорчают тем, что либо нельзя зарегистрироваться на их сайте (плюс у них, видите ли, недопустимый символ для e-mail), либо e-mail просто тупо ВООБЩЕ не приходит, пока там есть экстеншн (да и сам плюс, скорее, всего, тоже).

    В общем, чтобы не было разночтений «почему эти куски RFC можно не поддерживать, а эти — нельзя» я таки предлагаю поддерживать RFC полностью.

    Как-то так…
  • +4
    Вы меня простите, уважаемый переводчик, но автор статьи является вредителем.

    Несмотря на то, что этот совет может показаться слишком радикальным, это все равно лучше, чем слепо подчиняться стандартам.


    Замечательный, глубокий совет. Зачем слепо подчинятся, когда можно подойти творчески!

    «Alan Turing»example.com <== Это корректно, но поддерживать не стоит


    А апостроф стоит. Чем объяснсняется такая избирательность?

    Кроме того, некоторые системы позволяют добавлять теги к адресу. Обычно это происходит в формате:
    mailbox+tag@hostname

    Было бы недурно перед тем, как воевать со стандартами, их почитать.

    Никаких «тэгов» упомянутые 822/2822 не предусматривают. Знак плюс является ВАЛИДНЫМ символов в любом месте адресной части. Существуют другие RFC, которые описывают необязательный механизм адрсации одного ящика многими адесами, который сервера МОГУТ выбрать поддерживать. Опять же, нигде не сказано, что это непременно знак плюс.

    Считайте, что имя почтового ящика регистронезависимо:
    ALLEN@example.com
    Allen@example.com
    allen@example.com

    Удачи при посылки почты, например, мне. Если регистр будет нарушен, мой сервер отвергнет письмо из-за неверного адресата, и мне пофиг, как это делают другие. Регистр ОБЯЗАН учитываться.

    Gmail не делает различий между адресами ящиков с точками и без. Эти адреса указывают на один и тот же почтовый ящик:
    first.last@gmail.com
    firstlast@gmail.com
    f.i.r.s.t.l.a.s.t@gmail.com

    А не Gmail — делает различия. Кроме того, домены под Гугл Аппсом тоже не делают различий. Как будем выходить из ситуации?
    Некоторые адреса содержат ненужные поддомены: например, «email.msn.com» и «msn.com» являются одним и тем же почтовым доменом

    А некоторые не являются. Мой реальный адрес выглядит как name@subdomain.domain.lv, будете выкидывать первую часть или как?

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

    Ставлю три бочки рома против дохлого попугая, что неприятности на вашу у вас будут как раз в том случае, если вы будете подвергать «каноникализации» то, что ввёл пользователь, и согласно своему разумению уродовать адреса, сравнивать, и принимать решения об их идентичности либо различности.

    UUCP bang path, может, и впрямь экзотика, в первую очередь потому, что с высокой вероятностью сервер вашего провайдера не настроен на их роутинг, но трогать всё остальное, что не содержит знаков "!", это просто накликивать себе проблемы и лучи ненависти благодарных пользователей. DON'T DO IT.
    • +5
      Считайте, что имя почтового ящика регистронезависимо:
      ALLEN@example.com
      Allen@example.com
      allen@example.com


      Удачи при посылки почты, например, мне. Если регистр будет нарушен, мой сервер отвергнет письмо из-за неверного адресата, и мне пофиг, как это делают другие. Регистр ОБЯЗАН учитываться.


      По вашему мнению, публичные сервисы предоставляющие почту должны регистрировать Allen@example.com и allen@example.com как два разных аккаунта? Почему в таком случае в написании доменных имен регистр не учитывается, а в имени пользователя учитывается? Эта история яркий пример habrahabr.ru/post/224623/#comment_7642915 тому, что стандарт устарел и должен быть изменен.
      • –2
        Мне все равно, что должны и не должны публичные сервисы почты. Я лишь утверждаю, что публичный сервис, который воспринимает почтовые адреса, ДОЛЖНЫ учитывать регистр именной части.

        Почему в таком случае в написании доменных имен регистр не учитывается, а в имени пользователя учитывается?


        Потому что именная часть контролируется RFC-2822, а доменная RFC-1034.

        Когда стандарт изменится хотя бы в виде RFC proposal, тогда и обсудим. Пока же существуют как «Allen@», так и «allen@» в качестве различных адресов на одном сервере, одним чихом это не поменять.
        • +3
          С таким отношением Вы рискуете, что в какой-то момент сможете писать письма только самому себе.

          Где бы сейчас был веб если бы все ждали пока почешется W3C?
          • 0
            Такое отношение я исповедую последние 15 лет, и как-то пока плюсов заметно больше. Искажают адрес спамеры.
      • +2
        «Разные адреса» не равносильно «разные пользователи». Это дело конкретного почтового сервера конкретной организации, привязывать ли разные адреса, отличающиеся регистром к одному и тому же пользователю или нет. А приложение или веб-сервис не должно считать, что оно лучше пользователя знает, какой у него email и как-то его искажать.
  • +5
    Вот, кстати, про регистрозависимость могу небольшую сказку рассказать.

    Жил да был старик Ростом. Хорошо старик жил, в свои 87 успешно пользовался компьютером, ходил по садам зелёным (он каждые выходные приезжал в свой загородный дом на старой советской Волге), писал музыку и ходил собирать грибы. И тут Ростом решил сайт свой сделать. Ну, — думает старик, — дело-то простое, благо, Django ещё года 4 назад выучил. Сверстал всё Ростом, провёл UX-испытания, потом нарисовал прототип ещё более удачного интерфейса, снова за вёрстку принялся, потом сел за программирование бэкэнда, потом JS стал писать. В общем, так месяца два прошло, и в итоге сайт был готов. Два дня он с друзьями событие это светлое отмечал! Компания подобралась хорошая, громкая. Вино пили, песни пели, на гитаре играли. А на сайте тем временем уже где-то полтысячи пользователей зарегистрировалось. И тут стал у Ростома в кармане айфон пищать — сообщения на email приходят. Смотрит Ростом, а это пользователи пишут. Говорят, что не могут на сайт войти.

    Стал Ростом думать и гадать, сайт тестировать, и никак не поймёт, в чем дело. С виду — всё работает! Уж было хотел на Тостер иль Stackoverflow вопрос писать, и тут понимает: это так бэкэнд авторизации работает! Пользователь регистрировался с адресом Superkitty@yandex.ru, а вводит при логине superkitty@yandex.ru. Одна лишь буква в другом регистре, но этого достаточно, чтобы сайт считал, что это почтовый адрес другого пользователя.

    И написал тогда Ростом свой бэкэнд авторизации.
    from django.contrib.auth.backends import ModelBackend
    from django.contrib.admin.models import User
    
    class EmailAuthBackend(ModelBackend):
        def authenticate(self, email=None, password=None, **kwargs):
            if not email:
                return None
    
            try:
                user = User.objects.get(email__iexact=email)  
            except User.DoesNotExist:
                return None
            except User.MultipleObjectsReturned:
                user = User.objects.filter(email__iexact=email)[0]
    
            if user.check_password(password):
                return user
    

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

    Вот и сказочке конец, а кто слушал — молодец.
    • 0
      Непонятно, почему это для логина на сайте должны действовать такие же стандарты, как для адресов почты.
      Ничего нет страшного в том, чтобы адрес сохранять и использовать только в том же регистре, как ввёл его пользователь, а логиниться разрешать с любым регистром.
      • 0
        Ну, гипотетически, сайт таким образом ущемляет других пользователей с тем же адресом, в котором какие-то символы набраны в другом регистре (потому что среди пользователей, адрес которых отличается только регистром символов, сможет зарегистрироваться только первый). Но, как говорится, special cases aren’t special enough to break the rules: в данном случае можно вполне обоснованно принять позицию, в соответствии с которой такие пользователи ССЗБ, и должны использовать другую почту, если сайт им всё-таки нужен.
  • 0
    Итак, вам вероятно следует воздержаться от поддержки адреса, если он содержит:
    ***
    Специальные символы кроме '._+-
    ***

    Вот тут не соглашусь! У одного меня полно знакомых с ящиками, где есть спецсимволы. Особенно часто: _ и -
    • +1
      Ключевое слово кроме.
      • 0
        А, извиняюсь, протупил…
  • +16
    Страница ада для моего ника. Столько нотификаций я не получал никогда.
  • +5
    Шел 2014 год, а программисты все спорили как проверять валидность email.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.