Mail injection. Работа с e-mail средствами PHP.

Что это такое?
Формат e-mail
Защита
Комментарии (21)

Что это такое?
Это когда форма обратной связи на сайте используется для рассылки спама.
Как? Cамый дурной вариант - когда скрипт позволяет пользователю подставить адрес получателя. Это уж совсем ни в какие ворота не лезет. И даже словом injection не назовешь - а просто головотяпство.
Собственно injection - это когда адреса получателей подставляются в поля формы так, чтобы попасть в заголовки письма. К примеру, в заголовок.

Говоря о mail injection, невозможно обойти вниманием сам принцип работы почтовых отправлений. Даже больше скажу: человек, который знает, как устроено электронное письмо, сразу понимает, в чем смысл таких инъекций, и как от них защищаться. Вообще, это та проблема, о которой я все время говорю: Давая новичку полное понимание предмета, ты отвечаешь ему на сто вопросов разом. А пичкать ответами на каждый отдельный вопрос - ну это же жутко непроизводительно! Не по-программистски!
Что проще - посмотреть ответ в таблице умножения, или постоянно бегать к соседу с вопросами - сколько будет пятью пять, трижды семь?
Что проще - один раз объяснить (а ещё лучше - отправить почитать) про формат письма или отвечать на сто вопросов про инъекции, кодировки, вложения файлов и так далее?

Формат e-mail
Итак, что собой представляет почтовое сообщение?
Во-первых, это обычный текст. просто набор строк определенного формата. Которые можно читать и, при некотором навыке - понимать.
Во-вторых, вы будете смеяться, но устроено оно точно по тому же принципу, что и наши веб-страницы.
При запросе страницы по протоколу HTTP сначала идут заголовки, каждый на своей отдельной строке, а затем - тело ответа. Соответственно, заголовки от тела отделяются пустой строкой. Такой вот нехитрый формат. Сначала каждая строка воспринимается как заголовок, а как только встречается пустая - значит, дальше идет тело, которое надо показывать.

В почтовом сообщении все устроено точно так же.
Вообще, я считаю, что один раз посмотреть - это лучше, чем сто раз прочесть. А при работе с почтой исходный текст письма так же важен, как и исходник HTML страницы при разработке сайта на пхп.
Здесь ещё одна проблема. Очень многие программисты просто не знают, что они хотят сделать. Я, говорит, хочу отправить письмо. Но ведь письмо - это ТЕКСТ! Казалось бы, чего проще - составил сначала текст, который хочешь получить, убедился, что этот текст работает - и генери точно такой же на пхп! Но нет. поскольку человек не знает, из каких элементов состоит исходный текст письма, и какой за что отвечает - то и тычется с вопросами: а почему у меня письмо крокозябрами? А почему у меня письмо нормальное, а заголовок крокозябрами - я ведь кодировку указал? А почему заголовок не "Вам письмо" а лабуда какая-то - =?koi8-r?B?98HNINDJ09jNzw==?= ???
С SQL запросами и HTML текстом то же самое. Я, говорит, хочу меню на пхп. Милый - объясняешь ему - меню на пхп не бывает! Нарисуй какое хочешь меню на HTML, а потом пиши скрипт на пхп, который рисует такое же. Мне не надо - орет - на HTML! Подожду ответа более грамотного специалиста!

Так же и здесь. Ну разберись один раз - что какой заголовок значит и как кодируется.
В бате и в Outlook Express посмотреть исходник письма можно. В MS Outlook - только заголовки. В других клиентах не знаю. Но Outlook Express есть на любой виндовой машине, а пользователям других систем, я надеюсь, не нужно объяснять формат почтовых сообщений. Поэтому рекомендую создать письмо в OE, зайти в отправленные, нажать Свойства - Подробно - Исходное сообщение.
Да, очень рекомендую перед этим в настройках выставить формат отправления, как "просто текст". иначе исходник будет гораздо сложнее для понимания. Что мы там увидим?
From: "phorror" <phorror@phorror.ru>
To: =?koi8-r?B?98HNINDJ09jNzw==?= <phorror@phorror.ru>
Subject: =?koi8-r?B?98HNINDJ09jNzw==?=
Date: Wed, 13 Jun 2007 10:08:32 +0400
MIME-Version: 1.0
Content-Type: text/plain;
    charset="koi8-r";
