This post comes from the first version of this blog.
Please send me an email if anything needs an update. Thanks!

Sprawdzanie poprawności adresu email to częsty problem, z jakim zmagają się programiści stron internetowych [i nie tylko]. Jest to dosyć zaawansowane i nietrywialne zagadnienie, jeśli chcemy przeprowadzić całościową walidację tego typu danych. Większość artykułów, jakie w tym temacie możemy znaleźć w Internecie skupia się na sprawdzeniu, a przynajmniej próbie sprawdzenia poprawności samej struktury adresu. W dzisiejszym wpisie chciałbym zaprezentować nieco inny, aczkolwiek związany z tym problem - sprawdzanie istnienia adresu email.

Fotografia: DaveBleasdale, CC-BY.

Wstęp: Sprawdzanie poprawności danych.

Niektórzy z Was na pewno zastanawiają się, dlaczego napisałem, że sprawdzanie poprawności adresu email jest zagadnieniem nietrywialnym. Otóż: struktura tego adresu jest definiowana przez RFC 5322 - już pobieżna lektura tego dokumentu skłania nas do stwierdzenia, że raczej nie będzie to proste zadanie. W Internecie krąży od dawna pewne "meme" o nazwie: "The Ultimate Email Validation Regexp" - polecam wpisać tą nazwę w Google i rozkoszować się wynikami. ;]

Na potrzeby niniejszego artykułu załóżmy jednak, że wprowadzone przez użytkownika dane są… poprawne. Wiem, bardzo naiwne założenie, ale jednak przyjmijmy je jako bazę dla dalszej części wpisu. Problem polega na tym, że sprawdzenie samej struktury adresu email to nie wszystko. Pozostaje jeszcze kwestia jego istnienia. Jako twórcy stron bardzo często przyjmujemy także naiwne założenie, że jeśli zostały wprowadzone poprawne [w sensie zastosowanych mechanizmów walidacji] dane, to jesteśmy już bezpieczni. Niestety, nie jesteśmy.

Zazwyczaj jeśli sprawdzamy dane pochodzące od użytkownika nasza uwaga kieruje się w stronę walidacji, której wynik możemy uzyskać na podstawie [w miarę] deterministycznych algorytmów. Co jednak, jeśli podane informacje są sfabrykowane? Zauważmy, że nawet, jeśli użytkownik wprowadzi np. poprawny numer PESEL, to wcale nie gwarantuje nam istnienia takiej osoby. Jeśli użytkownik wprowadzi poprawny adres email, to wcale nie znaczy, że rzeczony adres istnieje.

PHP: Sprawdzanie istnienia adresu email.

Wystarczy tej negatywnej propagandy, przejdźmy do celu. ;] O ile w przypadku numeru PESEL bez dostępu do bazy osób nie dowiemy się o tym, czy dana osoba istnieje, o tyle w przypadku adresu email mamy trochę większe pole do popisu. Zauważmy, że pełną poprawność osiągniemy w momencie, kiedy będą spełnione trzy kryteria: Zgodnie z założeniami z początku niniejszego wpisu, pierwszy punkt możemy uznać za spełniony. Jak już wspomniałem, wielu programistów ogranicza się do niego, nie zważając na kolejne. Jak więc postąpić dalej? Aby sprawdzić, czy domena istnieje potrzebujemy narzędzia, które pozwoli nam stwierdzić, czy dany serwer pocztowy istnieje. Z pomocą przychodzi funkcja checkdnsrr() i jej "koleżanka po fachu": getmxrr(). Ja wykorzystuję tą pierwszą, dlatego na niej oprę swój przykład. Wykorzystanie jest bardzo proste:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
$email = 'janek.kowalski@badserver.com';
$parts = explode('@', $email);
if(checkdnsrr($parts[1], 'MX'))
	{
	echo 'server exists!';
	}
else
	{
	echo 'server does not exist :(';
	}

Sprawdzamy, czy dla podanej domeny istnieje adres MX, co pozwala nam stwierdzić, czy pod podanym adresem “żyje” serwer pocztowy. Jeśli nie dostaniemy poprawnej odpowiedzi - mamy pewność, że adres, mimo poprawności strukturalnej nie istnieje. Warto w tym miejscu wspomnieć o walidacji dla tej części adresu email - jak mówią komentarze pod wpisem o funkcji checkdnsrr() w manualu PHP:

These Windows checkdnsrr() replacement functions in the comments are handy, but none of them appear to be escaping the input data before they call the nslookup.exe program via the shell. Thus, if the hostname being passed to the replacement functions is something unexpected like, say, "microsoft.com; cd \; deltree *.*", the exec() function will happily execute all of those malicious user specified commands.

The solution would be to properly validate input data, only allowing characters that could be found in a hostname according to the proper RFCs.

Co oznacza, że testując / uruchamiając nasz kod pod systemem Windows możemy trafić na “złego człowieka”, który dostaje dodatkową furtkę do włamania.

Najbardziej istotnym jednak punktem z naszej strony jest ten ostatni - sprawdzenie, czy dany adres email faktycznie istnieje. W tym miejscu przychodzi nam jednak odbić się od betonowej ściany z wielkim napisem: “nie ma takiej możliwości”. Zazwyczaj w programowaniu stosujemy wiele sztuczek, obejść, trików, żeby osiągnąć założony cel. W tym miejscu jednak takiej możliwości nie znajdziemy. Dlaczego? Dlatego, że takie informacje są w posiadaniu właśnie serwera pocztowego, który może, ale nie musi się z nami nimi podzielić. W zdecydowanej większości przypadków na pewno nie będzie chciał.

Oczywiście nie znaczy to, że zostaliśmy kompletnie bez żadnego rozwiązania - co to, to nie. Jeśli sprawdzany adres email będzie służył np. celom rejestracji konta użytkownika, możemy wysłać mu maila z kodem aktywacyjnym. Jeśli go nie wpisze na stronie lub nie kliknie w zawarty w mailu link - po pewnym czasie kasujemy takiego użytkownika z bazy i po kłopocie. Możemy też np. wymagać od użytkownika wysłania emaila z podanego konta na nasz adres - możliwości jest wiele, jednak żadna z nich nie będzie w pełni satysfakcjonująca dla użytkownika, który chce po prostu podać maila i przejść dalej.

Jak zawsze, ilu programistów, tyle pomysłów i rozwiązań. Może jednak macie inne zdanie na ten temat? ;]