Autor Zpráva
Tomáš123
Profil
Prajem pekný večer,
Chcel by som umožniť čitateľovi informovať ma prostredníctvom e-mailu, ak nájde v článku chybu. Na internete som našiel jednoduchý návod ako na to, ale nezdá sa mi, že takýto skript je dobre zabezpečený a že ho môžem spokojne používať. Ak sa naozaj nemýlim, vedeli by ste mi prosím napísať, ako sa bezpečne spracúva kontaktný formulár podobného charakteru? Pre informáciu: je funkcia mail() jediná, ktorá dokáže odoslať e-mail?

Ďakujem za odpoveď.
lionel messi
Profil
Tomáš123:
Odkázaný návod mi neotvorí, stránka má asi výpadok. Slušný návod na odoslanie e-mailov je v FAQ, myslím, že ako prvý krok je určite dostačujúci.

Častejšie sa však na spoľahlivé riešenie odosielania e-mailov v PHP používajú rôzne hotové riešenia, napríklad PHP Mailer.
Tomáš123
Profil
lionel messi:
FAQ
Krásny návod. Zajtra sa tam určite pozriem a veľa si toho prečítam. Škoda, že som to neobjavil skôr...

Predstav si, že aj keď ide o takto jednoduchý skript, otáznik mi neprišiel ani jeden:
<meta charset="UTF-8">
<?php

$prijemca = "mail";
$meno = $_POST['meno'];
$priezvisko = $_POST['priezvisko'];
$email = $_POST['email'];
$predmet = $_POST['predmet'];
$text = $_POST['text'];


if ($meno && $email && $predmet && $text){

mail("$prijemca", "$predmet", "$meno $priezvisko Vám posiela tento text $text",
"from: $email");

echo "Váš <b>email bol</b> úspešne <b>odoslaný</b>!";

}

elseif (($meno && $email && $predmet && $text) == false){

echo "Váš <b>email nebol odoslaný</b>. <b>Nezadali ste všetky</b> povinné
<b>údaje</b>.";

}

?>
Prosím nekritizovať, kvalitu kódu. Nie je môj a určité chyby vidím aj ja.

Hotové riešenia nemám rád. Pohliadnem sa po FAQu, niečo vytvorím a ak budem mať nejaký problém ozvem sa.
Joker
Profil
Tomáš123:
Funkce mail() sice není jediný možný způsob, jak z PHP odeslat e-mail, ale je asi nejjednodušší. Pro jednoduché textové maily je použití mail() rychlé a snadné, pro složitější věci (přílohy apod.) může být lepší použít nadstavbu, třeba PHP Mailer.

U e-mailů se „zabezpečení“ týká hlavně toho, aby skrz to nešly rozesílat spamy (zejm. odesílat zprávy na zvolenou adresu). Případně když někdo odešle zprávu a pak chvíli podrží F5, asi by bylo fajn neodeslat hromadu dalších zpráv.

lionel messi:
PHP Mailer při typickém použití uvnitř taky používá funkci mail() (i když umí i jiné způsoby).
Tomáš123
Profil
Joker:
třeba PHP Mailer.
Je táto nadstavba niečo podobné ako jQuery pre JavaScript? Teda, dá sa všetko čo je v PHP Maileri odprogramovať aj ručne?

Případně když někdo odešle zprávu a pak chvíli podrží F5, asi by bylo fajn neodeslat hromadu dalších zpráv.
Na to som úplne zabudol. Je tento druh zabezpečenia spomenutý v miestnom FAQ? Hľadal som, ale nenašiel. Ak nie, prostredníctvom čoho sa dá užívateľovi znemožniť poslať dva maily v určitom časovom intervale? Pôjde o prácu s $_SESSION?