Content-Transfer-Encoding: 7bit
X-Priority: 3
X-MSMail-Priority: Normal
X-Mailer: Microsoft Outlook Express 6.00.2900.3028
X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.3028

test
test
test


на заголовки, начинающиеся с X, обращать внимание не надо - это необязательные.
главное, что мы видим:
1. Все, как я и рассказывал - сначала заголовки, потом пустая строка, потом текст
2. Формат заголовка: начинается с новой строки, дальше идет ключевое слово, дальше двоеточие, пробел и значение (опять очень похоже на НТТР заголовки!)
3. Формат указания e-mail адресов. Кроме имени, понятно без перевода. про имя ниже.
4. формат указания заголовка. Ничё непонятно. Хотя, если не ужасаться, а сесть и немножко подумать, то можно сообразить, что, во-первых, такая лабуда применяется для форматирования строк в кодировках, отличных от latin1, а, во-вторых, формат-то у них совсем простой. Вопросительные знаки разделяют разные поля (точь-в точь как палочки | или пять троеточий в твоей первой гостевой книге). Первым полем идет явно кодировка языка. Вторым - можно догадаться - формат кодирования текста. Там может быть B или Q. (base64 и Q-encoding соответственно). А дальше - сам текст заголовка.
Следовательно, понятен и алгоритм раскодирования: разбиваем по вопросам, раскодируем текст (функции есть в пхп, для Q подойдет от quoted-printable, как я понимаю), перекодируем язык если надо. Всё. Для сборки своего заголовка - обратная операция.
Если бы сабжект был английский, то он выглядел бы просто:
Subject: This is a test letter
5. Поле Content-Type показывает нам, что заголовок может состоять не из одной строки, а может продолжаться на следующих - для этого дополнительные строки должны начинаться с пробельного символа. А так же, что она отвечает за кодировку текста письма, указывает формат - текст или html, и ведает ещё одной очень важной функцией: отвечает за структуру сложных, multipart сообщений, которые мы рассматривать здесь не будем.

Из всего вышесказанного видно, что научиться отправлять собственные письма (те, которые формирует скрипт, а не из заполненной формы) очень просто:
1. Пишем в аутлуке ровно такое же письмо, которое хотим отправлять скриптом.
2. Смотрим заголовки.
3. Пишем скрипт, который формирует точно такие же.
4. Подсталяем эти заголовки в нужные поля функции mail
5. Готово!
И никаких проблем с кодировками самого письма, заголовоков, имени отправителя! слово "крокозяблы" пропадает из нашего лексикона!
И с SQL запросами все ровно то же самое. новички часто пытаются сразу писать скрипт, который формирует сложный запрос, даже не представляя себе в точности, как этот запрос должен выглядеть! Запрос сначала надо написать и отладить в консоли или графическом клиенте. А потом писать пхп скрипт, который формирует точно такой же текст запроса.

Всё. Теперь можно переходить к инъекциям.

Защита
Исходя из структуры письма можно сделать простой вывод: используя переводы строк, можно добавить любое количество новых заголовоков, в том числе - и адресов получателей.
то есть, если мы напишем в своем коде
$subject=$_POST['subject'], а в этом поле будет написано сто адресов получателей, разделенных переводами строк, то письмо отправится сотне жертв. просто, как два байта переслать.

Отсюда следует, что и защита форм обратной связи такая же простая.
Самое надёжное - помещать введенную пользователем информацию только в текст письма, и никуда больше. Сам я использую именно этот метод. Мне он нравится своей простотой и надежностью. А чем решение проще - тем оно мне милее.

Если же ну прямо так уж хочется эмулировать отправку "настоящего" емейла - с заголовком, адресом отправителя и так далее, то все данные из формы, которые вставляются не в текст сообщения, следует обязательно проверить на наличие символов "\r" и "\n"!
И при наличии таких символов письмо не отправлять.
Адрес получателя, разумеется, дожен быть жестко прописан в скрипте (видал я некоторых уникумов, писавших адрес в скрытое поле в форме).

Всё.