What are the limits on session names in PHP? - php

The PHP docs on session_name() say:
It should contain only alphanumeric characters; it should be short and descriptive (i.e. for users with enabled cookie warnings). ... The session name can't consist of digits only, at least one letter must be present. Otherwise a new session id is generated every time.
So it's clear you must have something non-numeric in there, but it's not quite clear what characters you can't have. The cookie spec itself denies ()<>#,;:\"/[]?={}, but that still leaves others that might be permitted but are not strictly alphanumeric. This is important because cookie security prefixes use - and _ in names like __Secure-PHPSESSID. So I had a rummage in the PHP source code at the session_name function – but I can't see that it does anything other than check it's a string. In practice, it works fine, but I'd be more comfortable knowing precisely why! For example, this works:
session_name('__Secure-PHPSESSID');
session_start();
$_SESSION['test'] = $_SESSION['test'] . "\n" . rand(0,100);
var_dump($_SESSION);
So what are the actual limits on PHP session names?

I got a bit further with this. The rules for a session name are defined in this validation function, which permits [a-zA-Z0-9,-]{1,256} (but not numeric-only). You can have commas and dashes in session names in addition to alphanumerics, so the docs are wrong on that. This function is called from an internal session_create_id function, which triggers a warning if the session name doesn't pass that validation.
Despite this, no warning is triggered when passing in a session name containing _. This is demonstrable:
<?php
ini_set('display_errors', true);
error_reporting(E_ALL);
session_name('__Secure-MySession');
session_start();
if (!array_key_exists('test', $_SESSION)) {
$_SESSION['test'] = '';
}
$_SESSION['test'] .= "\n" . rand(0,100);
var_dump($_SESSION);
echo session_name();
This works perfectly, triggering no errors or warnings, and shows a growing list of random numbers (showing that the session storage is working and therefore the cookies are too), and the second session_name call with no params shows the session name that we set:
__Secure-MySession
And the HTTP headers show that the script sets a cookie called __Secure-MySession:
I also tried naming the session My_Session, just in case PHP looks for explicit __Session- prefix, but that works just fine too. Other characters like # or ( do not trigger an error either; in those cases the session name is URL-encoded, which looks remarkably like this bug that was fixed quite a while ago. As expected, 123, works, but also URL-encodes the comma.
So while this demonstrates that having _ in session names works fine, I can't tell you why. I've asked elsewhere too, and if I find out, I will update this question!
Coincidentally, draft 06 of RFC6265bis expires today.

Related

Recovering from an invalid session id in the most elegant manner

