疑問に思ったきっかけ
PHP BLTというイベントでの発表の一つ
大枠はPhalconの話であるが、メール送信時の改行コードの扱い方についての話があった。
以前、mail()関数を使ってメール送信で問題が起こり、同じような方法で回避した覚えがある。
気になったので調べてみようと思った。
まずはマニュアルを見てみる
bool mail ( string $to , string $subject , string $message [, string $additional_headers [, string $additional_parameters ]] )
message
送信するメッセージ。
改行コードは CRLF (\r\n) となります。各行の長さは 70 文字を超えては いけません。
additional_headers(オプション)
メールヘッダの最後に挿入される文字列。
通常、これは追加のヘッダ(From、Cc、Bcc)のために用いられます。 複数のヘッダを追加する場合は CRLF(\r\n)で区切ります。 外部からのデータを用いてヘッダを組み立てる際には、想定外のヘッダが注入されることを防ぐための処理が必要です。
このように、ヘッダも本文もCRLFで改行をするようにと書いてある。
実装を見てみる
PHP5系と7.0.0とで大きな違いはないため、7.0.0のソースを見てみます
mail.c at php-7.0.0
sendmailコマンドのプロセスを立て、それに対してヘッダ・本文の情報を出力している(399行目から)
fprintf(sendmail, "To: %s\n", to);
fprintf(sendmail, "Subject: %s\n", subject);
if (hdr != NULL) {
fprintf(sendmail, "%s\n", hdr);
}
fprintf(sendmail, "\n%s\n", message);
ret = pclose(sendmail);
To や Subject、additional_headersの後、messageの前後の改行はLFである。
つまり複数のadditional_headersや複数行のmessageの改行をCRLFにすると、LFのみとCRLFが混ざった文字列をsendmailコマンドに渡すことになる。
実践
LFのみで改行した場合、CRLFで改行した場合の2通りを試してみた。
検証環境
PHP : 5.5.29
MTA : Postfix 2.6.6
LFのみで改行
<?php
mail(
'to@example.com',
'Test Subject',
"row1\nrow2\nrow3",
"From: from@example.com\nX-Mailer: hoge mail",
);
To: to@example.com
Subject: Test Subject
X-PHP-Originating-Script: 5000:mail-lf.php
From: from@example.com
X-Mailer: hoge mail
row1
row2
row3
LFのみで改行して、特に問題は見られない
CRLFで改行
<?php
mail(
'to@example.com',
'Test Subject',
"row1\r\nrow2\r\nrow3",
"From: from@example.com\r\nX-Mailer: hoge mail",
);
To: to@example.com
Subject: Test Subject
X-PHP-Originating-Script: 5000:mail-crlf.php
From: from@example.com
X-Mailer: hoge mail
row1
row2
row3
CRLFで改行をすると、本文に余計な改行が入る。
ヘッダは問題なし
LFのみで改行するほうが正しいのでは?
考察とまとめ
マニュアルには次のような記述がある。
注意:
メッセージが受信されなかった場合には、LF(\n)のみを使ってみてください。 Unix の MTA の中には、自動的に LF を CRLF に変換してしまう もの (有名なところでは、» qmail など) があります(もし CRLF を利用していた場合、CR が重複してしまいます)。 ただし、これは最後の手段です。というのも、これは » RFC 2822 に違反しているからです。
MTAによって、改行コードを変えるなどの対応が必要な場合があるらしい。
今回、Postfixでしか検証をしていない。他のMTA、例えばSendmailであれば結果は違うかも?
要検証
sendmailコマンドを使わずに、PHPからSMTPのリクエストを送信するような場合にはCRLFで改行する必要はあると思う。
sendmailコマンドを使う(mail()関数を使う)場合には、改行が意図した通りになされているか確認したほうが良い。
もっと時間のある時に、詳細に調べたい。