最直覺當然是直接先把問題丟給ChatGPT,我也按照他的建議逐一排查,雖然沒有直接解決我的問題,但也讓我更認識我們家的Server環境。由於不只一個活動網站架在這個Server上,甚至不同品牌合作的廠商所使用的PHP版本也不一致。為了避免讓同事等待太久又不一定能解決(幫自己留後路XD),所以我首先請同事直接請原廠商為專案升級PHP版本報價。
在他去請廠商報價的同時,我用了以下方法排查:
一、Telnet (正常)
telnet smtp.office365.com 587
顯示正常:
220 SI1PR02CA0000.outlook.office365.com Microsoft ESMTP MAIL Service ready at Mon, 29 May 2023 05:09:46 +0000
二、mail (正常)
echo "This is the body of the email" | mail -s "Test" recipient@example.com
收信正常:
再來我們撰寫sample code直接用廠商的套件來發信。
<?php
// 加入 PHPMailer 函式庫
require_once('path/to/PHPMailer/PHPMailerAutoload.php');
$mail = new PHPMailer();
$mail->isSMTP();
$mail->Host = 'smtp.office365.com';
$mail->Port = 587;
$mail->SMTPAuth = true;
$mail->SMTPSecure = 'tls';
$mail->Username = 'campaign@example.com';
$mail->Password = 'password';
$mail->setFrom('campaign@example.com', 'Campaign Name');
$mail->addAddress('recipient@example.com', 'Recipient Name');
$mail->Subject = 'Testing SMTP connection';
$mail->Body = 'This is a test email.';
// 啟用 SMTP 除錯模式
$mail->SMTPDebug = 2;
// 回傳結果
if ($mail->send()) {
echo '郵件已成功發送!';
} else {
echo '郵件發送失敗:' . $mail->ErrorInfo;
}
?>
用php指令直接執行,收信正常:
2023-05-29 08:30:37 SERVER -> CLIENT: 421 4.7.66
TLS 1.0 and 1.1 are not supported. Please upgrade/update your client to support TLS 1.2. Visit https://aka.ms/smtp_auth_tls. [SI2PR02CA0000.apcprd02.prod.outlook.com 2023-05-29T08:30:37.338Z 00DB0FA0F0A000A0]
...
2023-05-29 08:30:37 SMTP ERROR: QUIT command failed: SMTP connect() failed.
經過一番在Stack Overflow探險,我依樣將環境切換成PHP 7.3便成功了(Server上的其他專案是使用PHP 7.3開發)
四、驗證
發現SMTP connect() failed.與PHP版本的關聯,為了驗證我寫了一支
echo 'PHP version:' . phpversion();
分別在Server執行和用HTTP瀏覽:在Server執行(
sudo php info.php
):PHP version:7.3.13-1+ubuntu18.04.1+deb.sury.org+1
用HTTP瀏覽:PHP version:5.6.40-21+ubuntu18.04.1+deb.sury.org+1
果然,在Web上依然使用source code中include的PHP版本。
除了花錢讓開發商改版,我果斷把問題轉到另外一個方向:讓我的PHPMailer只能用TLS1.2與smtp.office365.com連線。
五、結論
最後的解法實在非常直接明瞭。在PHPMailer有一個class.smtp.php,裡面的
public function startTLS()
,呼叫內建的stream_socket_enable_crypto
。只要將參數crypto_method
從原來的STREAM_CRYPTO_METHOD_TLS_CLIENT
改為STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT
,這題就這麼解了^^/**
* Initiate a TLS (encrypted) session.
* @access public
* @return boolean
*/
public function startTLS()
{
if (!$this->sendCommand('STARTTLS', 'STARTTLS', 220)) {
return false;
}
// Begin encrypted connection
if (!stream_socket_enable_crypto(
$this->smtp_conn,
true,
STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT
)) {
return false;
}
return true;
}
後記
讓原本還想靠Laravel接案的我澆了一盆冷水XD 但解決完之後也是蠻有成就感,最近有看到John Liu跟HISKIO合作的Laravel課程,是不是該報名一下了(?
沒有留言:
張貼留言