Ešte by ma zaujímalo, ako to, že vo vyššie uvedenom kóde [#3] nepoužívam žiadne z rád spomenutých tu a aj tak mi text prišiel úplne správne.
pcmanik
Profil
Tomáš123:
Teda, dá sa všetko čo je v PHP Maileri odprogramovať aj ručne?
Neviem čo si predstavuješ pod pojmom nadstavba, ale je to obyčajná trieda v php. Len ti uľahčuje mnohé veci spojené s mailom, ako napríklad už spomenuté posielanie príloh. Lebo to riešiť cez mail() je naozaj o nervy.

znemožniť poslať dva maily v určitom časovom intervale
Session, cookies, local storage, ak je užívateľ prihlásený záznam v DB... Možností je veľa.

Ešte by ma zaujímalo, ako to, že vo vyššie uvedenom kóde
Väčsina klientov to dokáže prekúsať, ale viem že napríklad zoznam s tým má problém a zobrazí rozhádzané znaky, preto je lepšie tam tie hlavičky uviesť.
Tomáš123
Profil
pcmanik:
Neviem čo si predstavuješ pod pojmom nadstavba
To čo #4 Joker.

Session, cookies, local storage
Mohol by si mi priblížiť jednu z možností. Stačil by nejaký odkaz, kde je to dobre popísané (nevyhľadal som si to, lebo neviem, čo mám hľadať).
pcmanik
Profil
Tomáš123:
Napríklad si teda ulož do session čas posledného odoslania mailu, ak je menej ako povedzme 5 sekúnd, nedovolíš užívateľovi odoslať ďalší.
Tomáš123
Profil
pcmanik:
si teda ulož do session čas posledného odoslania mailu
Skúšal som to nejako takto, ale neviem, ako ďalej:
    session_start();
    $time = time();
    $newTime = 
    if (!$newTime - $time > 100) {
        echo "<p>Pred odoslaním ďalšieho e-mailu musíte chvíľu počkať</p>";
    }
Neviem, ako získať čas nového odoslania ($newTime)...

Čas som skúšal zistiť pomocou funkcie time() alebo poľa $_SERVER['REQUEST_TIME'] a formát obidvoch výsledkov je takýto: 1419792351. Posledné dvojčíslie sú sekundy a ďalej to ide asi tak, ako normálne. Minúta má však 100 sekúnd a pravdepodobne aj hodina má 100 minút, na to som ale nečakal. Teoreticky, je jedno, ako sa ten čas zobrazuje, keď beží správne, však.
mimochodec
Profil
Tomáš123:
Ak nie, prostredníctvom čoho sa dá užívateľovi znemožniť poslať dva maily v určitom časovom intervale?

To sice není úplně chybná úvaha, ale možná zbytečná. Základem použití formulářů je, že když detekuješ, že formulář byl vyplněn a odeslán, zpracuješ ho a přesměruješ na adresu, kde už F5 další odeslání toho formuláře nevyvolá. Když tam budeš mít toto, myslím, že nějakými časovými limity se zalamovat nemusíš.


Tomáš123:
Minúta má však 100 sekúnd a pravdepodobne aj hodina má 100 minút

No, to mě lehce pobavilo.. funkce time() pracuje s formátem http://en.wikipedia.org/wiki/Unix_time - tzn. počet sekund, uplynulých od 00:00:00 Coordinated Universal Time (UTC), Thursday, 1 January 1970.
lionel messi
Profil
Tomáš123:
Čas som skúšal zistiť pomocou funkcie time() alebo poľa $_SERVER['REQUEST_TIME'] a formát obidvoch výsledkov je takýto: 1419792351.
Tento formát je tzv. Unix timestamp a vyjadruje počet sekúnd, ktoré uplynuli od 1. 1. 1970 0:00.

Ale lepšie je spracovať to tak, ako píše mimochodec.
Tomáš123
Profil
mimochodec:
No, to mě lehce pobavilo
Už budem vedieť, ďakujem.

mimochodec, lionel messi:
Teda, viem, že už to nie je potrebné, mohli by ste mi prosím aj tak skúsiť upraviť uvedený kód, uvažujme, že negenerujem žiadnu správu... Je to „látka naviac“, takže, ak sa vám nechce, pochopím to.
pcmanik
Profil
lionel messi:
Je to ochrana pred neopatrnými užívateľmi, ale stále to nezabráni robotovi neustále odosielať emaily...

Tomáš123:
S tými sekundami si naozaj pobavil :D Si chýbal na ZŠ?

session_start();

if (isset($_SESSION['last_email']) && time() - $_SESSION['last_email'] < 5) echo 'Neubehlo 5 sekúnd počkajte pred ďalším odoslaním mailu.';
elseif (mail(...)) {
    $_SESSION['last_email'] = time();
    echo 'Email bol odoslaný';
}
else echo 'Problém s odosielanim mailu';
lionel messi
Profil
Tomáš123:
Je to ‚látka naviac‘, takže, ak sa vám nechce, pochopím to.
Nájdu sa skôr iné veci, ktoré by sa mi nechceli urobiť (nesúvisiace s webmi). :-)

Spôsobov je viacero, ten najjednoduchší je po úspešnom odoslaní formulára presmerovať:
<meta charset="UTF-8">
<?php
 
