I have a perl-cgi script through which I am trying to log in.
When the UserName and password are valid, I create a session and redirect a cookie to another page.
However, after the session expires(I have set the expiration time), I do not see it get deleted from the /tmp/sessions folder in this case. I have used the command to delete the session as well.
Can someone help me to delete the session once it expires? Also, does the cookie expire once the session is deleted?
use CGI::Session;
use CGI::Session::Tutorial;
use CGI::Session::Driver::file;
use CGI::Cookie;
my $session = new CGI::Session("driver:File", undef, {Directory=>"/tmp/sessions"});
my $sid = $session->id();
#my $cookie = $query->cookie(CGISESSID => $session->id);
my $cookie = $query->cookie(-name=>"CGISESSID",
-value=>$session->id,
-domain=>'abc.com',
-expires=>"+5m",
-path=>"/");
print $query->redirect(-uri => 'http://abc.cgi', -cookie => $cookie);
$session->param("UserName", $loginUserName);
$query->hidden( 'UserName', $loginUserName );
$session->expire("UserName",'1m');
$session->expire('+5m');
$session->delete();
To avoid confusion with ->delete, I'm going to use the word "remove" instead of "delete" to refer to the removal of the session from storage.
Can someone help me to delete the session once it expires?
The removal doesn't happen when the session expires. That would require having a continually running process. Furthermore, at no point does CGI::Session scan storage for expired sessions; that would take too long since it would require loading each and every session. Instead, CGI::Session only removes expired sessions when you try to load them.
#!/usr/bin/perl
use strict;
use warnings qw( all );
use feature qw( say );
use CGI::Session qw( );
use CGI::Session::Driver::file qw( );
my $session_id; # This represents the browser's cookie.
# These represent requests made the by the browser.
for my $request_num (1..3) {
my $session = CGI::Session->new("driver:file", $session_id, { Directory => "/tmp/sessions" });
$session->expire("1s");
$session_id = $session->id; # This represents setting the browser's cookie.
say "$request_num: ", $session->id;
say "$request_num: ", $session->param("foo") // "[undef]";
$session->param("foo" => "bar");
# This represents time passing by before the third request.
if ($request_num == 2) {
say "Letting session expire...";
sleep(2);
}
}
Output:
$ ./a
1: c57ab28952c6ed422c15f1a223f4b45d
1: [undef]
2: c57ab28952c6ed422c15f1a223f4b45d
2: bar
Letting session expire...
3: df8ba3b66f23a9a2a652520fa6b4c30b
3: [undef]
$ ls -1 /tmp/sessions
cgisess_df8ba3b66f23a9a2a652520fa6b4c30b
If you want to prevent files from accumulating on your drive, create a cron job that deletes old files.
find /tmp/sessions -mindepth 1 -maxdepth 1 -mtime 7 -delete
Also, does the cookie expire once the session is deleted?
No, the cookie expires when you tell it to expire. The thing is, it doesn't matter if the browser's cookie expires or not. For the second argument of new, there's no difference between passing undef, passing the id of a deleted session and passing the id of an expired session; you'll get a new session in all three cases. If anything, it's actually better if it doesn't expire as soon as the session expires because this allows the session to be removed (as demonstrated above).
How to delete a session in cgi-perl?
$session->delete is indeed the way to go, but the actual removal only happens when you would save (flush) the session.
$session->delete();
$session->flush(); # Or let `$session` get destroyed.
As the documentation notes
delete()
Sets the objects status to be "deleted". Subsequent read/write requests on the same object will fail. To physically delete it from the data store you need to call flush(). CGI::Session attempts to do this automatically when the object is being destroyed (usually as the script exits), but see "A Warning about Auto-flushing". (emphases mine)
You go on to ask:
Also, does the cookie expire once the session is deleted?
Of course not. You already sent a cookie to the user's browser with an expiration time of five minutes in the future. The cookie will expire then.
If, in the mean time, you have forced the expiration of the session on the server, the user's browser will still send the previously received cookie. Your application will just not find a session corresponding to the session identifier stored in the cookie.
You really need to understand the HTTP request/response cycle before taking one more step.
Per the CGI::Session documentation, deleteing a session "Sets the objects status to be "deleted". Subsequent read/write requests on the same object will fail. To physically delete it from the data store you need to call flush()." (emphasis mine)
Also, per the CGI::Session::Tutorial, "Expiring a session is the same as deleting it via delete(), but deletion takes place automatically." It is not necessary (or useful) to delete a session after it has expired.
Related
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";
I have a program where I want the session data to expire at an absolute Epoch time after a request. I don't want the expiration to update for every request.
default_expiration works but not expires.
This does not work:
post '/access' => sub {
my $self = shift;
my $user = $self->param('username');
if ($self->authenticate($user, $self->param('password'))) {
### Set this otherwise timeout refreshes for every request...
$self->session(expires => time + 120);
(...)
}
};
Mojolicious cookie set to 1 hour (3600s) the default...
$self->sessions->default_expiration(120) works but reset for every browser request.
I am using the Mojolicious::Plugin::Authentication plugin.
You can use the expiration key to set the same value as the default_expiration does. Before you set the value though, be sure to check to see if the key exists already and don't overwrite it if it does.
I am using CGI.pm to write out cookies. Now during the course of the user using my site, other cookies are added to the "test.com" cookie set, (as shown in the broswer history)
But now I want to log the user out, and "clean" the PC. Since I don't know what scripts the user has used, I can't foresee what cookies would be on the PC.
In short, it there a way to read all the cookies for "test.com" back into a script so I can then print them out again with a 1s duration, (effectively 'deleting' them) ** I know you can read the cookie back in with $xyz=cookie('$name') ... but how can I create the array holding the $name variable so I can loop through it? The script will also run on "test.com", so the cross site policy is not an issue
+++++
brian d foy added a partial answer below. So this how I envisage the code might be strung together.
use CGI::Cookie;
%cookies = CGI::Cookie->fetch;
for (keys %cookies) {
$del_cookie.="cookie(-NAME=>'$cookies[$_]',-PATH=>'/',-EXPIRES=>'+1s');";
}
print header(-cookie=>[$del_cookie]);
I wondered how the script would recognise the domain. Appears the script is intelligent enough to only load the cookies for the domain for which the script is being executed on. (Now I've just got to find out why Firefox doesn't delete expired cookies!! Just found some listed that expired 29th - 31st Jan within my test domain, and at first wondered why they didn't appear in my cookie list!)
If you are trying to do this from your CGI script, you'll only have access to the cookies for that domain. You can get that list and reset them by giving them a time in the past.
It sounds like you aren't asking a cookie question at all. You're asking how to make an array. The CGI::Cookies (which comes with CGI.pm) has an example to deal with all the cookies you have access to under that domain:
%cookies = CGI::Cookie->fetch;
for (keys %cookies) {
do_something($cookies{$_});
}
This is what I ended up with:
use CGI::Cookies;
%cookies = CGI::Cookie->fetch;
#cookie = keys %cookies;
for($x=0; $x<#cookie; $x++){
my $c = CGI::Cookie->new(-name => $cookie[$x],-value => '-',-expires => '+1s');
print "Set-Cookie: $c\n";
}
print "content-type: text/html\n\n";
Firefox still leaves the cookies intact, (apparently that's a "design issue" and not a bug!!) but they are reset to a void value, and set to expire / become redundant in 1 second. Plus, quite why the "print" statement being sent before the "content-type" header doesn't cause a server error I don't know. OK, so purists will probably find a simpler system, and use "foreach" rather than for/next loop ... but I understand how the latter works!
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' );
I need to set a very short session (3 minutes) upon hitting a specific page on my site. If someone hits that page again during that 3 minute session, the session should update to expire 3 minutes from that time.
On my "bootstrap" (it isn't a typical Zend bootstrap, but it is included in every page), I do the following:
$aSessionSaveHandlerConfig = array
(
"name" => "Sessions",
"primary" => "Session_ID",
"modifiedColumn" => "UpdateTimestamp",
"dataColumn" => "Data",
"lifetimeColumn" => "Lifetime",
);
$oSaveHandler = new Zend_Session_SaveHandler_DbTable($aSessionSaveHandlerConfig);
$oSaveHandler->setLifetime(App::$ReservationTimeout)->setOverrideLifetime(true);
Zend_Session::setSaveHandler($oSaveHandler);
ini_set("session.cookie_lifetime",App::$ReservationTimeout);
$aSessionOptions = array
(
"gc_probability" => 100,
"gc_divisor" => 100,
"gc_maxlifetime" => App::$ReservationTimeout,
"cookie_lifetime" => App::$ReservationTimeout,
);
Zend_Session::setOptions($aSessionOptions);
Then within the page that should create/update the session, I have:
App::$ReservationSession = new Zend_Session_Namespace("ReservationSession");
$oSaveHandler = Zend_Session::getSaveHandler();
$oSaveHandler->setLifetime(App::$ReservationTimeout);
I see the records in the database, the lifetime column is correct, but if i repeatedly hit the page that creates/updates the session, I get a new Session ID after 3 minutes passes (and the other one gets removed after garbage collection.
It appears the problem is getting the cookie to update it's time. Any ideas?
To get the session cookie to update its expiration time, you can use Zend_Session::rememberMe() to change the default lifetime of the cookie. Calling rememberMe() will also cause Zend_Session::regenerateId() to be called which generates a new session ID, copies old session data to the new session, and sends a new session cookie to the browser.
Try the following code and see if it solves your problem:
App::$ReservationSession = new Zend_Session_Namespace("ReservationSession");
$oSaveHandler = Zend_Session::getSaveHandler();
$oSaveHandler->setLifetime(App::$ReservationTimeout);
// Call remember me which will send a new session cookie with 3 minute expiration
// from the current time. Old session data is copied to the new one and the old
// session is deleted
Zend_Session::rememberMe(App::$ReservationTimeout);
See the manual section on Session Identifiers for more information, or see also How to reset a Zend rememberMe function on each automatic login?
UPDATE:
Given your comment, I came up with this solution that you can use.
What this does is start your session as usual and then checks a value in the session to see if the user had an existing session.
If they do have an session, it uses setcookie() to send an updated session cookie using the existing parameters (including session id), except it sets the expiration to time() + $ReservationTimeout. If they did not have a session, then there is no need to update the cookie since the expiration is already correct and it will be updated on their next request (assuming they visit before it expires).
App::$ReservationSession = new Zend_Session_Namespace("ReservationSession");
$oSaveHandler = Zend_Session::getSaveHandler();
$oSaveHandler->setLifetime(App::$ReservationTimeout);
if (!isset(App::$ReservationSession->hasSession)) {
// user had no session before or it was expired
App::$ReservationSession->hasSession = true;
} else {
// user has a valid session, update the cookie to expire 3 mins from now
$params = session_get_cookie_params();
$expire = time() + App::$ReservationTimeout;
setcookie(session_name(),
Zend_Session::getId(),
$expire,
$params['path'],
$params['domain'],
$params['secure'],
$params['httponly']);
}
I tested the solution using the files session handler and it worked as expected, I think it should be fine for your situation as well.