PHP Session conflicts with AJAX

code speaks a thousand words

page.php?id=123

<?php
if(is_ajax()){// function that determines whether the request is from ajax (http header stuff)
$_SESSION['token'] = md5(rand());
}
//some ajax request to ajax.php?id=123
?>

ajax.php?id=123

<?php
if($_SESSION['token'] == $_GET['token']){
echo 'Tell me this is for reall';
}else{
echo 'Invalid Request';
}
?>

Every thing works fine until the user opens page.php?id=456 on another tab, the ajax returns 'invalid request' on page.php?id=123 How to resolve this conflict?

ps: if its possible i want new session for each page for CSRF purposes

Asked By: kornesh
||

Answer #1:

If I understand this correctly, it sounds like you're setting the token for each request. My guess would be that the old page still has the old token. I would check to see if the token is set before automatically blowing it away.

 if (isset($_SESSION['token'])){
    //do nothing
 } else{
   $_SESSION['token'] = md5(rand());
 }

Edit To address your question.

Rather than just using one key of "token," create a key for each browser session.

$_SESSION[$sessionId] = md5(rand());

The trick, of course will be figuring out when those happen because if you can't use session, you really won't know whether the request is coming from a new tab or an old one. You could the use the query string to pass this parameter around. Basically all requests would have to have this parameter otherwise you don't associate a session with them.

e.g.

http://www.yoursite.com/somepage.php?sessionid=<some generated id>

Ultimately, the user could monkey with this but I'm not sure there's any way around that.

Edit 2 Ok, here's my thought for how you should do this. Security experts out there, feel free to flame me if I have this wrong, like I said before I'm no expert, but it doesn't look like I'm going to get out of this without suggesting something ;-)

The issue with CSFR is that some malicious user, Bob, could create an element on another site that causes Alice's browser to make a request to some other site and, because Alice had previously logged in and that information is stored either as a cookie or Alice is recognized via session, the site executes the request as if Alice had requested it. For instance, if Alice's bank is http://www.mybank.com, then Bob could create a forum post that contains

<img srg="http://www.mybank.com/transferfunds.php?amount=1000&receiver=Bob" />

Alice's bank would recognize her browser as making the request, thinking it's her. There are a couple of key things that have to happen (together, either of these failing will cause the attack to fail) to make this a viable attack (these are the key ones to understanding how to prevent it):

  1. Alice has to have logged into her bank site such that the bank remembers her. This could happen either in a cookie ("remember me") or via session. However, if she closes her browser (ends the session) or clears her cookies, there's no threat because the bank site won't recognize her and will deny the request.
  2. Bob has to be able to supply all of the necessary parameters to the request, otherwise the bank website will deny the request.

In order to provide some notion of "state" on top of a stateless protocol (HTTP), you really can't get around the risk in (1). Unless you get people to always click "log out" or close their window etc, there's nothing you can do get around storing information in the browser or session. However, you can prevent (2) from being a problem. My solution to this (and I'm sure there are tons of others) is to generate a hash, like you're doing and store it in session.

For instance,

$_SESSION['token'] = md5(rand());

Then, what you do is append that token to all of your internal links.

http://www.mysite.com/secure.php?token=giuwnrefviunslfghahgliuwnvwrgbaasd

You NEVER store that token in browser memory: i.e. a cookie. When requests are made, before you do anything, you check the token

//note, you'll want to sanitize user input, I'm just being brief
if ($_GET['token'] != $_SESSION['token']){
   //User either attempted to enter a link on their own or it's a CSRF attack
   header('HTTP/1.1 403 Forbidden');
 }else{
 //do whatever needs to be done
 }

The key to this is that all of the links on your site will include the token. However, Bob has no way of knowing what that token is because it's not stored in a cookie on the browser. If he attempts to craft a link to one of your pages, it will either contain the wrong key, or it won't contain any key and you can deny it. (To be fair, there is a chance he could correctly guess the token for a specific user that happens to view his code, but he's probably more likely to burst into flames.)

There's no need to assign a timeout to the token either as the token will be obliterated when the browser is closed and need to be regenerated when the user visits the site.

Answered By: kornesh

Answer #2:

What are you trying to do? Can we see the JavaScript?

It looks like you set the session token to a random hash, and then later you check it. So if the user opens page twice, it gets randomized again and invalidates the previous token. What behaviour are you expecting?

Answered By: Chris Thompson

Answer #3:

You need to have each page start it's own token, otherwise this conflict will occur.

<?php
if(is_ajax()){// function that determines whether the request is from ajax (http header stuff)
$page_id = $_GET['id'];
$_SESSION['token'][$page_id] = md5(rand());
}
//some ajax request to ajax.php?id=123
?>

and

<?php
$page_id = $_GET['id'];
if($_SESSION['token'][$page_id] == $_GET['token']){
echo 'This is for real!';
}else{
echo 'Invalid Request';
}
?>

Depending on exactly how you are implementing everything you will probably need something more complicated or specialized, but this should get you started.

Answered By: mpen
The answers/resolutions are collected from stackoverflow, are licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0 .



# More Articles