Lately, I've been getting the following error(s) in my log:
PHP Warning: session_start(): The session id is too long or contains illegal characters, valid characters are a-z, A-Z, 0-9 and '-,' in [...] on line [..]
PHP Warning: Unknown: The session id is too long or contains illegal characters, valid characters are a-z, A-Z, 0-9 and '-,' in Unknown on line 0
PHP Warning: Unknown: Failed to write session data (files). Please verify that the current setting of session.save_path is correct (/var/lib/php5) in Unknown on line 0
The answers to this previous question suffice for detecting and diverting such scenarios, so that no error is generated, but I'm interested in the most elegant recovery; that is, ending up with a valid new (empty) session.
First, though, a few refinements to the detection & diversion code in that previous question, which is now six years old:
These days, sessions are more likely to be handled exclusively through cookies. The session.use_only_cookies flag in my php.ini is enabled, and since I don't remember changing it I assume this is the default value.
Which characters are used in a valid session id, and how many of those characters, depends on the session.hash_function and session.hash_bits_per_character values in php.ini. My values are 0 and 5, respectively, which (unless I'm mistaken) means my own session ids should match the regular expression /^[a-v0-9]{26}$/. I assume these are default values as well.
The name of the cookie used to store the session can be customized using session.name in php.ini. The correct value can always be retrieved using the session_name() function.
Given these, the most elegant means of diversion would (probably) be as follows:
function my_session_start() {
if (!isset($_COOKIE[session_name()]) || preg_match('/^[a-v0-9]{26}$/', $_COOKIE[session_name()]) !== 0) {
return session_start(); // since 5.3, returns TRUE on success, FALSE on failure.
}
else {
return false;
}
}
As for recovery (which would replace the return false; in the else block), one answer to the previous question suggested the following:
session_id(uniqid());
session_start();
session_regenerate_id();
I'm concerned, however, that this means of recovery, having received only two upvotes and no comments, is insufficiently reviewed.
The answer to this question suggests that the internal session_start() function relies directly on the value of $_COOKIE[session_name()], rather than some other internal representation of that value. Is this the case? If so, a my_session_start() function with detection and recovery could be as simple as:
function my_session_start() {
if (isset($_COOKIE[session_name()]) && preg_match('/^[a-v0-9]{26}$/', $_COOKIE[session_name()]) === 0) {
unset($_COOKIE[session_name()]);
}
return session_start();
}
I've only seen this happen when $_COOKIE['PHPSESSID'] === ''. This happens when someone uses Firebug to "clear cookie". I want to only handle this specific scenario; if the session ID is invalid in some other way, I want to be warned. Dealing with my specific use case is simple:
$session_name = session_name();
if (isset($_COOKIE[$session_name]) and empty($_COOKIE[$session_name])) {
// This happens when someone does "clear cookie" in Firebug; it causes session_start()
// to trigger a warning. session_start() relies on $_COOKIE[$session_name], thus:
unset($_COOKIE[$session_name]);
}
session_start();
Out of curiosity, do you actually need to deal with any scenario other than it being an empty string? It could only be something else if done maliciously (in order to trigger a warning), but since you have display_errors disabled in production, nobody can gain any information from that.

Session Variables Disappearing in PHP

I came across some strange session behavior today while testing some things out. First off, I've been tasked to create a registration feature for a WordPress website. I've done a fair amount of work in WordPress in the past, but I've never done anything involving any server-side scripting languages.
I was testing out my work environment, which involves making sure that I'm able to connect to MySQL, making sure sessions are working, and basically just familiarizing myself with the setup.
When testing sessions, I did the following:
<?php
$timer = time();
$_SESSION[$timer] = $timer;
print_r($_SESSION);
?>
I was expecting on the first load of the page to see something like this:
Array
(
[1396745563] => 1396745563
)
... which is exactly what happened. On the second refresh, I wanted to see:
Array
(
[1396745563] => 1396745563
[1396745570] => 1396745570
)
... But instead, I saw:
Array
(
[1396745570] => 1396745570
)
Wow! The original session variable for timestamp 1396745563 was gone, and there was a new one in it's place.
My immediate reaction was that maybe a session_start() was missing, but that was not the case. Just to further convince myself that something was weird about this, I altered my code to look like this:
<?php
$timer = time();
$_SESSION['time_' . $timer] = $timer;
print_r($_SESSION);
?>
I thought that perhaps having a session variable with a timestamp for an index had some sort of special behavior (which might be the answer to this question for all I know).
This time, the behavior was exactly as I had expected. Each refresh of the page added on to the array print-out. After a few refreshes of the page, it looked something like this:
Array
(
[time_1396745663] => 1396745663
[time_1396745667] => 1396745667
[time_1396745671] => 1396745671
[time_1396745675] => 1396745675
[1396745570] => 1396745570
)
That oddball variable is the session data that carried over from my original attempt (I'm just being precise).
Can someone explain this extremely odd behavior? I can't think of anything obvious that I could have done that would have caused this.
Note: I doubt that this has anything to do with WordPress; I only included it to give a motive behind doing this.
The keys of $_SESSION adhere to the same rules as valid variable names in PHP. They must begin with a letter or an underscore, followed by any number of letters, numbers or underscores. Therefore, the output of time() can’t serve as a key unless it’s prefixed by at least one letter or underscore.
Add the line error_reporting(E_ALL); to your code and PHP will throw a notice like the following:
Notice: Unknown: Skipping numeric key 1396747512 in Unknown on line 0

Can anyone please explain me how is this happening?

I have created a page like account_details.php in which I'm counting the number of times the user visits the page. So for that I have created a file called count_session.php in which the code is like:
<?php
session_start();
if(!isset($_SESSION['counter'])){
$_SESSION['counter'] = 0;
}else{
$_SESSION['counter'] = $_SESSION['counter'] + 1;
}
?>
I include the above file in account_details.php page in which I want the user to see how many times he has visited and it works fine but I don't understand how does it work? I have checked using the echo message inside the first 'if' condition that is if(!isset) and it seems it does not enter that condition instead it enters the else part but then how come it's getting initialized by 0, but when the user visits the account_details.php page the message pops up like "You have visited this page for 1 times". How come it's calculating 0 + 1 when it's not yet initialized anywhere in the count_session.php file. Can anyone please tell me?
This has nothing to do with session variables, as it works the same way for regular variables.
If you write something like $a = $a + 1 and $a is not declared anywhere, PHP considers it as not set, which translates to 0 if cast to int (the same way it translates to false if cast to bool, but that's not relevant in this case). The reason why your variable is cast to int, is that PHP feels the need to, because you are trying to perform a mathematical operation on that variable.
To sum up: PHP detects that you want to add up 2 values, does something like intval($a) (but internally of course) and adds 1 to that. In addition, if you have notices enabled, this should throw you a notice.
Sessions are global to a site (as in hostname) because they are identified by an ID stored in cookies, which are by default sent back to to all URLs under a single domain name. You can restrict that further, but this is the default behavior.

Stored value from soap __getLastRequest() echos but won't register in array

I've got a problem that seems particularly odd (to me at least). So I'm using a soap API to interact with a program that (among other things) creates user groups and assigns users to them. I need to create a group and then assign a user to it using two different soap functions; this requires me to store the returned variable from one soap api call and input it into the next (see below).
$value = $SOAP->addOrganisation($params);
$group=$SOAP->__getLastResponse();
echo "<br/>Group: ".$group."<br/>";
$params=array('SID'=>$sid, 'user_id'=>$user, 'organisation_id'=>$group, 'insertedby'=>1);
$value= $SOAP->addUserToOrganisation($params);
Straight-forward right?
But here's where it gets interesting: the variable $group echos what I'm expecting but seems not to work in the soap call addUserToOrganisation. The returned value says that it ran fine, however addUserToOrganisation doesn't end up carrying out its task (actually adding the user to the group).
Conjectures anticipated: (Stuff I've already guessed at)
Maybe it's a glitch in the function addUserToOrganisation?
That would make sense and I thought the same. However when I hard-coded a number in there (even when it was the exact same number the variable would have echoed), it works just fine.
Maybe the API doesn't play nicely with variables in that spot in the array?
I actually reset the variable manually with a number (see below) and it worked no problem.
$value = $SOAP->addOrganisation($params);
$group=$SOAP->__getLastResponse();
echo "<br/>Group: ".$group."<br/>";
$group=55;
echo "<br/>Group: ".$group."<br/>";
$params=array('SID'=>$sid, 'user_id'=>$user, 'organisation_id'=>$group, 'insertedby'=>1);
$value= $SOAP->addUserToOrganisation($params);
Any stray spaces attached to that variable?
None that I can see.
...Maybe you pissed off the gnomes inside your computer that make it work?
Quite possibly but they've put up with my abuse for so long, why the sudden uprising?
My best guess: As far as I can tell, there just seems that there is something inherently unacceptable about using the output of __getLastRequest(), even though it just looks like a regular old number when echoed. I don't know if not all strings are equal or something but if any of you have an idea, I'd really appreciate it.
(just watch, it turns out to somehow be some dumb syntax error)
Anyway, thanks in advance

Do I need to escape/sanitize the following? If so, what is the best method?

Do I need to escape/sanitize the following?
$_SERVER['HTTP_USER_AGENT'] in a
PHP script (not inserted into
database or displayed to user), for
example:
if ($_SERVER['HTTP_USER_AGENT']==$xyz) {
echo "Congrats, you are using XYZ browser";
} else {
echo "You are not using XYZ browser.";
}
$_SERVER['HTTP_USER_AGENT'] when
placed as a session variable, for
example:
$_SESSION['userAgent']=$_SERVER['HTTP_USER_AGENT']
Anything that is going to be hashed,
for example:
hash('sha512',$randomDataPostedByUser)
User input destined for email body
(in other words, I've already taken
care of email header injections).
If any of the above do need to be excaped/sanitized, what is the best method for each case?
No, there is no need for sanitation in any of the examples you show, with the following very rare exception for the mail body example:
(Windows only) When PHP is talking to a SMTP server directly, if a full stop is found on the start of a line, it is removed. To counter-act this, replace these occurrences with a double dot.
However, you may need to sanitize the session variable later, depending on what you are going to do with it.
Other notes:
Your first example doesn't seem to make sense, because user agent strings vary heavily. You will have to use strstr() or regular expressions to match user agents.
Storing the user agent in a session variable might not be a good idea if you're doing comparisons - just pull it from the $_SERVER array when you need it.

Categories