E-mailový formulář

Školení, která pořádám

Z několika důvodů je zvykem na web umísťovat formulář pro poslání e-mailu. Častý je kontaktní formulář sloužící jednak jako obrana před spamem, kdy nechceme na webu zveřejňovat svou e-mailovou adresu, a jednak určený pro lidi bez e-mailového klienta. Osobně tyto formuláře nemám rád, protože nemám k dispozici pohodlné prostředí svého e-mailového klienta a takto odeslané zprávy se mi nikam neuloží, a doporučuji třeba pod tímto formulářem zobrazit i skutečnou e-mailovou adresu (alespoň zkomolenou nebo ve formě obrázku, ale osobně používám plně funkční zápis jakub@vrana.cz, se kterým si spousta sběračů adres neporadí). Druhým častým případem e-mailového formuláře je poslání odkazu na článek, kde to má větší smysl, protože kromě URL odkazu můžeme automaticky poslat třeba i nadpis a perex.

Formulář se v PHP velice jednoduše zpracuje funkcí mail. Překvapivě i při použití této funkce musíme pamatovat na bezpečnost. Pokud uživateli dovolujeme vyplnit adresu příjemce (tedy např. při poslání odkazu na článek), měla by být určitá část zprávy pevná a u doplňujícího textu je vhodné omezit délku, aby formulář někdo nezneužil k něčemu zcela jinému. Pokud umožňujeme vyplnit adresu odesílatele (jako u kontaktního formuláře), kterou následně použijeme v hlavičce From nebo Reply-To, je nutno tuto hlavičku pečlivě kontrolovat. E-mailová zpráva je totiž podobně jako HTTP komunikace tvořena sadou hlaviček následovaných prázdným řádkem a tělem zprávy. Pokud tedy v kódu máme jednoduché mail($to, $subject, $zprava, "Reply-To: $_POST[from]") a útočník nám za from podstrčí adresa\nCc: adresa, …\n\nNová zpráva, vytvoří se následující zpráva:

To: $to
Subject: $subject
Reply-To: adresa
Cc: adresa, …

Nová zpráva

Původní zpráva

Pokud tedy v hlavičkách používáme jakoukoliv hodnotu zadanou uživatelem, je vhodné ji přinejmenším za prvním koncem řádku oříznout. U kontaktních formulářů je také vhodné adresu příjemce neuvádět ve skrytých polích formuláře, ale až ve skriptu pro poslání zprávy, protože jinak opět může dojít ke snadnému zneužití formuláře. Pokud má skript různé formuláře odesílat na různé adresy, je vhodné v něm definovat číselník povolených adres a ve skrytém poli formuláře potom uvádět záznam z tohoto číselníku.

U posílání zpráv je také nutné používat správné konce řádků. Norma hovoří o \r\n, ale řada Unixových posílátek v odesílaných zprávách automaticky nahrazuje \n za \r\n, takže použití \r\n způsobí odeslání \r\r\n a tím nevalidní zprávu. O žádném řešení zajišťujícím univerzálně přenositelné použití bohužel nevím.

Pokud chceme poslat zprávu v češtině, mělo by u novějších serverů stačit poslat hlavičky Content-Type: text/plain; charset=iso-8859-2 a Content-Transfer-Encoding: 8bit (zprávy by však neměly mít na žádném řádku více než 1000 bajtů, takže se může hodit funkce wordwrap). Hlavičky e-mailů je nutné zakódovat zvlášť. Složitější zprávy s přílohami nebo s alternativním HTML formátem lze vytvořit třeba funkcí imap_mail_compose nebo pomocí knihovny PEAR Mail_Mime.

Přijďte si o tomto tématu popovídat na školení Bezpečnost PHP aplikací.

Jakub Vrána, Výuka, 30.1.2006, diskuse: 23 (nové: 0)

Diskuse

Bohdan:

Jednoduchá (ale šikovná) třída na posílání e-mailů:

http://phpmailer.sourceforge.net/

ikona llook:

> "zápis jakub@vrana.cz, se kterým si spousta sběračů adres neporadí"
To je pověra, o sběračích adres toho víme ještě méně než o vyhledávačích. Kdybych psal sběrače, použil bych asi něco na způsob Watir (prostě skriptem ovládaný skutečný browser), takže bych dostal i adresy vkládané přes document.write.