$prijemca = "mail";
$meno = $_POST['meno'];
$priezvisko = $_POST['priezvisko'];
$email = $_POST['email'];
$predmet = $_POST['predmet'];
$text = $_POST['text'];
 
 
if ($meno && $email && $predmet && $text){
 
  if (mail($prijemca, $predmet, "$meno $priezvisko Vám posiela tento text $text",
"from: $email")) {
      header("Location: ok.php");
      exit();
    } 
}
 
elseif (($meno && $email && $predmet && $text) == false){
 
echo "Váš <b>email nebol odoslaný</b>. <b>Nezadali ste všetky</b> povinné
<b>údaje</b>.";
 
}
 
?>

a hlášku o úspešnom odoslaní vypíšeš na stránke ok.php.

Pozri aj na pripomienku od pcmanika.
Fisir
Profil
Reaguji na pcmanika:
Ani toto pravděpodobně nezabrání robotovi odesílat emaily neustále po sobě, protože k tomu, aby se na serveru udržel přehled, jaká sada sessions patří k jakému návštěvníkovi, musí mít návštěvníci uloženou identifikační cookie. A pochybuji, že roboti toto budou splňovat. Jako definitivní řešení bych tedy označil ukládání posledních odeslaných mailů do databáze; pokud budou texty odeslané krátce po sobě příliš podobné či shodné, odeslání neumožnit. Nebo si zkrátka pořídit dobrý spamový filtr k emailu.
Tomáš123
Profil
pcmanik:
Si chýbal na ZŠ
Podľa nových koncepcií sa látka „čas“ preberá až v druhom polroku prvého ročníka. Kam to školstvo speje? Ja samozrejme nie som závislý od školy a pracovať s časom som sa naučil už v troch rokoch. Keď som ale videl tvar čísla a po opakovanom stlačení F5 sa koncovka menila tak rýchlo ako ubiehali sekundy, vedel som o čo ide. Neskôr sa tam ale objavilo číslo väčšie ako 59 a tak sa v mojej hlave zrodila myšlienka, či to hádam nepočíta do 100. Tak som to sem aj napísal.

Inak ďakujem za kód.

lionel messi:
Taktiež ďakujem.

Máme tu teda dve možnosti. Typujem, že tá so $SESSION by bola lepšia (pri funkcii header() by sa nemusel načítať súbor). Ani jednu z nich ale nepotrebujem, pretože roboti vypĺňajú všetky polia a tak sa cez všeobecne známu spam ochranu nedostanú (snáď).

EDIT: Keďže po odoslaní sa výsledok vygeneruje na tej istej stránke, dá sa stlačiť F5 a po potvrdení „Znova odoslať“ sa e-mail bez problémov odošle, i keď určite 100 sekúnd neprešlo. Čo mám v kóde zle (chyba sa negeneruje)?:
<?php
    session_start();
    if (isset($_SESSION['last-email']) && time() - $_SESSION['last-email'] < 100) {
        echo "<p>Pred odoslaním ďalšieho e-mailu musíte chvíľu počkať</p>";
    }
    else {
        $recipient = "tomas123@123.sk"; //e-mail neexistuje
        $show = true;
        if(isset($_POST['name'], $_POST['email'], $_POST['subject'], $_POST['text']) && empty($_POST['hidden'])) {
            $name = htmlspecialchars($_POST['name']);
            $email = htmlspecialchars($_POST['email']);
            $subject = htmlspecialchars($_POST['subject']);
            $text = htmlspecialchars($_POST['text']);
            $content = "Email od: <b>".$name."</b>\n".$text;
            if (mail($recipient, $subject, $content)) {
                $_SESSION['last_email'] = time();
                $return = "<p>E-mail bol úspešne odoslaný</p>";
                $show = false;
            }
            else {
                $return = "<p>E-mail sa nepodarilo odoslať</p>";
                $show = false;
            }
        }
        else {
            $return = "<p>Vyplňte prosím všetky formulárové polia</p>";
        }
    }
?>
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <link rel="stylesheet" href="css/styles.css">
        <title><?php echo $return;?></title>
    </head>
    <body>
