lat9:
Circling back on this one.
Since a session is not *normally *created for spiders (under the control of Configuration->Sessions->Prevent Spider Sessions), if a store's SQL mode is set for STRICT_TRANS_TABLES, then the $val passed into the _sess_write function (present in /includes/functions/sessions.php) is going to be null when a spider's session has ended.
Examining the database class' *bindVars *method, using a *string *bindType can result in a **null **value being returned, which is a "bad thing" in this case, since the value field can't be null per the *sessions *database-table's configuration.
function _sess_write($key, $val) {
global $db;
if (!is_object($db)) return;
$val = base64_encode($val);
global $SESS_LIFE;
$expiry = time() + $SESS_LIFE;
$sql = "insert into " . TABLE_SESSIONS . " (sesskey, expiry, `value`)
values (:zkey, :zexpiry, :zvalue)
ON DUPLICATE KEY UPDATE `value`=:zvalue, expiry=:zexpiry";
$sql = $db->bindVars($sql, ':zkey', $key, 'string');
$sql = $db->bindVars($sql, ':zexpiry', $expiry, 'integer');
$sql = $db->bindVars($sql, ':zvalue', $val, 'string');
$result = $db->Execute($sql);
return (!empty($result) && !empty($result->resource));
}
> I suggest changing that function to use the *stringIgnoreNull *type instead, since that bindType will always result in a quoted string:
> ```
function _sess_write($key, $val) {
global $db;
if (!is_object($db)) return;
$val = base64_encode($val);
global $SESS_LIFE;
$expiry = time() + $SESS_LIFE;
$sql = "insert into " . TABLE_SESSIONS . " (sesskey, expiry, `value`)
values (:zkey, :zexpiry, :zvalue)
ON DUPLICATE KEY UPDATE `value`=:zvalue, expiry=:zexpiry";
$sql = $db->bindVars($sql, ':zkey', $key, 'string');
$sql = $db->bindVars($sql, ':zexpiry', $expiry, 'integer');
$sql = $db->bindVars($sql, ':zvalue', $val, '[B]stringIgnoreNull[/B]');
$result = $db->Execute($sql);
return (!empty($result) && !empty($result->resource));
}
I've been trying to understand this since I read it several hours ago. I'm guessing that some amount of testing went into it, but I've been having difficulty following the above discussion as I have tried to lay out below even if the solution stops the error that was reported/described.
Not sure if it's the description of how this occurs or the recommended solution that doesn't seem right, but if the $val of null is passed into _sess_write (as a non-string), then base64_encode(null) is going to error and $val is not going to get set, and presumably a different error message would result. If the incoming $val is "null" (as a string) then it would get base64_encoded and therefore the result would not include the preg_match string of NULL because it would be encoded and the storage of that encoded value would not cause an error because it would be a string.
The next thing is that in order for the bindVars function to see $val = "null" would be that the $val passed into the function would have to have been the base64_decode of "null" which is not the same as passing null into the _sess_write function (pointing out that the above describes the condition differently).
Working back from that (the fact that the $val going to bindVars would contain the text of 'NULL'), the val coming into the function would have to be base64_decode('NULL') (instead of just null as described above), which doesn't exactly make sense unless that is some sort of default behavior of the session not having been started.
The other thing though is that if SESSION_FORCE_COOKIE_USE is set to true and a cookie becomes setable, then the session could be started and the whole spiders block thing doesn't do anything.
Why though would it be desired to save a null session value if the session was never started (assuming spiders are ignored and cookie use is not forced to true) and based on the expected action of not storing/maintaining a session and not expecting the session to be restored? Wouldn't the expectation be that both the read and write session code get rewritten to bypass that null condition/situation? In the write function before encoding the value take an action like, if $val == base64_decode('NULL') then return;
and on _sess_read add $value->fields['value'] != 'NULL' to the test of whether to base64_decode the value or not, though that would mean that the entire session got stored as 'NULL' rather than as an encoded series of data pairs using key | value similar to:
securityToken|s:32:"00000000000000000000000000000000";customers_host_address|s:12:"xx.xx.xxx.xx";ip_blocker_pass|b:0;cartID|s:0:"";cart|O:12:"shoppingCart":11:{s:8:"contents";a:0:{}s:5:"total";i:0;s:6:"weight";i:0;s:12:"content_type";b:0;s:18:"free_shipping_item";i:0;s:20:"free_shipping_weight";i:0;s:19:"free_shipping_price";i:0;s:14:"download_count";i:0;s:22:"total_before_discounts";i:0;s:22:"display_debug_messages";b:0;s:23:"flag_duplicate_msgs_set";b:0;}check_valid|s:4:"true";navigation|O:17:"navigationHistory":2:{s:4:"path";a:1:{i:0;a:4:{s:4:"page";s:12:"product_info";s:4:"mode";s:6:"NONSSL";s:3:"get";a:3:{s:5:"cPath";s:1:"0";s:11:"products_id";s:1:"3";s:5:"zenid";s:32:"00000000000000000000000000000000";}s:4:"post";a:0:{}}}s:8:"snapshot";a:4:{s:4:"page";s:12:"product_info";s:4:"mode";s:6:"NONSSL";s:3:"get";a:3:{s:5:"cPath";s:1:"0";s:11:"products_id";s:1:"3";s:5:"zenid";s:32:"00000000000000000000000000000000";}s:4:"post";a:0:{}}}language|s:7:"english";languages_id|s:1:"1";languages_code|s:2:"en";currency|s:3:"USD";today_is|s:10:"2017-01-07";updateExpirations|b:1;session_counter|b:1;customers_ip_address|s:12:"xx.xx.xxx.xx";
I mean, I think I can see where the suggestion of using the ignore null version of bindVars could be a good idea, but I don't see that it was fully/accurately described in the previous message.
Website Rob:
Might want to flesh that out a bit. I tried your code with MySQL in STRICT_TRANS_TABLES mode and got a broken page with the error msg.
var-type undefined: stringIgnoreNull
Do/did you have the appropriate version 1.5.5 of ZC installed? The datatype was added in 1.5.5a with preparation to transition to using 'NULL' in zen_db_input, but that was rolled back from version c to version d take 2 which was issued a few days after the first notification of version d.
in includes/classes/db/mysql/query_factory.php there is a section within the function getBindVarValue() related to strings with the following:
case 'string':
if (preg_match('/NULL/', $value)) return 'null';
return '\'' . $this->prepare_input($value) . '\'';
break;
case 'stringIgnoreNull':
return '\'' . $this->prepare_input($value) . '\'';
break;
case 'noquotestring':
return $this->prepare_input($value);
break;
The first provides the results described as needing to be "undone".
Previously there were just two string related actions:
case 'string':
return '\'' . $this->prepare_input($value) . '\'';
break;
case 'noquotestring':
return $this->prepare_input($value);
break;