Pokud adresu dokáže interpretovat prohlížeč, může ji přečíst i sběrač.

ikona MISHAK.wz.cz wz Bohužel:

Mám trochu času (čerstvě bez práce (10minut))
1.) Ja jsem to vyresil trochu jinak http://mishak.wz.cz/mail.php nechal jsem uzivateli na vyber jestli chce posilat pres klienta nebo web.
2.) Na odhaleni emailu staci napsat jednoduchou sadu reg. vyrazu a je to, takze ani zapis &#64 neni vselekem ale 90% neprojde. Rozhodne jsem proti zapisum jakub[at]vrana.cz nebo jeste horsim jakub zavinac vrana tecka cz.
Rozhodne jsem pro user friendly copy a paste nebo jen zkopirovat odkaz :-)
ale to je imho muj nazor...
jo 2.2.2006 budu otvirat blog :-)

ikona llook:

"ale 90% neprojde" To je právě to, o čem mluvím. Tendence podceňovat nepřítele, kterého neznáme.

Pokud jde o neco&#64;neco, stačí i obyčejné DOMDocument::loadHtml(). U ošetření přes ne<span></span>co&#64;<span>neco</span> je to už o něco těžší, ale pořád to lze. Další způsob co mě napadá je "lynx -dump", který celou stránku převede na prostý text a ještě přidá seznam odkazů (v nichž také entity nejdřív převede).

Pokud tohle napadne někoho (mě), kdo nemá v úmyslu účelům sbírat adresy, co je asi schopný vymyslet někdo, kdo z toho má prachy?

btw. Sám používám nejradši webmail (konkrétně Gmail) a určitě nejsem výjimkou. Na to tvůj formulář nepomýšlí.

ikona MISHAK:

Dobre dostal jsi me...

ikona spaze:

Neznám kvalitu mailových sběračů, ale obecně spamware disponuje všeobecně nízkou kvalitou. Odvozuju tak z jakéstakés znalosti rozesílačů, kdy ty softy nedodržují žádný normy a doporučení, pálí stylem fire-and-forget apod. Na zjištění těchhle rozesílačů stačí pár jednoduchých pravidel (a teď nemyslím pravidla do nějakého učícího se classifieru). Sběrače budou IMHO psát lidé stejného ražení, takže i kvalita bude ~stejná. Samozřejmě, jsou i skvěle napsané kousky :)

Mám pocit, že lidi, co mají znalosti prostě nebudou dělat kvalitní spamware na jedný straně, aby na druhý straně museli dělat kvalitní antispamware :) Ale jsou i tací s černejma kloboukama ;)

ikona MISHAK:

1.) Taky jsem spis pro pouziti imap pak pear class pak phpmailer IMHO vetsinou mi staci mail.
2.) Ohledne bezpecnosti, co kdyz uzivatel zada seznam oddeleny carkou jako "jakub&#64vrana.cz, jiri&#64kosek.cz" a pod. a hidden prvek je bezpecny asi tak jako kure pred hadem.
3.) Co je to ciselnik (presne/definice)?
dik uz nebudu otravovat ;-] :-]

ikona spaze:

2) ale nevecpe si tam svůj obsah. Jestli teda dobře chápu co tím bylo myšleno.

3) $ciselnik = array(
1 => 'foo@example.com',
2 => 'bar@example.com',
3 => 'quux@example.com',
);

Z formu pak dostaneme 1,2,3.

maro:

Mám problém s češtinou v předmětu. Stránky mám v utf8, pro mail() používám toto:
<?php
$cfg
['hlavicka']='From: "admin" <admin@neco.cosi>'."\n".
'MIME-Version: 1.0'."\n".
'Content-Transfer-Encoding: Binary'."\n".
'Content-Type: text/html;charset=utf-8'."\n";
mail($zaznam['email'],'ěščřž','obsah',$cfg['hlavicka']);
?>
Obsah mailu je v pořádku, ale předmět je divný. Zkoušel jsem použít i jiné Content-Transfer-Encoding, ale nepomohlo. Nevíte v čem dělám chybu?

ikona Jakub Vrána OpenID:

Kódování se pro každou hlavičku určuje zvlášť. Viz např. http://en.wikipedia.org/wiki/MIME#Encoded-Word.

vin:

Možná hloupá otázka, ale kdy se vyplatí(pokud se vůbec vyplatí) přejít z číselníku z pole na číselník z databázové tabulky?
A mimochodem, s tím posíláním adresy příjemce přes hiddeny...nestačilo by adresu rozdělit do více hiddenů, části ještě zašifrovat a ve skriptu posléze rozšifrovat a spojit?

ikona Jakub Vrána OpenID:

Já rozlišuji dva typy číselníků: 1. Pevné, u kterých nemá smysl, aby je uživatel měnil - ty jsou obvykle přímo ve sloupci jako ENUM. 2. Uživatelsky měnitelné - ty jsou obvykle v jednoduché tabulce (ID, Název). U e-mailových adres, na které se má odesílat formulář, dává smysl, aby si je měnil uživatel.

Co se šifrování adres týče - co z toho?

vin:

Snad jenom to, že by se někdy někomu nechtělo pro samotné e-maily tahat databázi. Člověk může mít víc adres, které toho nemají moc společného (a tak netvoří nějaký seznam/tabulku, který by se dal snadno vytáhnout z databáze a použít v tabulce na stránce - je třeba v souvislém textu), a přitom se nechce probírat tabulkou při úpravě stávající adresy. K linku na adresu se dá pak přistupovat přes nějakou šablonu přes parametry.

Na druhou stranu, takový případ asi zas tak často nenastává.

liska11:

jak si mohu poslat více věcí na email jednu umím jako kdybych měla zprava: a vt om nakej text tak to mi de ale pokud budu mít víc polí a chci všechny poslat na email jak to mám uděla¨

předem děkuji za radu

Koumak :D:

Udelal jsem formular na webu ic.cz ale mail se odesle pouze do administrace a ne do meho mailu. Jak nastavit aby se mail poslal do mailu?

kikakupska:

Dobrý den,

můžete mi prosím blíže specifikovat následující "Pokud tedy v hlavičkách používáme jakoukoliv hodnotu zadanou uživatelem, je vhodné ji přinejmenším za prvním koncem řádku oříznout." jak na to? Děkuji. Kupská

Prdlořeznictví Krkovička, n. p.:

<?php
// predpokladame, ze ve $_POST['from'] je e-mailova adresa odesilatele, ale uzivatel muze podstrcit i vice radku..

$pos=strpos($_POST['from'],"\n");
if(
$pos!==false)
  $_POST['from']=substr($_POST['from'],0,$pos);
?>

pojízdná kočka:

Jakube, znáš MOD_SECURITY? Nedávno jsem na něco takového narazila: (např. návod na zprovoznění zde - http://forum.mamboserver.com/showthread.php?t=26406 )
Jak jsem tak koukala, několik prvků, které v něm lze nastavit, se týkají právě podstrčeného nového řádku do mailu:

SecFilter "\x0aTo\:"
SecFilter "\x0ato\:"
SecFilter "\x0aFrom\:"
SecFilter "\x0afrom\:"
SecFilter "\x0aCc\:"
SecFilter "\x0acc\:"
SecFilter "\x0aBcc\:"
SecFilter "\x0abcc\:"

(zatím to teprve pročítám, takže ještě moc neznám podrobnosti...)

ikona v6ak:

To se mi zdá jako blacklist, ať se na to koukám sebevíc. A k tomu jsem celkem skeptický.

ebací krtek:

IC.cz = mail fce nefunguje, nebo nekomu snad jo?

Michal:

Tyto dotazy směřujte sem http://diskuse.jakpsatweb.cz/?action=vtopic&forum=9 jak je uvedeno u formuláře.

radomira navratilova:

prosim vas chtela bych zalozit imejl a nevim jak nato

ikona Jakub Vrána OpenID:

Např. na email.cz nebo gmail.com.

Diskuse je zrušena z důvodu spamu.

avatar © 2005-2024 Jakub Vrána. Publikované texty můžete přetiskovat pouze se svolením autora. Ukázky kódu smíte používat s uvedením autora a URL tohoto webu bez dalších omezení Creative Commons. Můžeme si tykat. Skripty předpokládají nastavení: magic_quotes_gpc=Off, magic_quotes_runtime=Off, error_reporting=E_ALL & ~E_NOTICE a očekávají předchozí zavolání mysql_set_charset. Skripty by měly být funkční v PHP >= 4.3 a PHP >= 5.0.