<?php
    echo $return;
    if ($show == true):?>
        <form action="processing.php" method="POST">
            <fieldset><legend>Kontaktný formulár</legend>
                <table>
                    <tr style="display: none">
                        <td><strong>Nevylplniť:</strong></td>
                        <td><input type="hidden" name="hidden"></td>
                    </tr>
                    <tr>
                        <td><strong>Meno:</strong></td>
                        <td><input type="text" name="name"></td>
                    </tr>
                    <tr>
                        <td><strong>E-mail:</strong></td>
                        <td><input type="text" name="email"></td>
                    </tr>
                    <tr>
                        <td><strong>Predmet:</strong></td>
                        <td><input type="text" name="subject"></td>
                    </tr>
                    <tr>
                        <td valign="top"><strong>Text správy:</strong></td>
                        <td><textarea name="text" cols="50" rows="10"></textarea></td>
                    </tr>
                    <tr>
                        <td></td>
                        <td><input type="submit"></td>
                    </tr>
                </table>
            </fieldset>
        </form>
    <?php endif ?>
    </body>
</html>    
mimochodec
Profil
Tomáš123:
pretože roboti vypĺňajú všetky polia

A jen vyjímečně umí javascript. Toho využívá ochrana, o které někde psal Yuhů. Přidej si do formuláře jeden input navíc, k němu pro jistotu napiš něco jako "sem nepište nic" a toto všechno skryj javascriptem. Když v tom něco bude, posílá to robot. S úspěchem používám.
Tomáš123
Profil
mimochodec:
Spoliehal som na to, že každý si prezrie celý kód. Ak by si tak učinil, zistil by si, že presne to, čo si popísal už robím (s tým rozdielom, že na zmiznutie nepoužívam JavaScript).

Keďže sa objavil nový problém a nechcel som zasielať nový príspevok, keď ten predošlý ešte možno nikto nevidel, pridal som tam iba úpravu. Prosím, prečítajte si to.
mimochodec
Profil
Tomáš123:
ok, nečetl jsem, pardon.

K tomuto:
Keďže po odoslaní sa výsledok vygeneruje na tej istej stránke, dá sa stlačiť F5 a po potvrdení ‚Znova odoslať‘ sa e-mail bez problémov odošle, i keď určite 100 sekúnd neprešlo. Čo mám v kóde zle (chyba sa negeneruje)?:

Tím, že nezobrazíš formulář, možnost refreshe neovlivníš. Přesměrovávej tak, jak radí lionel messi - pomocí header location.
Tomáš123
Profil
mimochodec:
ok, nečetl jsem, pardon
V poriadku. Aj ja sa musím ospravedlniť. Trochu som vyskočil.

Tím, že nezobrazíš formulář, možnost refreshe neovlivníš
To samozrejme viem, viem aj o riešení, ktoré poskytol lionel, ale vyriešiť by som to chcel pomocou session.
mimochodec
Profil
Tomáš123:
Teď už se nevyznám v tom, co vlastně řešíš. Konkrétně ta záležitost se session by se dala udělat třeba takto.

 session_start();

 if (time() < ( $_SESSION['mailodeslan'] +100)) {
  // ...  nic nebude
 } else {
  // ...  posilam mail

 $_SESSION['mailodeslan'] = time();
 }

