Reeeallly hoping someone can help me out here, as this has been plaguing me for a while and no amount of googling has helped. I am a website developer hobbyest who's trying to create a contest entry app for a non-profit org.
When I load a page in my web application, I see 2 sessions created in my custom web app folder (I am only expecting one):
This is, of course, problematic as I'm setting session values to be used by downstream PHP code and depending on which one is grabbed my code fails on checking for my CSRF token.
Here's what's happening (Chrome browser):
- Force-reload page to update cache, as I'm working on JS/CSS changes; I do not submit the form or navigate anywhere.
- My log files show two different session_ids: session_id:ccbbhescjqevk02id43nf6tmgc and session_id:vf7fgnu92up1uim9nlup1h8nlu (the latter is only created at the very end of the page load)
- I'm adding a CSRF token to my form which gets submitted via POST and I check that in my downstream PHP file, but sometimes this fails as it's not in the session it's using.
Here's my page:
<?php
// include setConfig
require_once '../../../private/contest/include/setConfig.php';
// include startSession
require_once '../../../private/contest/include/startSession.php';
if (session_status() == PHP_SESSION_NONE) {
startWFsession('entry_form');
}
$_SESSION['current_time'] = time();
// Blank out assoc array of new entry details that are used by printout.php
unset($_SESSION['newEntryRec']);
$pageName = 'entryForm';
$pageVer = '1.0.0';
$ip = getUserIP();
$sessionID = session_id();
// Generate form token for CSRF protection
if (!isset($_SESSION['token'])) {
$_SESSION['token'] = generateCSRFtoken($sessionID, $ip);
}
$token = $_SESSION['token'];
if (DEV_MODE) {
$log->debug('CSRF token for this session:',
array('file:' => basename(__FILE__),
'line#:' => __LINE__,
'session_id' => $sessionID,
'IP' => $ip,
'$_SESSION["token"]' => bin2hex($_SESSION['token'])));
}
?>
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta name="robots" content="noindex,nofollow" />
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1">
.
.
.
<title>Testing 123</title>
<noscript>
<!-- Hide page contents if JS is disabled -->
<style>
.pagecontainer {display:none;}
</style>
</noscript>
<script src="js/entrylogic_v1.0.0.js"></script>
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
</head>
<body>
<div>
<?php include('../common/html/navbar.htm');?>
</div>
<noscript>
<div class="noscriptmsg">
<h4><span class="red">JavaScript</span> is disabled on your browser. You will not be able to use this application until it is enabled.</h4>
<h5>Please refer to your browser's specific configuration details to change this setting.</h5>
</div>
</noscript>
<div class="pagecontainer">
<div id="outline">
<div class="container">
<?php
// Determine if this year's contest is open
// ========================================
$current_time = new DateTime('now');
$chkContest = isContestOpen($current_time, $sessionID, $ip);
$log->debug('Function isContestOpen check ',
array('file:' => basename(__FILE__),
'line#:' => __LINE__,
'session_id' => $sessionID,
'IP' => $ip,
'chkContest' => $chkContest));
if ($chkContest != 'open' || is_null($chkContest)) {
exit($chkContest);
}
// Got this far, so contest is open
?>
<div class="form-row">
<!--<div class="center">-->
<form id="entryForm" name="entryForm" class="form-horizontal" enctype="multipart/form-data">
<!-- CSRF protection -->
<input type="hidden" name="token" value="<?php echo $token; ?>" />
<div class="entryFields">
<div>
<fieldset class="scheduler-border">
<legend class="scheduler-border">Entrant Info</legend>
<div class="form-group">
<div class="col-sm-4">
<label for="first-name" class="sr-only">First name</label>
<input id="first-name" name="fName" class="form-control" type="text" placeholder="First name [required]" title="Requried. Max <?php echo FORM_NAME_FLD_LENGTH; ?> characters"
maxlength="<?php echo FORM_NAME_FLD_LENGTH; ?>" data-wf-fld-len="<?php echo FORM_NAME_FLD_LENGTH; ?>" required autofocus autocomplete="given-name">
</div>
<div class="col-sm-8">
<label for="last-name" class="sr-only">Last name</label>
<input id="last-name" name="lName" class="form-control" type="text" placeholder="Last name [required]" title="Requried. Max <?php echo FORM_NAME_FLD_LENGTH; ?> characters"
maxlength="<?php echo FORM_NAME_FLD_LENGTH; ?>" data-wf-fld-len="<?php echo FORM_NAME_FLD_LENGTH; ?>" required autocomplete="family-name">
</div>
</div>
<div class="form-group">
<div class="col-sm-12">
<label for="email" class="sr-only">Email</label>
<input id="email" name="email" class="form-control" type="email" placeholder="Email [required]"
title="Required. Max <?php echo FORM_EMAIL_FLD_LENGTH; ?> characters (e.g. bela@transylvania.com)" maxlength="<?php echo FORM_EMAIL_FLD_LENGTH; ?>"
data-wf-fld-len="<?php echo FORM_EMAIL_FLD_LENGTH; ?>" required autocomplete="home email">
</div>
</div>
</fieldset>
</div>
<div>
<fieldset class="scheduler-border">
<legend class="scheduler-border">Model Info</legend>
<div class="form-group">
<div class="col-sm-12">
<label for="model_name" class="sr-only">Model name</label>
<textarea rows="2" id="model_name" name="modelName" class="form-control textarea-group-lg" placeholder="Title or name of entry [required]"
title="Required. Max <?php echo FORM_MODELNAME_FLD_LENGTH; ?> characters" maxlength="<?php echo FORM_MODELNAME_FLD_LENGTH; ?>"
data-wf-fld-len="<?php echo FORM_MODELNAME_FLD_LENGTH; ?>" required></textarea>
</div>
</div>
<div class="center">
<div class="form-group"><!--category -->
<div class="col-sm-12">
<label for="category" class="sr-only">Category</label>
<!-- https://developer.snapappointments.com/bootstrap-select/options/#default-settings -->
<select id="category" name="category" class="selectpicker show-tick" data-show-subtext="true" data-size="10" data-live-search="true" data-width="auto"
title="Category [required]" data-header="Select the most appropriate category:" size="1" required>
<?php
$log->debug('About to call getCategories() ...',
array('file:' => basename(__FILE__),
'line#:' => __LINE__,
'session_id' => $sessionID,
'IP' => $ip));
// Call function to get categories for current year
$result = getCategories(date('Y'), $sessionID, $ip);
if (!empty($result)){?>
<option disabled selected value>Category [required]</option>
<?php
for ($i = 0; $i < count($result); $i++){
//echo nl2br ($result[$i]["Category"] . "\n");?>
<option value="<?php echo $result[$i]['ID']; ?>" data-subtext="<?php echo htmlspecialchars($result[$i]['Description']); ?>"><?php echo htmlspecialchars($result[$i]['Category']); ?></option><?php
}
} else {?>
<option disabled selected value>No categories exist for current year</option><?php
}
?>
</select>
</div>
</div><!--end of category -->
<?php
$log->debug('After call getCategories() ...',
array('file:' => basename(__FILE__),
'line#:' => __LINE__,
'session_id' => $sessionID,
'IP' => $ip));
?>
</div>
<div class="form-group">
<div class="col-sm-12">
<label for="exampleFormControlFile1">Select up to four (4) model photos (PNG or JPG only):</label>
<input type="file" class="form-control-file" id="modelPhoto1" name="modelPhoto1" accept=".jpg, .png, .jpeg|image/*" required>
<input type="file" class="form-control-file" id="modelPhoto2" name="modelPhoto2" accept=".jpg, .png, .jpeg|image/*">
<input type="file" class="form-control-file" id="modelPhoto3" name="modelPhoto3" accept=".jpg, .png, .jpeg|image/*">
<input type="file" class="form-control-file" id="modelPhoto4" name="modelPhoto4" accept=".jpg, .png, .jpeg|image/*">
</div>
</div>
</fieldset>
</div>
<div>
<fieldset class="scheduler-border">
<legend class="scheduler-border">Terms and Conditions</legend>
<p style="text-align:left;font-size:x-small;margin:0px 0px 0px 0px">I grant WANT-A-FEST permission to use my model photos in their marketing materials without limitation or restriction.</p>
<p style="margin-top:4px;margin-bottom:4px;font-size:small"><input id="acceptAgrmt" type="checkbox" name="acceptAgrmt" value="" required> I accept the Terms and Conditions</p>
</fieldset>
</div>
<div id='recaptcha' class="g-recaptcha" data-sitekey="<?php echo RECAPTCHA_SITE_KEY; ?>" data-callback="onSubmit" data-size="invisible"></div>
<div class="center">
<div><!--form buttons-->
<input type="submit" id="formsubmit" name="mysubmit" class="btn btn-danger" value="Submit">
<input type="reset" id="formreset" class="btn btn-warning" value="Clear Form">
</div><!--end of form buttons-->
<div class="center" style="margin-top: 10px;font-size: x-small">
This site is protected by reCAPTCHA and the Google
<a href="https://policies.google.com/privacy">Privacy Policy</a> and
<a href="https://policies.google.com/terms">Terms of Service</a> apply.
</div>
</div>
</div>
</form>
<!--</div>-->
</div>
</div><!--container-->
</div><!--outline-->
</div><!--pagecontainer-->
<div class="modalLoading">
<?php
$log->debug('Before loading modalLoading.htm ...',
array('file:' => basename(__FILE__),
'line#:' => __LINE__,
'session_id' => $sessionID,
'IP' => $ip));
?>
<?php include('../common/html/modalLoading.htm');?>
<?php
$log->debug('After loading modalLoading.htm ...',
array('file:' => basename(__FILE__),
'line#:' => __LINE__,
'session_id' => $sessionID,
'IP' => $ip));
?>
</div>
<!-- SHOPPING CART START -->
<?php
$log->debug('Before loading shopping cart stuff ...',
array('file:' => basename(__FILE__),
'line#:' => __LINE__,
'session_id' => $sessionID,
'IP' => $ip));
?>
<?php include('../common/html/jquery_dlg_clear_cart.htm');?>
<?php include('../common/html/jquery_dlg_delete_entry.htm');?>
<?php
$log->debug('After loading shopping cart stuff ...',
array('file:' => basename(__FILE__),
'line#:' => __LINE__,
'session_id' => $sessionID,
'IP' => $ip));
?>
<!-- SHOPPING CART END -->
<div class="footer">
<?php
$log->debug('Before loading footer.htm ...',
array('file:' => basename(__FILE__),
'line#:' => __LINE__,
'session_id' => $sessionID,
'IP' => $ip));
?>
<?php include('../common/html/footer.htm');?>
<?php
$log->debug('After loading footer.htm ...',
array('file:' => basename(__FILE__),
'line#:' => __LINE__,
'session_id' => $sessionID,
'IP' => $ip));
?>
</div>
</body>
</html>
My custom session start function:
function startWFsession($sessionName) {
global $log, $entryFormCookieParams;
// From http://php.net/manual/en/function.session-set-cookie-params.php :
session_set_cookie_params(
$entryFormCookieParams["lifetime"],
$entryFormCookieParams["path"],
$entryFormCookieParams["domain"],
$entryFormCookieParams["secure"],
$entryFormCookieParams["httponly"]
);
// Set custom PHP session save path before every call to session_start() as per Dean Brook's (Iglou) suggestion
ini_set('session.save_path', PHP_SESSION_FLDR);
// Set session timeout to be the same as the cookie
ini_set('session.gc-maxlifetime', $entryFormCookieParams["lifetime"]);
// Set session srict mode on (rejects session IDs not initialized by the server)
ini_set('session.use_strict_mode', true);
if (!empty($sessionName)) {
session_name($sessionName);
}
session_start();
setcookie(session_name(),session_id(),time()+$entryFormCookieParams["lifetime"]);
$log->debug('Session started or resumed - check session_id',
array('file:' => basename(__FILE__),
'line#:' => __LINE__,
'session_id' => session_id(),
'session_name' => session_name(),
'Cookie lifetime' => $entryFormCookieParams["lifetime"],
'Cookie path' => $entryFormCookieParams["path"],
'Cookie domain' => $entryFormCookieParams["domain"],
'Cookie secure' => $entryFormCookieParams["secure"],
'Cookie httponly' => $entryFormCookieParams["httponly"],
'IP' => getUserIP()
));
return is_session_started() ? TRUE : FALSE;
}
My log file output:
[2020-06-09 19:13:12] WF.DEBUG: Reading config settings and setting file includes {"file:":"setConfig.php","line#:":48,"$_SERVER[\"DOCUMENT_ROOT\"]":"/Users/Ross/Dropbox/htdocs/wantafest_mamp_pro/public_html","DB_SERVERNAME":"localhost","DB_NAME":"wantafest_contest_online","DB_SERVER_PORT":3311,"DB_USERNAME":"root","EMAIL_HOST":"mail.wonderfest.com","EMAIL_SMTP_AUTH":true,"EMAIL_USERNAME":"x.com","EMAIL_SMTP_SECURE":"tls","EMAIL_SERVER_PORT":587,"EMAIL_SEND_LOGS_TO":"y.com","EMAIL_SEND":false,"LOG_FLDR":"/Users/Ross/Dropbox/htdocs/wantafest_mamp_pro/private/contest/logs/","CACHE_FLDR":"/Users/Ross/Dropbox/htdocs/wantafest_mamp_pro/private/contest/cached-files","CACHE_EXPIRE_SQL_SEC":59,"CACHE_EXPIRE_PRINTOUT_SEC":360,"PHOTOS_FLDR":"/Users/Ross/Dropbox/htdocs/wantafest_mamp_pro/private/contest/photos/","BO_EXPIRE_SESSION_AFTER_SEC":900,"$entryFormCookieParams[\"lifetime\"]":1800,"$entryFormCookieParams[\"path\"]":"/","$entryFormCookieParams[\"domain\"]":".localhost","$entryFormCookieParams[\"secure\"]":false,"$entryFormCookieParams[\"httponly\"]":true,"PHP_SESSION_FLDR":"/Users/Ross/Dropbox/htdocs/wantafest_mamp_pro/sessions"}
[2020-06-09 19:13:12] WF.DEBUG: Session started or resumed - check session_id {"file:":"startSession.php","line#:":41,"session_id":"**ccbbhescjqevk02id43nf6tmgc**","session_name":"entry_form","Cookie lifetime":1800,"Cookie path":"/","Cookie domain":".localhost","Cookie secure":false,"Cookie httponly":true,"IP":"::1"}
[2020-06-09 19:13:12] WF.DEBUG: PHP version is 5.4.0 or greater {"file:":"startSession.php","line#:":65,"IP":"::1","phpversion()":"7.4.2","session_status()":2}
[2020-06-09 19:13:12] WF.DEBUG: generateCSRFtoken {"file:":"commonfunctions.php","line#:":910,"session_id":"**ccbbhescjqevk02id43nf6tmgc**","IP":"::1","token":"64373566386535363733353835303938623364643931333838643636643030373736346332396136303432626634306266303366383433386361633530376465"}
[2020-06-09 19:13:12] WF.DEBUG: CSRF token for this session: {"file:":"entryForm_v1.0.0.php","line#:":36,"session_id":"**ccbbhescjqevk02id43nf6tmgc**","IP":"::1","$_SESSION[\"token\"]":"64373566386535363733353835303938623364643931333838643636643030373736346332396136303432626634306266303366383433386361633530376465"}
[2020-06-09 19:13:12] WF.DEBUG: Bypassing date check - DEV MODE {"file:":"commonfunctions.php","line#:":176,"session_id":"**ccbbhescjqevk02id43nf6tmgc**","IP":"::1"}
[2020-06-09 19:13:12] WF.DEBUG: Function isContestOpen check {"file:":"entryForm_v1.0.0.php","line#:":253,"session_id":"**ccbbhescjqevk02id43nf6tmgc**","IP":"::1","chkContest":"open"}
[2020-06-09 19:13:12] WF.DEBUG: About to call getCategories() ... {"file:":"entryForm_v1.0.0.php","line#:":331,"session_id":"**ccbbhescjqevk02id43nf6tmgc**","IP":"::1"}
[2020-06-09 19:13:12] WF.DEBUG: Cache key "categories" found. {"file:":"commonfunctions.php","line#:":393,"session_id":"**ccbbhescjqevk02id43nf6tmgc**","IP":"::1","Year":"2020"}
[2020-06-09 19:13:12] WF.DEBUG: After call getCategories() ... {"file:":"entryForm_v1.0.0.php","line#:":359,"session_id":"**ccbbhescjqevk02id43nf6tmgc**","IP":"::1"}
[2020-06-09 19:13:12] WF.DEBUG: Before loading modalLoading.htm ... {"file:":"entryForm_v1.0.0.php","line#:":418,"session_id":"**ccbbhescjqevk02id43nf6tmgc**","IP":"::1"}
[2020-06-09 19:13:12] WF.DEBUG: After loading modalLoading.htm ... {"file:":"entryForm_v1.0.0.php","line#:":428,"session_id":"**ccbbhescjqevk02id43nf6tmgc**","IP":"::1"}
[2020-06-09 19:13:12] WF.DEBUG: Before loading shopping cart stuff ... {"file:":"entryForm_v1.0.0.php","line#:":438,"session_id":"**ccbbhescjqevk02id43nf6tmgc**","IP":"::1"}
[2020-06-09 19:13:12] WF.DEBUG: After loading shopping cart stuff ... {"file:":"entryForm_v1.0.0.php","line#:":449,"session_id":"**ccbbhescjqevk02id43nf6tmgc**","IP":"::1"}
[2020-06-09 19:13:12] WF.DEBUG: Before loading footer.htm ... {"file:":"entryForm_v1.0.0.php","line#:":459,"session_id":"**ccbbhescjqevk02id43nf6tmgc**","IP":"::1"}
[2020-06-09 19:13:12] WF.DEBUG: After loading footer.htm ... {"file:":"entryForm_v1.0.0.php","line#:":467,"session_id":"**ccbbhescjqevk02id43nf6tmgc**","IP":"::1"}
[2020-06-09 19:13:12] WF.DEBUG: Reading config settings and setting file includes {"file:":"setConfig.php","line#:":48,"$_SERVER[\"DOCUMENT_ROOT\"]":"/Users/Ross/Dropbox/htdocs/wantafest_mamp_pro/public_html","DB_SERVERNAME":"localhost","DB_NAME":"wantafest_contest_online","DB_SERVER_PORT":3311,"DB_USERNAME":"root","EMAIL_HOST":"mail.wonderfest.com","EMAIL_SMTP_AUTH":true,"EMAIL_USERNAME":"x.com","EMAIL_SMTP_SECURE":"tls","EMAIL_SERVER_PORT":587,"EMAIL_SEND_LOGS_TO":"y.com","EMAIL_SEND":false,"LOG_FLDR":"/Users/Ross/Dropbox/htdocs/wantafest_mamp_pro/private/contest/logs/","CACHE_FLDR":"/Users/Ross/Dropbox/htdocs/wantafest_mamp_pro/private/contest/cached-files","CACHE_EXPIRE_SQL_SEC":59,"CACHE_EXPIRE_PRINTOUT_SEC":360,"PHOTOS_FLDR":"/Users/Ross/Dropbox/htdocs/wantafest_mamp_pro/private/contest/photos/","BO_EXPIRE_SESSION_AFTER_SEC":900,"$entryFormCookieParams[\"lifetime\"]":1800,"$entryFormCookieParams[\"path\"]":"/","$entryFormCookieParams[\"domain\"]":".localhost","$entryFormCookieParams[\"secure\"]":false,"$entryFormCookieParams[\"httponly\"]":true,"PHP_SESSION_FLDR":"/Users/Ross/Dropbox/htdocs/wantafest_mamp_pro/sessions"}
[2020-06-09 19:13:12] WF.DEBUG: Session started or resumed - check session_id {"file:":"startSession.php","line#:":41,"session_id":"**vf7fgnu92up1uim9nlup1h8nlu**","session_name":"entry_form","Cookie lifetime":1800,"Cookie path":"/","Cookie domain":".localhost","Cookie secure":false,"Cookie httponly":true,"IP":"::1"}
[2020-06-09 19:13:12] WF.DEBUG: PHP version is 5.4.0 or greater {"file:":"startSession.php","line#:":65,"IP":"::1","phpversion()":"7.4.2","session_status()":2}
I'm using MAMP Pro 5.7 on macOS 10.14.6. My PHP version is 7.4.2
EDIT Per @verjas' suggestion, I moved my favicon to a specific location and updated my page to use that. I can see that icon in the browser tab, and when I view-source I see this:
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta name="robots" content="noindex,nofollow" />
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet">
<link rel="shortcut icon" href="/contest/common/img/Favicon.png"/>
.
.
.
But looking in my Apache error log, I see this:
[Wed Jun 10 13:53:34 2020] [error] [client ::1] File does not exist: /Users/Ross/Dropbox/htdocs/wantafest_mamp_pro/public_html/favicon.ico, referer: http://want-a-fest:8888/contest/entry/entryForm.php
What the? My page shouldn't be looking for it in the public_html root, and the file name is different (should be "Favicon.png"). Do I have to have a "favicon.ico" in the public_html even if it is not being referenced?