Perl, CGI::Session doesn't work correctly - perl

I'm writing a web application that needs to make use of module CGI::Session ver 4.35. Upon receiving request from the client with an SESSIONID string
$sid = $cgi->cookie("CGISESSID") || $cgi->param("CGISESSID") || undef;
it tries to recreate the session by passing the $sid as an argument
$session = new CGI::Session($sid) or ($logger->error(CGI::Session->errstr) and die);
If there was an session created with that sid, $session->id and $sid are suppose to be the same, but the truth is it's NOT.
This is the statement where I create a completely new session
$session = new CGI::Session("id:md5", undef, {Directory=>$SESSION_DIR})
or ($logger->error(CGI::Session->errstr) and die);
What went wrong here? How am I supposed to use the module CGI::Session correctly?

I'm the maintainer of CGI::Session. I recommend creating the session the same way in all cases, like this:
$session = CGI::Session->new("id:md5", $cgi, {Directory=>$SESSION_DIR});
This follows the recommended syntax in the docs for new(). I also recommend making sure that you call flush() explicitly near the end of the script. The reason for that is explained more here:
http://metacpan.org/pod/CGI::Session#A-Warning-about-Auto-flushing

there really is no need for you to grab the session cookie yourself. If you pass a CGI object instance to CGI::Session it does it for you. So, basically, the above code by jfd can be re-written like this:
my $session = CGI::Session->new( $query );
$self->header_props(-cookie => $session->cookie);
And $query->cookie() and if/else blocks are all redundant, because they already exist in CGI::Session's logic!
So the above code checks for client's cookie named CGI::Session->name (which defaults to CGISESSID). If it doesn't exist, looks for query parameter in the URL or request's body named CGI::Session->name (which also defaults to CGISESSID). If it can get claimed session id it tries to load its data into the session. If the session id cannot be validated (either expired, or forged) it ignores it, and creates a brand new, empty session.
If the session id cannot be found in either cookie nor in URL parameters it creates a new session.
most examples of session management I see out there try to re-invent the session logic inside the code while using CGI::Session. I'm just here to tell you that all that code is completely redundant!!!
Enjoy using CGI::Session!

So, I'm not clear why you are creating the session twice? You want to first try and get the sid, and then create the session with it, whether it exists or not. If it doesn't exist, set the cookie. It's been awhile, but I pulled this from an old piece of code...
my $sid = $query->cookie('CGISESSID') || undef;
# grab the session obj, if one already exists otherwise create one
my $session = new CGI::Session( "id:md5", $sid, { Directory => $SESSION_DIR } );
# If there is no user cookie, or it's non existent, we give them a new one
if ( !$sid or $sid ne $session->id ) {
my $cookie = $query->cookie(
-name => 'CGISESSID',
-value => $session->id,
-expires => EXPIRE_TIME
);
$self->header_props( -cookie => $cookie );
}

Related

Perl: how can i read a specific cookie value stored by Firefox

