Wer endlich der Weiterleitungs-Wut bei abgesendeten Formularen entkommen will:
(ich hab nie verstanden, warum das so Weiterleiten weit verbreitet ist)
PHP
<?php
session_start();
#session_destroy();
function html_($str){
return htmlspecialchars($str);
}
class ErrorHandler
{
# your e-handler
public static function stop($str)
{
die($str);
}
}
class FormGuard
{
const SESSIONKEY_MAIN = 'formguard';
const SESSIONKEY_UNIQUE_STR = 'uniquestr';
const SESSIONKEY_UNIQUE_FORMID_EXPECTED = 'uniquefidexp';
const SESSIONKEY_UNIQUE_FORMID_NEXT = 'uniquefidnex';
const FORM_HIDDENFIELD_NAME = 'formid';
private static
$uniqueformid,
$uniquestring,
$skips,
$initialized;
public static function init()
{
if(!isset($_SESSION)){
ErrorHandler::stop('$_SESSION not started! @'.__METHOD__.'()');
}
if(self::$initialized){
return;
}
# sort session:
if(self::getSession(self::SESSIONKEY_UNIQUE_FORMID_NEXT)){
self::setSession(
self::SESSIONKEY_UNIQUE_FORMID_EXPECTED,
self::getSession(self::SESSIONKEY_UNIQUE_FORMID_NEXT)
);
self::setSession(self::SESSIONKEY_UNIQUE_FORMID_NEXT,null);
}
# create unique form id:
self::$uniqueformid = self::oneWayEncode(microtime(true));
self::$initialized = true;
return;
}
public static function getHiddenField()
{
self::init();
self::setSession(self::SESSIONKEY_UNIQUE_FORMID_NEXT,self::$uniqueformid);
return '<input
type="hidden"
name="'.html_(self::FORM_HIDDENFIELD_NAME).'"
value="'.html_(self::$uniqueformid).'"
/>';
}
public static function justSubmitted()
{
self::init();
self::setSession(self::SESSIONKEY_UNIQUE_STR,self::getUniqueString());
return;
}
public static function check()
{
self::init();
if(self::getSession(self::SESSIONKEY_UNIQUE_STR)or !self::is_expectedFormId()){
if(self::getUniqueString() == self::getSession(self::SESSIONKEY_UNIQUE_STR)or !self::is_expectedFormId()){
self::unsetByRef($_GET);
self::unsetByRef($_POST);
return false;
}
}
return true;
}
public static function addSkips($arr)
{
self::init();
$arr = (!is_array($arr))
?array($arr)
:$arr;
foreach($arr as $skip){
self::$skips[] = $skip;
}
return;
}
public static function delSkips($arr)
{
self::init();
$arr = (!is_array($arr))
?array($arr)
:$arr;
foreach($arr as $del){
foreach(self::$skips as $key => $val){
if($del == $val){
unset(self::$skips[$key]);
}
}
}
return;
}
private static function unsetByRef(&$that)
{
if(isset($that)and is_array($that)){
foreach($that as $key => $val){
if(empty(self::$skips)or !in_array($key,self::$skips)){
unset($that[$key]);
}
}
}
return;
}
private static function setSession($key,$val)
{
$_SESSION[self::SESSIONKEY_MAIN][$key] = $val;
return;
}
private static function getSession($key)
{
return (isset($_SESSION[self::SESSIONKEY_MAIN][$key]))
?$_SESSION[self::SESSIONKEY_MAIN][$key]
:false;
}
private static function getUniqueString()
{
if(!isset(self::$uniquestring)or empty(self::$uniquestring)){
self::createUniqueString();
}
return self::$uniquestring;
}
private static function createUniqueString()
{
self::$uniquestring = (!isset(self::$uniquestring)or empty(self::$uniquestring))
?self::oneWayEncode(json_encode($_GET).json_encode($_POST))
:self::$uniquestring;
return;
}
private static function is_expectedFormId()
{
$expected_formid = (isset($_POST[self::FORM_HIDDENFIELD_NAME]))
?$_POST[self::FORM_HIDDENFIELD_NAME]
:false;
return (
$expected_formid
and (
!self::getSession(self::SESSIONKEY_UNIQUE_FORMID_EXPECTED)
or self::getSession(self::SESSIONKEY_UNIQUE_FORMID_EXPECTED) != $expected_formid
)
)
?false
:true;
}
private static function oneWayEncode($str)
{
return sha1($str);
}
}
###
# add formguard skips: this GET/POST keys wont be unset
#
FormGuard::addSkips(
array('textfield1')
);
###
# let the formguard check the incomming GET/POST (always before you execute any requeste action!)
#
if(!FormGuard::check()){
$msg = 'Hello, I am the FormGuard. You just sent a now invalid form. '.
'I have allowed myself to ignore this one. (Possibly the session just has been expired)';
}
###
# somewhere in the script you wanna check if the form was submitted
# if so, and you executed the requested action,
# use FormGuard::justSubmitted(); to tell the formguard that this form is "done"
#
if(isset($_POST['sub'])){ # submit was pushed
FormGuard::justSubmitted(); # hey formguard - dont let this form come in again =)
$executed = true;
}
?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf8" />
<title>FormGuard Test</title>
<!-- cottton @i-stats.net [TS3: i-stats.net] -->
</head>
<body>
<h1>FormGuard Test</h1>
<p>Submit that form and then try to submit it agani by using F5 =)</p>
<?php echo (isset($msg))?$msg:''; ?>
<form name="form" action="" method="POST" enctype="application/x-www-form-urlencoded">
<?php echo FormGuard::getHiddenField(); ?>
<input type="text" name="textfield1" value="" placeholder="textfield1" />
<input type="text" name="textfield2" value="" placeholder="textfield2" />
<input type="text" name="textfield3" value="" placeholder="textfield3" />
<input type="submit" name="sub" value="submit" />
</form>
<?php echo (isset($executed))?'Form executed':''; ?>
<hr>
<h2>Debug:</h2>
<b>$_POST:</b>
<pre><?php print_r($_POST); ?></pre>
<b>$_GET:</b>
<pre><?php print_r($_GET); ?></pre>
<b>$_SESSION:</b>
<pre><?php print_r($_SESSION); ?></pre>
</body>
</html>
Alles anzeigen
Was passiert hier?
Steht in den comments.
Kurz: es ist (bei richtiger Anwendung) nicht möglich per F5 eine Form mehr als einmal anzusenden.
Keine "noobigen" Weiterleitungen mehr.
Ist immernoch ausbaufähig