i’ve copied some of my code for posting here. i really can’t see how my form is vulnerable to any injections or xss. i’ve tried entering commands and the process.php sets the error *which displays nothing on the screen.) my error handling is not implemented. i’m not sure how to proceed with errors: use process php error handler to redisplay a form with erros? then send back to process.php? anyway, my form.php and process.php is still being developed. i am far from finished here. however, i can only see session management as an issue. you cannot enter data into my form that isn’t accepted data. i’ve tried it. the session management needs to be implemented correctly but for a novice, i think that my subscription is very good.
i keep all of my php files in their own directory so file.php is never visible. apache loads index.php
using xampp: http://localhost/subscribe/ is all that you see. likewise, http://localhost/subscribe/process/
here is an edited version of my html in form index.php (subscribe/)
<?php
header("Cache-Control: no-cache, no-store, must-revalidate");
header("Pragma: no-cache");
header("Expires: Sat, 26 Jul 1997 05:00:00 GMT");
header("Content-Type: text/html; charset=utf-8");
// my apache httpd.config file also has headers in the root directory:
// Header set X-Frame-Options DENY
// Header set X-XSS-Protection "1; mode=block"
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang="de-DE" dir="ltr">
<head>
<title>Subscription form</title>
<meta http-equiv="x-ua-compatible" content="IE=edge" /><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><meta name="format-detection" content="telephone-no" />
<meta http-equiv="Content-Security-Policy" content="script-src 'self'; worker-src 'none'; connect-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self'; media-src 'self'; font-src 'none'; plugin-types 'none'; frame-src 'none'; child-src 'none'; object-src 'none'" />
<meta name="no-email-collection" content="impressum" /><meta name="distribution" content="IU" /><meta http-equiv="window-target" content="_top" />
<link rel="Stylesheet" type="text/css" href="main.css" />
</head>
<body>
<!-- my script actually calls a directory to load index.php: action="process/" which loads index.php (here named process.php) -->
<div class="Form" align="center"><form autocomplete="off" accept-charset="UTF-8" action="process.php" method="post">
<input type="hidden" name="token" value="<?php echo hash_hmac('sha512', $sessionKey, $formToken); ?>" /> <!-- stored in $_SESSION['secToken'] for matching -->
<div><input autocomplete="off" name="gender" id="lady" type="radio" value="Frau" required><label class="FormLabel" for="lady">Frau</label><input class="FormRadio" autocomplete="off" name="gender" id="man" type="radio" value="Herr"><label class="FormLabel" for="man">Herr</label><br /></div>
<div><input autocomplete="off" name="title" id="dr" type="radio" value="Dr."><label for="dr">Dr.</label><input class="FormRadio" autocomplete="off" name="title" id="prof" type="radio" value="Prof."><label for="prof">Prof.</label><input autocomplete="off" name="title" id="profdr" type="radio" value="Prof. Dr."><label class="FormLabel" for="profdr">Prof. Dr.</label></div>
<div>
<label class="FormLabel">* Vorname</label><input autocomplete="off" type="text" name="firstName" placeholder="2 bis 27 buchstabe" required minlength="2" maxlength="27" /><br />
<label class="FormLabel">* Nachname</label><input autocomplete="off" type="text" name="lastName" placeholder="2 bis 27 buchstabe" required minlength="2" maxlength="27" />
</div>
<div>
<label class="FormLabel">* Tag</label><input autocomplete="off" name="dobday" id="dobyear" type="text" placeholder="TT wie 01 oder 23" minlength="2" maxlength="2" title="nn" pattern="[0-9]{2}" required><br />
<label class="FormLabel">* Monat</label><input autocomplete="off" name="dobmonth" id="dobyear" type="text" placeholder="MM wie 01 oder 12" required minlength="2" maxlength="2" title="nn" pattern="[0-9]{2}"><br />
<label class="FormLabel">* Jahr</label><input autocomplete="off" name="dobyear" id="dobyear" type="text" placeholder="JJJJ wie 1981 oder 2015" required minlength="4" maxlength="4" title="nnnn" pattern="[0-9]{4}">
</div>
<div><input value="Submit" name="submit" type="submit" /></div>
</form></div>
</div>
</div></body>
</html>
<?php
exit; //exit the php script for security.
?>
then my process/ index.php file
<?php // process subscription form data
if ($_SERVER["REQUEST_METHOD"] == "POST") {
//paranoid and don't know how to handle requests that may get by the if conditional
$_GET = array(); $_SERVER["QUERY_STRING"] = NULL;
// be sure that the ini file is in my favor. still need to implement a conditional to exit if not set
ini_set('session.use_strict_mode', 1);
ini_set('session.use_cookies', 1);
ini_set('session.use_only_cookies', 1);
ini_set('session.use_trans_sid', 0);
ini_set('session.cookie_lifetime', 0);
ini_set('session.cookie_httponly', TRUE);
ini_set('session.gc_maxlifetime', 1800);
session_cache_limiter('nocache');
session_cache_expire(3);
session_start();
session_regenerate_id(true); // elevation, so regenerate
// still need to implement an isset check for the submit button. maybe not?
// unset the token to enforce a one-at-a-time gateway
if (hash_equals($_SESSION['secToken'], $_POST['token'])) { unset($_SESSION['secToken']); unset($_POST['token']);
// still need to handle cookie comparisons and timestamps
if ($_SERVER['REMOTE_ADDR'] == $_SESSION['userIP'] && $_SERVER['HTTP_USER_AGENT'] == $_SESSION['userAgent']) {
// maybe variables are breadcrumbs? avoid variables in form processing? can they be accessed by hackers?
$birthDay = $birthMonth = $birthYear = $gbt = NULL;
$fnErr = $lnErr = $birthDayErr = $birthMonthErr = $birthYearErr = FALSE;
$check100 = date(Y) - 100; $check18 = date(Y) - 18;
// analyze, sanitize and validate gender and titles. exit if not what is set because they are tampered data
if (!( ($_POST['gender'] == 'Frau') || ($_POST['gender'] == 'Herr') )) { echo 'error'; $_POST = array(); $_SESSION = array(); session_destroy(); exit; } else { sanStrings($_POST['gender']); }
if (isset($_POST['title'])) {
if (!( ($_POST['title'] == 'Dr.') || ($_POST['title'] == 'Prof.') || ($_POST['title'] == 'Prof. Dr.') )) { echo 'error'; $_POST = array(); $_SESSION = array(); session_destroy(); exit; } else { sanStrings($_POST['title']); }
}
// analyze, sanitize and validate first and last names. astonecipher recommends a function. thus, credit goes to astonecipher when function is implemented
// to astonecipher: i will place a credit at the bottom of the form page. Thank You for helping a novice.
if (isset($_POST['firstName']) && !preg_match('/^[a-zA-Z]{2,27}$/', $_POST['firstName'])) { $fnErr = TRUE; } else { sanStrings($_POST['firstName']); }
if (isset($_POST['lastName']) && !preg_match('/^[a-zA-Z]{2,27}$/', $_POST['lastName'])) { $lnErr = TRUE; } else { sanStrings($_POST['lastName']); }
// analyze, sanitize and validate date of birth
if (isset($_POST['dobday']) && !preg_match('/^[0-9]{2}$/', $_POST['dobday'])) { $birthDayErr = TRUE; } else { $birthDay = sanInteger($_POST['dobday']); }
if (isset($_POST['dobmonth']) && !preg_match('/^[0-9]{2}$/', $_POST['dobmonth'])) { $birthMonthErr = TRUE; } else { $birthMonth = sanInteger($_POST['dobmonth']); }
if (isset($_POST['dobyear']) && !preg_match('/^[0-9]{4}$/', $_POST['dobyear'])) { $birthYearErr = TRUE; } else { $birthYear = sanInteger($_POST['dobyear']); }
if ($birthYear < $check18 && $birthYear >= $check100 && checkdate($birthMonth, $birthDay, $birthYear)) { $gbt = $birthDay . '.' . $birthMonth . '.' . $birthYear; } else { $birthYearErr = TRUE; }
if ($birthYear == $check18) {
if ($birthMonth == date(m) && $birthDay <= date(d) && checkdate($birthMonth, $birthDay, $birthYear)) { $gbt = $birthDay . '.' . $birthMonth . '.' . $birthYear; } else { $birthYearErr = TRUE; }
if ($birthMonth < date(m) && checkdate($birthMonth, $birthDay, $birthYear)) { $gbt = $birthDay . '.' . $birthMonth . '.' . $birthYear; } else { $birthYearErr = TRUE; }
}
echo '<big><b>form submission successful!</b></big><br>';
echo '<big><b>' . $_POST['gender'] . ' ' . $_POST['title'] . ' ' . $_POST['firstName'] . ' ' . $_POST['lastName'] . '</b></big><br>';
echo '<big><b>' . $gbt . '</b></big><br>';
// not sure but clearing data should prevent a hijacker from following user here and seeing this info. yes? no?
$_POST = array();
$_SESSION = array();
session_destroy();
} else {
echo 'Error authenticating session user.'; //token is not a match or a bypass has been thwarted.
$_POST = array();
$_SESSION = array();
session_destroy();
exit;
}
} else { //stop refresh from resubmitting data
$_SESSION = array();
//$params = session_get_cookie_params(); sec experts say to delete the cookies. here is example code for later
//setcookie(session_name(), '', time() - 4200, $params["domain"], $params["path"], $params["httponly"], $params["secure"]);
session_destroy();
echo '<big><b>the form has been submitted or the session has expired.</b></big>';
exit;
}
} else { // use invalid request error page from root page that calls process.php or simply redirect to the agreement page. so exit here.
exit;
}
// begin functions =================================================================== should be at the top of the page?
function sanStrings($s) { //sanitize string also FILTER_SANITIZE_EMAIL
$s = trim($s);
$s = stripslashes($s);
$s = filter_var($s, FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_BACKTICK, FILTER_FLAG_ENCODE_AMP, ENT_QUOTES);
$s = htmlspecialchars($s);
return $s;
}
function sanInteger($n) { //sanitize number
$n = trim($n);
$n = stripslashes($n);
$n = filter_var($n, FILTER_SANITIZE_NUMBER_INT);
$n = htmlspecialchars($n);
return $n;
}
?>
// paranoid so added an extra exit to avoid a running automobile script waiting for a thief
<?php exit; ?>
you should be able to test it by setting a token and session in the subscribe/ index.php
actually, my files are located outside of the root. subscribe/ has an index.php which calls the actual script.
but starting a session and setting a sha512 token will allow the scripts to be tested.