I'm currently developing a script in Perl as want to recover a specific cookie that Facebook is currently storing in my browser.
The cookie name is Datr and i've tried multiple Perl modules such as HTTP::Cookies, CGI::Cookie and so forth, without success.
What i wanna do is simple and i need to do it via Perl: storing the Datr value (which changes dynamically) into a new variable in my Perl script.
I decided to set a test cookie and try to read its value, but neither the script returns something nor the browser (Mozilla) seems to store my cookie.
Here is the code i used:
#!bin/perl
use CGI;
$query = new CGI;
##setting a new cookie into the browser
$cookie = $query->cookie(-name=>'MY_COOKIE',
-value=>'HelloWorld',
-domain=>'facebook.com',
-expires=>'+4h');
##retrieving cookie value
$theCookie = $query->cookie('MY_COOKIE');
Please help me with this as i'm going crazy!
thanks
First of all i would like to thanks all of you for the prompt reply.
To Dave: thanks for your very good answer on this. Yes i'm aware that Firefox can read the Datr value via GUI, but actually i need to read it with Perl code, because my original script features a specific SSL request which actually needs the Datr value. Facebook has arealdy answered on Datr ''understanding'' (http://www.adweek.com/digital/datr-cookie-belgium/) and seems an interesting side-topic. Getting back to my original request, i'm quite sure that Firefox stores all cookies value locally in some sort of .sqlite db called cookie.sqlite, and i can get there! If a try to read it ''manually'', i can see the Datr string, but that's not going to fix anything as i do not want to update my PL script each and every time i want to perform any sort of SSL request to FB!! that's the point.
My original question could be reformulated as follows: is there any way to query the .sqlite cookie db created by Firefox in order to retreive the Datr cookie and store it in a Perl variable? Please bear in mind that i don't want to set an absolute ''path to file'' in my PL script, as it is supposed to be executed either in Linux or any other OS (Windows, OSx..).
Thanks in advace for any further reply on this subject.
There are two steps to reading a cookie.
Accessing the value of the cookie.
Understanding that value.
Modules like the ones you mention, only deal with the first step. They allow you to write cookie headers into your web application's responses and read cookie headers from requests that come into your web application. But they will only read or write cookies for the domain that your web application is running on. It's very unlikely that your application is running on facebook.com, therefore these modules are going to be useless to you.
However, all is not lost. Firefox will give you access to any cookies that are stored in the browser. I assume you already know that (as you know the name of the cookie you're interested in) but in case you don't - choose "preferences" from the hamburger menu and then "privacy"; that page has a "remove individual cookies" link.
So you can see the contents of the datr cookie. I'm looking at mine right now. It's string of 24 random-looking characters.
And that's the next problem. How do you interpret that string? Only Facebook can answer that. It's possible that it is a hash containing all sorts of interesting data. But it will be almost impossible to prove that. More likely (because this is, I think, best practice) it's just a random string of characters which is a key into some data store that is held somewhere within Facebook's system.
So it's either a well-encrypted secret or a random string. Either way it's useless to you.
Getting the value of a cookie is easy. Understanding that value is (usually) impossible.
Update: So actually, now you've redefined the question completely. It's not about cookies at all. It's about reading data from an SQLite database. And for that you should look at DBI and DBD::SQLite. If you have any more specific questions about how to do this, then please ask a new question.
This is what i've figured out. It works well. I didn't use absolute path (at least, directly) therefore, in theory, it should work also in Windows (having Perl on it) and Mac OS, but i didn't test it. It simply search the .sqlite db into the system and once found it returns the value of Datr stored by Firefox.
#!/bin/perl
use File::Find::Rule;
use DBI;
use strict;
my #files = File::Find::Rule->file()
->name('firefox', 'cookies.sqlite')
->in( '/home' );
my #matches = grep { /firefox/i && /mozilla/i } #files;
print "DB PATH: #matches\n\n";
####### connect to .sqlite database
my $database = #matches[0];
my $dbfile = "$database";
my $dsn = "dbi:SQLite:dbname=$dbfile"; #set path to db
my $user = "";
my $password = "";
my $dbh = DBI->connect($dsn, $user, $password, {
PrintError => 0,
RaiseError => 1,
});
print "Database opened successfully\n\n";
my $stmt = "SELECT VALUE FROM moz_cookies WHERE NAME='datr'";
my $sth = $dbh->prepare( $stmt );
my $rv = $sth->execute() or die $DBI::errstr;
if($rv < 0) {
print $DBI::errstr;
}
my #row=$sth->fetchrow_array(); #fetching data of sth into a new array
print "COOKIE VALUE: #row"; #print data
print "\n\nOperation done successfully\n";

How do I change the session id with CGI::Session?

I have this application where you create a session cookie like so:
$session = CGI::Session->new() or die CGI::Session->errstr;
$cookie = CGI::Cookie->new(-name=>$session->name, -value=>$session->id,-expires=>'+2h', -secure => 1 );
And then set the header like this:
print $q->header(-cookie=>$cookie);
I need to change the session ID of this cookie upon logging in to the application (in a smiliar manner to php's session_regenerate_id ). Is there anyway of doing this in Perl? I've been looking through the documentation and I can't find any ways of doing this really. If not, other suggestions on how to solve this are welcomed.

CGI::Session Randomly clearing parameter

When users log in I have created a new Session in Perl CGI, and stored the session ID in a cookie (CGISESSID). This cookie has then been sent the next page via a redirect, as shown below:
my $session = CGI::Session->new("driver:File", undef, {Directory=>"/tmp"});
my $sid = $session->id();
$session->param("username", $username);
$session->expire('+15m');
print redirect ( -cookie => cookie(CGISESSID => $session->id), -uri => 'x.cgi');
On 'x.cgi' (and all other pages of the site) I have:
my $sid = cookie ('CGISESSID') || param('CGISESSID') || undef;
my $session = CGI::Session->load(undef, $sid, {Directory=>'/tmp'});
$session->expire('+15m');
my $username = $session->param("username");
if (!defined ($username)) {
print redirect ("login.cgi");
}
However, after a random amount of time/clicks (well before the 15m mark, anywhere from the 1st click to a click 2-3 minutes later), it redirects to login.cgi.
Debugging has shown me that although it redirects me (and $username is not defined), the code still obtains the correct session ID originally created (the session seems to be still intact), and the cookie remains.
I don't pass CGISESSID as a parameter on any page requests/links (as I assume a cookie saves me from doing this)
Any idea what could be causing $username to be undefined after a random amount of time?
Given that your description is correct and your code works for some time, and after that stops working, I would suggest checking that noone is cleaning up your /tmp directory and, in particular, the session file there.
When you create a session using CGI::Session module with file driver it just creates a text file in the given directory (/tmp in your code). It's a text file with some Perl code, you can cat it and see what's inside:
$ cat cgisess_126b3cd2c4b9ac6eaac0185afbc46d34 && echo
$D = {'_SESSION_ID' => '126b3cd2c4b9ac6eaac0185afbc46d34','_SESSION_ATIME' => 1413493418,'_SESSION_REMOTE_ADDR' => '','_SESSION_CTIME' => 1413493418};;$D
For file driver the filename it uses for the session can be obtained by
my $filename = sprintf $CGI::Session::Driver::file::FileName, $session->id;
Check that it is created and exists for at least 15 minutes. If it disappears, blame some cron job, some other script or your hosting provider.

Perl CGI persistent cookies

I want the user to non have to login even if the browser was closed. My cookies are set to expire after a month.
when the user logs in sucessfully
$session = CGI::Session->new (undef, undef, {Directory=>'tmp/'})
or die CGI::Session->errstr;
$session->param('username', $username);
$session->expire('+1M');
$cookie = $cgi->cookie( -name=>$session->name, -value=>$session->id );
print $cgi->header(-cookie=>$cookie );
They are then redirected to another page that they can access as long as they don't close the browser. This is the code in the second page:
my $cookie = $cgi->cookie('CGISESSID');
if ($cookie){
print $cgi->header(-cookie => $cookie);
else{
//ask them to relog in
}
I can see the sessions created in tmp/. How do I load an existing cookie after the browser is closed. How do I know which session to load based on the user/browser?
As long as you set a future expiration date on your cookies, they should persist even after a user restarts their browser (as long as they restart before that date, of course). To load the cookie, do exactly what you're doing:
my $cookie = $cgi->cookie('CGISESSID');
To try to load an existing session using the cookie you can simply pass your CGI object to the new method of CGI::Session:
my $session = new CGI::Session(undef, $cgi, {Directory=>"/tmp"});
This will attempt to initialize an existing session using the cookie passed in with the CGI request; if one doesn't exist, it will create a new session. Note that this assumes the cookie name is CGISESSID. To use another name, run:
CGI::Session->name("MY_SID");
# or
$session->name("MY_SID");
$session = new CGI::Session(undef, $cgi, {Directory=>'/tmp'});
If you haven't already, I would recommend reading through the CGI::Session tutorial.
EDIT: The session was set to expire in one month
$session->expire('+1M');
but the cookie was not. If you don't set an expiration on a cookie, the browser will store it in memory but not on disk; as soon as you close the browser, the cookie disappears. To set the cookie expiration, do something like
$cookie = $cgi->cookie( -name=>$session->name, -value=>$session->id, -expires=>'+1M' );

Perl CGI::Session save_param saves all parameters as array under one key

Im using CGI::Session to store session data from CGI::Application (specifically i'm using CGI::Session through the CGI::Application::Plugin::Session module).
In one of my application modes I do this:
my $self = shift;
# Get CGI query object
my $q = $self->query();
$self->session->save_param($q);
To save my parameters to the session data however on retrieving them using $self->session->param('user') I find that only the user parameter contains any data even though other parameters are being sent server side and are accessible through $q->param() the user parameter retrieved from the session is an array of the parameters, however i would expect that $self->session->param('user') would return a single string with the contents of the parameter 'user'.
Is this behavior expected?
If so why?
I'm not sure I understand exactly what you mean, but this looks weird. You're not doing what the CGI::Session doc says you should. You can't just save the CGI object. You need to store each param individually.
# Storing data in the session:
$session->param('f_name', 'Sherzod');
If you want to just store all the CGI params in your Session, do it like this:
# $q := CGI object
# $session := CGI::Session object
$session->param('foo', $q->param('foo'));
$session->param('bar', $q->param('bar'));
Or you might even do it like this for all of them:
foreach my $key ($q->param) {
$session->param($key, $q->param($key));
}