Chybí tam ošetření prvního pokusu, tzn. stavu, kdy ta session ještě neexistuje. To máš za domácí úkol.
Kubo2
Profil
lionel messi ([#14]; outdated reaction):
Trochu by som si dovolil tvoj kód „doladiť“.

Riadok 4 by bolo vhodné zameniť za konštantu.
const PRIJEMCA = 'tomas123@example.com';

Riadky 5, 6, 7, 8 a 9 vygenerujú v prípade neexistencie príslušného indexu v poli $_POST chybu E_NOTICE. Obísť sa to dá (s minimálnym zásahom do kódu) takto:
$meno       = & $_POST['meno'];
$priezvisko = & $_POST['priezvisko'];
// ...
...čo je však strašná prasiareň, pretože v PHP referencie fungujú úplne inak, ako by sa dalo logicky predpokladať; hlavne pre tých, ktorí nikdy nenahliadli do zdrojového kódu PHP Interpreta.

Lepším spôsobom teda je:

// ...
$required = ['meno', 'priezvisko', 'email', 'predmet', 'text'];

foreach($required as $field) {
   if(empty($_POST[$field]) || is_array($_POST[$field])) {
      printf('Nezadali ste %s.', $field);
      exit(1);
   }
   $$field = $_POST[$field]; // vytvorí premennú s názvom, ktorý je položkou poľa $required
}
// ...

if(mail(...)) je na môj vkus tiež dosť neprehľadný, výrazne by sa asi zvýšila čitateľnosť nasledujúcou zmenou.
$mailSent = mail(PRIJEMCA, $predmet, sprintf("Emailový klient Vášho webu: %s %s Vám posiela text:\n\n%s", $meno, $priezvisko, $text), "From: $email");

if($mailSent) {
   // ...
}

Riadok 16 by mal posielať kód 303 namiesto stávajúceho 302.
lionel messi
Profil
Kubo2:
Trochu by som si dovolil tvoj kód ‚doladiť‘.
V pohode, ale nie je to môj kód, vznikol z návodu odkázaného v [#1] Tomáš123 zavrhnutia hodnou metódou copy/paste. Úpravy sú rozhodne zmenami k lepšiemu, len miesto $required = ['meno', 'priezvisko', 'email', 'predmet', 'text']; by som pre istotu použil klasický zápis $required = array("meno", "priezvisko", "email", "predmet", "text");, prvá verzia totiž funguje až od PHP 5.4.
Tomáš123
Profil
mimochodec:
by se dala udělat třeba takto
Aj toto už mám vyriešené: Viď kód v [#16]. Štruktúru mám veľmi podobnú. Teda to nefunguje preto, lebo pri prvom odoslaní neexistuje $_SESSION['last-email']?

To máš za domácí úkol.
Rozmýšľal som nad tým... Šlo by to nejako takto?:
<?php
    session_start();
    if (!isset($_SESSION['last-email']) OR time() - $_SESSION['last-email'] > 100) {
        $recipient = "tomas123@example.com";
        $show = true;
        if(isset($_POST['name'], $_POST['email'], $_POST['subject'], $_POST['text']) && empty($_POST['hidden'])) {
            $name = htmlspecialchars($_POST['name']);
            $email = htmlspecialchars($_POST['email']);
            $subject = htmlspecialchars($_POST['subject']);
            $text = htmlspecialchars($_POST['text']);
            $content = "Email od: <b>".$name."</b>\n".$text;
            if (mail($recipient, $subject, $content)) {
                $_SESSION['last_email'] = time();
                $return = "<p>E-mail bol úspešne odoslaný.</p>";
                $show = false;
            }
            else {
                $return = "<p>E-mail sa nepodarilo odoslať.</p>";
                $show = false;
            }
        }
        else {
            $return = "<p>Vyplňte prosím všetky formulárové polia.</p>";
        }
    }    
    else {
        echo "<p>Pred odoslaním ďalšieho e-mailu musíte chvíľu počkať.</p>";
    }
?>

Zdá sa mi, že by to malo fungovať, ale nefunguje...

lionel messi:
z návodu odkázaného v [#1] Tomáš123
Aj ja som upozornil na nekvalitu kódu... Takže to je vina stránky, z ktorej som ho skopíroval.
pcmanik
Profil
Kubo2:
Riadok 4 by bolo vhodné zameniť za konštantu.
Tento zápis sa používa v triedach. Na deklaráciu konštánt slúži define.

Lepším spôsobom teda je:
Najlepším spôsobom je overiť či boli zadané všetky POST ktoré očakávame. A následne overovať ich hodnoty, väčsinou nestačí overiť len či to bolo zadané, ale aj či to má dostatočnú dĺžku, či sú to len čísla atď.... A to s tymi referenciami bol teda riadny extrém, dúfam že to nikde nepoužívaš a ani vlastne netuším ako to môže fungovať. Mám skôr pocit že to bude neočakávané chovanie.
if (isset($_POST['name']) && isset($_POST['email']) ... ) { ... }

je na môj vkus tiež dosť neprehľadný
V 99% prípadov sa aj na samotný text použije premenná a na hlavičky tiež, hádam nikdy sa neposiela len jedna a tým pádom mi
if (mail($recipient, $subject, $message, $headers)) { ... }
nepríde neprehladné.
Tomáš123
Profil
Problém bol dosť jednoduchý. Konkrétne spočíval v inom pomenovaní prvku v poli $_SESSION pri ukladaní a pri kontrolovaní. Pozrite si kód v príspevku číslo [#24]. Chyba je zanesená už tam.

mimochodec:
To máš za domácí úkol.
Je domáca úloha zvládnutá dobre, alebo by si to spravil inak?
lionel messi
Profil
Tomáš123:
Je domáca úloha zvládnutá dobre, alebo by si to spravil inak?
Odpoviem namiesto mimochodca: Podľa mňa je to ok.

Pozrite si kód v príspevku číslo [#24]. Chyba je zanesená už tam.
Máš pravdu (počiarkovník vs. spojovník), všimol som si to až teraz.

Vaše odpověď

Mohlo by se hodit

Odkud se sem odkazuje


Prosím používejte diakritiku a interpunkci.

Ochrana proti spamu. Napište prosím číslo dvě-sta čtyřicet-sedm: