$_POST Unset?
Ik heb een formulier, dat roept een php aan. Werkt goed. Maar nu wil ik vermijden dat een gebruiker deze laatste pagina refreshed, F5 of Ctrl F5. Ik heb gelezen dat je met unset $_POST iets kunt doen. Nou dat werkt niet.
Ik check if($_SERVER['REQUEST_METHOD']=="POST") {
Wat is jullie ervaring om dit op te lossen? Want elke refresh voegt de gegevens toe aan de database. Dus vervelend. Is dat op de lossen?
Gewijzigd op 19/03/2018 10:43:36 door Jan te Pas
Je zou naar een beveiliging kunnen kijken met CSRF, waarbij je steeds een nieuwe hash genereert in je sessie, en deze controleert in een hidden POST value.
Wat veel wordt toegepast is Post/Redirect/Get. Kortom, na het posten en valideren van gegevens redirect je naar een nieuwe pagina, desnoods met een flash message in je session ter bevestiging. Dat voorkomt dat je na refreshes meer gegevens in je database krijgt.
Flash message?
Zou ik ook een $_SESSION['nieuw'] kunnen gebruiken? In de pagina waar ik de php aanroep, $_SESSION['nieuw']= true zetten vervolgens na de eerste check in de POST PHP deze $_SESSION['nieuw']=false zetten. Dan voorin checken of if ($_SESSION['nieuw']) melding en terug naar formulier of ander pagina. Kan dat?
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
class Csrf {
public static function makeToken() {
$max_time = 60 * 60 * 24;
$csrf_token = $_SESSION['csrf_token'];
$stored_time = $_SESSION['csrf_token_time'];
if ( $max_time + $stored_time <= time() || empty( $csrf_token ) ) {
$_SESSION['csrf_token'] = md5( uniqid( rand(), true ) );
$_SESSION['csrf_token_time'] = time();
}
return $_SESSION['csrf_token'];
}
public static function isTokenValid( $field ) {
return $field === $_SESSION['csrf_token'];
}
}
?>
class Csrf {
public static function makeToken() {
$max_time = 60 * 60 * 24;
$csrf_token = $_SESSION['csrf_token'];
$stored_time = $_SESSION['csrf_token_time'];
if ( $max_time + $stored_time <= time() || empty( $csrf_token ) ) {
$_SESSION['csrf_token'] = md5( uniqid( rand(), true ) );
$_SESSION['csrf_token_time'] = time();
}
return $_SESSION['csrf_token'];
}
public static function isTokenValid( $field ) {
return $field === $_SESSION['csrf_token'];
}
}
?>
Code (php)
Wat ik een hash noemde, heet hier een token.
Gewijzigd op 19/03/2018 12:09:35 door - Ariën -
en in php staat:
Code (php)
Toevoeging op 19/03/2018 12:12:12:
en als je dan op F5 drukt, wordt de laatste (GET) request herhaald en kom je dus gewoon steeds op "zijn we weer" uit.
Eigenlijk is CSRF er meer voor bedoeld dat je niet een actie kan uitvoeren via een aanval, van bijv. een externe site.
Gewijzigd op 19/03/2018 12:17:55 door - Ariën -
Als je op back drukt is de post niet beschikbaar, omdat je vanuit de POST een redirect hebt gedaan. De pagina bestaat voor de historie niet.
Oef... hier moet ik eens dieper induiken. Kennelijk toch niet zo gemakkelijk.
Als je niet wilt iemand bijv. URL's op je site aanroept via scriptjes, dan is CSRF de way to go.
Dat POST-Redirect-GET ga ik proberen. Dank
Post/Redirect/Get voorkomt ook het dubbelposten-door-terug-te-navigeren omdat je meteen na de verwerking van het formulier redirect. Navigeer je in zo'n opzet terug na versturen van het formulier kom je weer uit bij het formulier.
Neemt niet weg dat extra voorzieningen nodig kunnen zijn, zoals een CSRF-token (een token om cross site request forgeries, oftewel het posten van informatie vanaf een andere website, te voorkomen). Als het formulier al afgeschermd is door een login is dit trouwens niet per se nodig, maar het kan handig zijn om je formulieren standaard uit te rusten met deze functionaliteit.
Ook kan het handig zijn om een soort van validatie-routines te schrijven die alles controleren voordat je het formulier daadwerkelijk verwerkt. En het zou helemaal mooi zijn als je dan het formulier opnieuw inlaadt met de reeds ingevulde informatie en aangeeft wat hier aan mankeert.
Zorg ook dat alle verschillende acties apart worden gecompartimenteerd, ofwel:
- het tonen van het formulier
- het verwerken van het formulier
- het tonen van enige succesboodschap (of een flash message op een overzichtspagina kan ook afdoende zijn)
Dit om te voorkomen dat je code één grote if-elseif-else-brei wordt.
Manieren om deze acties te splitsen zijn:
- stop ze alle in aparte bestanden (maar dit kan bewerkelijk zijn)
- stop ze alle in aparte methoden van een klasse
- andere? (ik zou niet teruggaan naar normale functies maar proberen om in de OOP-richting te sturen)
Doordat je de acties scheidt is het wel nodig om op een of andere manier informatie over te hevelen tussen "pagina's". Als je dus iemand terugstuurt naar de toon-het-formulier-actie met reeds eerder ingevulde informatie zul je deze data op een of andere manier moeten transporteren, bijvoorbeeld via de sessie.
NB een (session) flash message is een boodschap die eenmalig op het scherm getoond wordt en daarna meteen wordt gewist uit de sessie. Dit zou je bijvoorbeeld in je maintemplate kunnen opnemen, ik maar hier zelf in het backend ook gebruik van. Op die manier kun je met minimale middelen overbrengen dat een actie (die geen echte output heeft zoals het toevoegen, wijzigen of verwijderen van een item) is geslaagd. Zo zit deze snippet in mijn admin maintemplate:
Ik geef ook de berichten een kleurtje die direct iets vertellen over de status van het bericht (groen voor geslaagd, rood voor fout, oranje voor mogelijk actie nodig).
NB: dit is een van de weinige plekken waar je afwijkt van de regel "filter input escape output" omdat de output in een vorige actie is gegenereerd en de volgende actie niet echt kan inschatten wat er moet gebeuren, daarom is het handiger dat de vorige actie ook gelijk de boodschap formatteert in een formaat dat veilig is voor output.
Dat is inderdaad wat je zou moeten doen. Neemt niet weg dat extra voorzieningen nodig kunnen zijn, zoals een CSRF-token (een token om cross site request forgeries, oftewel het posten van informatie vanaf een andere website, te voorkomen). Als het formulier al afgeschermd is door een login is dit trouwens niet per se nodig, maar het kan handig zijn om je formulieren standaard uit te rusten met deze functionaliteit.
Ook kan het handig zijn om een soort van validatie-routines te schrijven die alles controleren voordat je het formulier daadwerkelijk verwerkt. En het zou helemaal mooi zijn als je dan het formulier opnieuw inlaadt met de reeds ingevulde informatie en aangeeft wat hier aan mankeert.
Zorg ook dat alle verschillende acties apart worden gecompartimenteerd, ofwel:
- het tonen van het formulier
- het verwerken van het formulier
- het tonen van enige succesboodschap (of een flash message op een overzichtspagina kan ook afdoende zijn)
Dit om te voorkomen dat je code één grote if-elseif-else-brei wordt.
Manieren om deze acties te splitsen zijn:
- stop ze alle in aparte bestanden (maar dit kan bewerkelijk zijn)
- stop ze alle in aparte methoden van een klasse
- andere? (ik zou niet teruggaan naar normale functies maar proberen om in de OOP-richting te sturen)
Doordat je de acties scheidt is het wel nodig om op een of andere manier informatie over te hevelen tussen "pagina's". Als je dus iemand terugstuurt naar de toon-het-formulier-actie met reeds eerder ingevulde informatie zul je deze data op een of andere manier moeten transporteren, bijvoorbeeld via de sessie.
NB een (session) flash message is een boodschap die eenmalig op het scherm getoond wordt en daarna meteen wordt gewist uit de sessie. Dit zou je bijvoorbeeld in je maintemplate kunnen opnemen, ik maar hier zelf in het backend ook gebruik van. Op die manier kun je met minimale middelen overbrengen dat een actie (die geen echte output heeft zoals het toevoegen, wijzigen of verwijderen van een item) is geslaagd. Zo zit deze snippet in mijn admin maintemplate:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
if (empty($_SESSION['messages']) === false) {
?><div id="messages"><?php
foreach ($_SESSION['messages'] as $index => $message) {
?><div class="<?php echo $this->escape($message['type']) ?>"><?php
// It is the programmers responsibility to escape these messages on input as they may contain HTML.
echo $message['message'];
?></div><?php
}
$_SESSION['messages'] = array();
// @todo think of a way to make notifications more persistent
?></div><?php
}
?>
if (empty($_SESSION['messages']) === false) {
?><div id="messages"><?php
foreach ($_SESSION['messages'] as $index => $message) {
?><div class="<?php echo $this->escape($message['type']) ?>"><?php
// It is the programmers responsibility to escape these messages on input as they may contain HTML.
echo $message['message'];
?></div><?php
}
$_SESSION['messages'] = array();
// @todo think of a way to make notifications more persistent
?></div><?php
}
?>
Ik geef ook de berichten een kleurtje die direct iets vertellen over de status van het bericht (groen voor geslaagd, rood voor fout, oranje voor mogelijk actie nodig).
NB: dit is een van de weinige plekken waar je afwijkt van de regel "filter input escape output" omdat de output in een vorige actie is gegenereerd en de volgende actie niet echt kan inschatten wat er moet gebeuren, daarom is het handiger dat de vorige actie ook gelijk de boodschap formatteert in een formaat dat veilig is voor output.
Gewijzigd op 19/03/2018 16:28:22 door Thomas van den Heuvel
@Thomas: Dank ik heb de input met check in een formulier. Pas als alles goed staat, kan gesubmit worden. Maar wel graag 1x. Dus ik ga jouw opzet gebruiken. Dank!