I think I forgetting something in my code but can't find what.
On my server I have simple logging.php file.
If I pass user/password parameters then a new session is created.
If I pass loggout the session is destroyed.
If I pass report the list of current session variables are reported on screen.
If I test the code writing urls in my browser all works fine. First invoke .../logging.php?user=xxx&password=xxx. The session is started and the session variables are reported to me. And finally I destroy the session passing the logout parameter.
If user request a report or a logout and no session exists a HTTP-401 error code is returned to client.
On the other hand I have a piece of JavaScript code that I can inject on web page using a bookmarklet. Once code is injected I show a toolbar where user can write user/password and send to server.
The logging actions seems to works fine, and server returns me a 200 status code, but later if I make a request to logout the server returns me a 401 error, which mean no session exists.
I was using chrome and looking at HTTP request and responses can see that when I logging the server returns in the response different values for PHPSESSIONID.
That means two different AJAX request are considered different sessions. The server seems to not recognize the second request from AJAX as if it was started by the same client.
Repeat, the PHP code works fine if I execute using browser directly but not with AJAX request, so I think I forgetting something in AJAX.
Any ideas?
Thanks in advance.
Update
To be more concise, my problem is calling php from JavaScript. It seems there are no sessions started.
Imagine a very simple PHP code:
logging.php: given a user/password starts a new session and also stores 'user' names as a session variable.
request.php: which returns the user name stored as session variable.
logout.php: which destroys the session.
My first AJAX request start a PHP session. That seems fine because a PHPSESSIONID cookie is returned from server. Also I store the user name as session variable.
The second AJAX request tries to get the user name (stored in the session) but it gets nothing and in addition a new PHPSESSIONID cookie is returned from server.
I know it seems impossible and more when I'm testing using browser url request and works fine, but it's the truth.
I'm forgetting something on AJAX, expiration times or something similar?
Update again
I made some tests and I found the problem but not the solution.
My JS code is injected through a bookmarklet.
When I inject the code in a HTML page from my server, the AJAX requests works fine. The first (logging) request gets a PHPSESSID which is passed in subsequent request to the server.
On the other hand If I load google.com and inject the code, the first (logging) request gets the PHPSESSID too but later it is not sent with next requests.
Anyone has experienced the same issue? which is the problem?
Thanks in advance.
Update again, again
Ok finally I found my problem. Because my JS is injected from a different domain (current page is from domainA and my JS code comes from domainB) cookies are not cross domain, so PHPSESSID can be shared.
A possible soulution is when I logging I will return the PHP session ID in pice of JSON data and use it for subsequent calls.
If I'm correct, you're trying to log in a user by making an AJAX request to a URL, with the username and password provided in the URL? That's not really a safe construction, the password is very vulnerable this way?!
I would advice you to implement jQuery, and transer the login details using the $.POST command:
http://api.jquery.com/jQuery.post/
Make sure all your files (also those requested by AJAX) contain session_start(); on top of the file.
When every file contains session_start(); and you're using the same $_SESSION variables to check if a user is loggedin, it should work!
Are both of your AJAX requests coming from the same page? The requests are Asynchronous, so it may be that the "logged in?" request is returning its result before the "log in" request goes through.
From what you have asked, I hope your code is (at its beginning more or less) something like:
A file logging.php like this:
<?php # file : loggging.php
if(!ini_set('session.auto_start'))
// more stuff
if(!empty($_REQUEST['user']) && !empty($_REQUEST['passwd'])) {
session_regenerate_sid(); // This is important (1)
$_SESSION['user'] = $_REQUEST['user'];
// Whatever
}
A file request.php like this..
<?php # file : request.php
if(!ini_set('session.auto_start'))
// Whatever stuff to process data
var_dump($_SESSION);
// Or a nice foreach($v as $i => $x) {
// echo("[$i] => $x\n<br />");
// } instead :)
And your logout.php should read something like..
<?php # file : logout.php
if(!ini_set('session.auto_start')) session_start();
session_destroy();
You are probably not calling either session_start() or you are calling it twice.
To check this out try this: change all your session_start() lines for:
session_name('MYCoolNewName');
session_start();
Now your session should not read PHPSESSID, instead it should be MYCoolNewName.
If it is not, then your problem is the aforementioned.
(1) I put as important session_regenerate_sid() because opened authenticated sessions are a threat out there. I'll demonstrate it with an example.
Alice visits coolwebsite.com/login.php, which gives her a SID which I'll call AliceSID.
Alice tells Bob to visit coolwebsite.com/login.php?PHPSESSID=AliceSID, and when Bob does Alice could log in his account unless Bob's session was regenerated.
Related
I have several forms brought in via jQuery .ajax funciton. In the parent page I start a session like this
<php
session_start();
$_SESSION['authenticated'] = 'yes';
?>
then in the form that is loaded have a check like this:
<?php
session_start();
if($_SESSION['authenticated'] != 'yes') {
header("Location: http://www.google.com");
}
?>
I know its not the best, but it's an attempt to stop people form accessing the forms directly. The problem is that if you go to the parent page, then you can enter the form URL and get there because the session was started when you hit the parent page. How can I destroy the session or remedy this issue?
Effectively, you can't.
To make it more complicated, don't request the form URLs directly. Try to request authorize tokens per request of the main page:
If you generate the main page and you know the form to be requested beforehand, then generate tokens e.g. using md5(time().rnd()), associate each with one you your forms and save the association in your session
Then, your JS code won't request the form URLs, but a factory script using a token injected into the JS code
If you find the token in your saved association in your session, emit the form and delete the token in your session.
This way, each form can only be requested once by one preceding call of the main page.
Note, that this isn't fully safe too: If a user requests the URL of the main page using wget, he can request each form once.
You can check $_SERVER['HTTP_REFERER'] in your form .php code to see where the request is coming from. An AJAX call will set the HTTP_REFERER to the page it is called from.
if (strpos($_SERVER['HTTP_REFERER'], $_SERVER['HTTP_HOST']) === false) {
die();
}
It's not a bulletproof solution. Any page that is publicly accessible can be retrieved by an automated script.
I have a login form in every page of a website so the user can login from everywhere. I have a login.php file that I refer to it from the form (using 'action').
I use $_SERVER['HTTP_REFERER'] to redirect the user to the same page he logged in from when he succesfully log in or when he logs out.
But if there was a problem logging in, how can I send an error to the same page he is trying to log in?? I have tried sending the error using $_GET, like this:
// process the script only if the form has been submitted
if (array_key_exists('login', $_POST)) {
// Login code goes here...
// If there was a problem, destroy the session and prepare error message
else {
$_SESSION = array();
session_destroy();
header('Location: '.$_SERVER['HTTP_REFERER'].'?error');
exit;
}
But the problem is that a lot of pages in the website are like this details.php?mid=0172495. They already recive information from the $_GET method and for security reasons I cant use another $_GET method...
So, How can I pass the error???
Thanks...
Since you're already using sessions, after you destroy the session why not create a new one with $_SESSION['error'] or something similar set? Or alternatively simply don't delete the session at all but set the error which you can immediately check in other pages?
To add to what Chad Birch said...
In your login script where you redirect, check the HTTP_REFERER value for the character '?'. If it is present, append '&error' to the HTTP_REFERER and redirect to that. Otherwise append '?error' to the HTTP_REFERER and redirect to that.
I'm not sure what exactly you mean by "for security reasons I cant use another $_GET method", but in the case that there's already something in the query string, you just need to append another variable to it, instead of replacing it.
That is, if the address is like details.php?mid=0172495, you should be sending them to details.php?mid=0172495&error, whereas if it was just details.php, you send them to details.php?error.
Another way of doing what you need is to include your login.php file in every page that has the login form and just post to that same page. So you won't need any redirection at all.
This maybe is not a good scalable and maintainable solution, but it is simple. It all depends what kind of app you are writing. But you are saying that you are new to php so you can start like this. You can always go fancy later...
Scenario:
All pages of xyz.com use sessions to check if I am logged in and display customized content. I am logged into xyz.com using Firefox on my iMac. Now, when I try to visit, xyz.com/page1.php and xyz.com/page2.php at the same time, until page1 loads page2 is not processed.
Code Samples
xyz.com/contains (links):
/* session check code here */
Page1
Page2
xyz.com/page1.php contains:
/* session check code here */
echo "Hello";
flush();
sleep(5);
echo "done";
xyz.com/page2.php contains:
/* session check code here */
echo "Second Page";
/* {insert code} to log time() to log.txt */
Testing
Here's what I do. I login to shell and keep this command running:
user#xyz.com [~/public_html]# tail -f log.txt
From my browser (Firefox), (on xyz.com), I open multiple instances of page1 and page2 in tabs. If you can see, page1 will at least take 5 seconds to load. Now, until page 1 loads, request to page2 is pending. It's not at all processed. How do I know that? The code at the end of page2 logs access time to a file which I am monitoring. Let's consider the following case:
Requests fired to (in order)
1. page1.php
2. page2.php
3. page2.php
4. page1.php
5. page1.php
6. page2.php
Result (approx)
1237961919 <-- #2
1237961920 <-- #3
1237961931 <-- #6
What was expected (approx)
1237961915 <-- #2
1237961915 <-- #3
1237961915 <-- #6
^Assuming that all calls were fired at the same time
Observation
While the calls are being processed, I am obviously not able to visit xyz.com as well (being another request). However, if I open it in another browser (Safari), it works well. That's because it's another session?
Possible Causes
The server has some security in place?
I am more of a coder but if someone can give pointers, I can check the server (VPS) to see if apache/php is misconfigured.
I am not handling sessions correctly?
Does it matter? Shouldn't the second request be processed as a different thread?
Aliens are controlling my VPS.
Ok, kidding. I am just tired of tracing this issue. Would really appreciate if anyone has any clue what could be going wrong here.
Your server is using file based sessions. When one request is pending, the session file is locked and the server will queue any later requests trying to use the same session.
Switch to database-stored sessions and you'll be fine.
Try to call session_write_close() in page1.php after you finished session check code.
/* session check code here */
session_write_close();
echo "Hello";
flush();
This should close the session file (remove the lock) and page2.php can open it.
http://php.net/manual/en/function.session-write-close.php
check your network.http.max-persistent-connections-per-server and network.http.max-connections-per-server.
Never encountered anything like that. Are you sure you're not using CGI instead of FastCGI? Also check how many php processes your webserver spawns and how many requests they can handle each.
To help with testing you might want to put sleep function in test-page.
Dont know for php, but with asp.net, the session object cannot be used in read/write by more than one thread at a time. So it produced something like you describe. The second request wait for the session state to be "unlocked" to start processing. It does makes sense, that way the session object is consistent for all the processing of the request.
Just want to know if anyone have the same problem.
The website need to login to perform certain task. We use stock Auth component to do the job.
Everything is fine until it hits an interface which build in Flash. Talking to Amf seems fine. But when the Flash player try to talk to other controller - got redirect because the session in not presented.
So basically when a user login - I need to somehow find a way to login the Flash player in as well.
ADDITION:
This only solve half of the problem.
Backtrack a little bit. How the Auth components evaluate the requester?
If the Session.checkAgent is true. They check if its the last one. So Flash has no chance they have a different Agent string.
OK now - Auth check them out - what? The Session cookie they store earlier ... so fail again.
UPDATE
Thanks for all the answers.
I have tried the suggested solution. Only one problem.
I am using Amf (as Cakephp Plugins) when I tried to test if the $this->params['actions'] is start with amf - it works sometime doesn't work sometime. Looking at "Charles" I can see they all call to the amf controller. Very puzzling ....
in config/core.php
try
Configure::write('Session.checkAgent', false);
It appears that if you manage to call your Session->id($sessionId) before any call to Session->read(), Session->check() or Session->write(), you don't need to bother with all the destroy old session, update userAgent and delete cookie stuff.
use this in beforeFilter action of your controllere called by flash:
if ($this->action == 'flashCalledAction') {
Configure::write('Security.level', 'medium');
//Using instead the session specified
$this->Session->destroy();
$this->Session->id($_REQUEST['sessionId']);
$this->Session->start();
// We revert to the original userAgent because starting a new session modified it
$this->Session->write('Config.userAgent', $_REQUEST['userAgent']);
// We delete the flash cookie, forcing it to restart this whole process on each request
setcookie(Configure::read('Session.cookie'), '', time() - 42000, $this->Session->path);
}
then you have to pass these 2 params in each flash call to this controller:
param: 'userAgent' -> value: '$this->Session->read('Config.userAgent')'
param: 'sessionId' -> value: $this->Session->id()
http://blogs.bigfish.tv/adam/2008/04/01/cakephp-12-sessions-and-swfupload/
This is specifically for swfUpload but the process of appending the session_id to the urls and the settings for checkAgent and session security are covered and should help point you in the right direction.
Flash doesn't send the cookie along with its requests, that's why Cake doesn't log it in. The way I do it is: you need to somehow pass $this->Session->id() along with your flash requests. That is probably the hardest part because some flash application doesn't let you tag some info along in the request. Then write a component (FlashComponent, or whatever you want to call it) that check if it's a flash request, then look for the session id in its request and set the session id. You need to include this component before 'Auth': so var $components = array('Flash','Auth',...) to intercept the request before Auth does.
Or you can set Auth->allow list, but then you will expose these actions to non-authorization, and the action won't know who the current logged in user is (unless you can pass something in the flash request, in that case, use my first solution).
one of my php page returns data like this:
<?php
//...
echo "json string";
?>
but someone else use file_get_contents() to get my data and use in other website.
can anybody tell me what can i do to prevent such thing happen.
i consider if i can get the request's domain name to echo something else.but i dont know
the function to get request's domain name.and if the request is sent by a server,that
will be unhelpful. My English is poor, to express doubts, please bear with.
you can also use sessions. if somewhere in your application, before the user gets the json data, you start a session, then in this page where you are outputting json data, you can check for the session variable. this way only users that have passed the session generator page, can view your output.
suppose you have page A.php that generates the session. use this code before outputting anything in this page.
session_start();
$_SESSION['approvedForJson'] = true;
then in your page where you are outputting json data, before outputting anything, call session_start() again. the beginning of your PHP code is a good place to call it.
then before outputting the json data, check if the session variable for approved users exists, or not.
if ( isset($_SESSION['approvedForJson']) && $_SESSION['approvedForJson'] ) {
echo "json data";
} else {
// bad request
}
You can use $_SERVER['REMOTE_ADDR'] to get the address of the client address. You can also check $_SERVER['HTTP_REFERER'] and block external requests that way, but it's less reliable. There's probably a few other techniques involving $_SERVER that you can try.
Your fighting an uphill battle here. I am assuming your serverside process that responds in json is being consumed via javascript in your users browsers... so there is no easy way to encrypt it. You might try some of the techniques used to prevent xspf (see http://en.wikipedia.org/wiki/Cross-site_request_forgery ). If you developed the client to pass along some session token that is uniq per client you could reduce some of the problem. But, chances are whoever is stealing your data is gonna figure out whatever mechanism you put in place ... assuming this is some sort of ajax type thing. If its a server-server thing then as sli mentions, setting up some restrictions based on the remote ip would help, plus setting up some sort of API authentication tokens would help even more (see oauth for some pointers)
You could also using .htaccess with apache block every external request to the page if it get's called internally or block every request that is not from your domain:
Google search thingie
EDIT
You could also use some php file which includes the file which can not be read. So for example you have file.php:
<?php
$allowedFiles[] = 'somefile.php';
$allowedFiles[] = 'someotherFile.php';
$allowedFiles[] = 'jsonReturnFile.php';
if(in_array($_GET['file'], $allowedFiles)){
include( "include/".$_GET['file'] );
}
?>
Then you can allow file_ get _contents() on that file and write a rewriteRule in your .htacces to disallow any request to the include/ folder.
RewriteRule include* - [F,NC]
That will return a 403 forbidden error for a request to that directory or any file in the directory.
Then you can do you JSON request to something like: file.php?file=jsonReturnFile.php&someothherParamReadByJsonFile=1
And when someone tries to get the file contents for the JSON file they will get the forbidden error, and getting the file contents for the include.php won't return anything usefull.