判斷收信人是否為正確的信箱

分類: 技術分享 作者: daniel

23 十二月 2008

不論是在會員註冊確認或是寄發最新優惠資訊,Email 已經成為現代人不可或缺的溝通工具,網路上 Email 就好像地址一樣的重要;就因為它如此的重要,在會員註冊時為了避免以後在寄發郵件可能發生的錯誤,我們常常會在 Email 輸入時進行判斷。以往大部分是使用正規表示式就使用者輸入格式上進行判斷,優點是判斷快速準確;但是對於格式正確卻並不是真實存在的 Email 卻無法有效過濾。透過 SMTP 協定,對 mail server 發出連接發送需求,就可以達到驗證 Email 是否真實存在的目的。

當然這個需求在茫茫網海中,已經有前輩為我們實現;當初找到二個 class:E-mail address validation classPHP SMTP Email Validation。基本上這二個類別都是藉由 SMTP 協定實現驗證的四個步驟:

  1. 發送 HELO 問候。
  2. 發送 MAIL FROM 命令,如果返回 250 表示可以正確連接服務器。
  3. 發送 RCPT TO 命令,如果返回 250 表示該 Email 存在。
  4. 發送 QUIT 命令,退出連接。

E-mail address validation class 年代比較久遠,對於單一主機的 mail server 可以順利解析;但是如果是在多個伺服器的狀況下,就會沒有辦法完整判斷。以下是大概的使用方法:

require_once('email_validation.php');

$validator = new email_validation_class;
// 延遲時間限定
$validator->timeout = 10;
$validator->data_timeout = 0;
// 測試發信的帳號與位址(這邊可以隨便設)
$validator->localuser = "admin";
$validator->localhost = "localhost";
// 是否開啟 Debug Mode
$validator->debug = 1;
$validator->html_debug = 1;
$validator->exclude_address = '';

if (isset($email) && strcmp($email, "")) {
// ValidateEmailBox 帶入的第二個參數,必須輸入 DNS;範例以 Hinet 的 DNS 為例
if (($result = $validator->ValidateEmailBox($email, array('168.95.192.1', '168.95.1.1'))) < 0) {
echo "<H2><CENTER>It was not possible to determine if <TT>$email</TT> is a valid deliverable e-mail box address.</CENTER></H2>\n";
} else {
echo "<H2><CENTER><TT>$email</TT> is ".($result ? "" : "not ")."a valid deliverable e-mail box address.</CENTER></H2>\n";
}
} else {
$port = (strcmp($port=getenv("SERVER_PORT"), '') ? intval($port) : 80);
$site = 'http://' . (strcmp($site = getenv("SERVER_NAME"), '') ? $site : 'localhost') . ($port == 80 ? '' : ':' . $port) . GetEnv("REQUEST_URI");
echo "<H2>Access this page using a URL like: $site?email=<A HREF=\"$site?email=mlemos@acm.org\"><TT>your@test.email.here</TT></A></H2>\n";
}

PHP SMTP Email Validation 是比較新的版本;另外在 php.net 搜尋 getmxrr 參數時,意外發現外國的另一個 function:PHP Email address validation with Verify probe。他們在 DNS 的解析上有使用 PEAR 的 Net_DNS,所以對於多個 mail server 的情況也能順利解析,是目前比較適用的解決方案。以下是大概的使用方式:

PHP SMTP Email Validation/email.php

$email = $_POST['email'];

require_once('smtp_validateEmail.class.php');

// 測試發信的位址
$sender = 'user@mydomain.com';
// 啟用類別
$SMTP_Validator = new SMTP_validateEmail();
// 是否開啟 Debug Mode
$SMTP_Validator->debug = true;
// 進行驗證
$results = $SMTP_Validator->validate(array($email), $sender);
// 依據回傳的結果顯示
echo $email.' is '.($results[$email] ? 'valid' : 'invalid')."\n";

PHP SMTP Email Validation/smtp_validateEmail.class.php

// 在此處必須輸入 DNS;範例以 Hinet 的 DNS 為例
/**
* Nameservers to use when make DNS query for MX entries
* @var Array $nameservers
*/
var $nameservers = array(
'168.95.192.1',
'168.95.1.1'
);

PHP Email address validation with Verify probe/email.php

$email = $_POST['email'];

require_once('email_verify_source.php');

// validateEmail 帶入的參數依序為:需要驗證的 Email、是否檢查 DNS、是否檢查 SMTP、測試發信的位址、測試發信的 hostname、是否開啟 Debug Mode
$error = validateEmail($email, true, true, 'postmaster@tienhuis.nl', 'outkast.tienhuis.nl', true);
if ($error) {
echo "<span class='bad'>$error</span>";
} else {
echo "<span class='good'>This address appears to be correct!</span>";
}

PHP Email address validation with Verify probe/email_verify_source.php

// 在此處必須輸入 DNS;範例以 Hinet 的 DNS 為例
# Workaround for bug in Net_DNS, you have to explicitly tell the name servers
#
# ***********  CHANGE THIS TO YOUR OWN NAME SERVERS **************
$resolver->nameservers = array ('168.95.192.1', '168.95.1.1');

經由這些前輩們的幫忙,讓我們不用再重新造輪子囉!非常感謝他們~另外在蒐集資料的過程中。我也發現幾個線上驗證 Email 真實性的網站:Verify Email Address OnlineVerify Email addressPHP Email address validation with Verify probe(作者不但提供程式碼,還提供線上測試,真是太佛心啦!)

後來經過交叉測試發現,並不是每個 mail server 都提供使用者驗證;比如說「grgergrfefwdwqdqwdwqfef@yahoo.com.tw」經測試後竟然是合法的 Email(不會有人真的信箱是這個吧?);但是同樣的 username 在 gmail 就有被測出不存在。而 Yahoo 的信箱還真是惡名昭彰啊,稍微在網路上搜尋一下,甚至連「寧可錯殺一百,也不能放過一個」這種形容都出來了;驗證的過程 SMTP 大概只有 1/5 成功的機率吧,因為實在太會檔了。

就目的來說,這樣的方式就可以達到;但是要 100% 的完全驗證,似乎是不太可能的;而且因為會透過 SMTP 發送訊息,多少會影響效能。在什麼時候利用什麼方式最好,就必須由需求面來判斷囉~

2 Responses to 判斷收信人是否為正確的信箱

Avatar

Mark.long

三月 19th, 2009 at 10:10 上午

從phpclasses.org鏈接過來的,寫得挺不錯的。

Avatar

捡了西瓜,丢了芝麻。 | 马克龙的博客

十月 9th, 2009 at 2:10 下午

[...] 网络上也有关于第④步骤的实现(link),但其可行性和准确性有待证实。至于第③步则可以使用checkdnsrr()函数(但Windows平台下的PHP没有实现该函数的)。 [...]

我要留言

關於這裡

這個部落格分享了哇寶在電子商務領域的技術及資訊,希望能讓更多人一起為台灣的網路產業加油。