The tale of the lost session (FileReference.upload)

Feb 21, 2009

Tags: , , ,

In one of my recent projects the client wanted a user files system included in the site. For the sake of explanation basically the user logged into the system and their files would be displayed. This file manager aloud the user to upload, delete, rename and download their files stored on the server. The user's session was stored in your typical PHP session, which if you know a bit about how a session works you know the client's browser stores a cookie with their session id which is sent to the server on every request, well normally every request. I'm going to call it a bug, and so do many other users in adobe's bug report system (here, here and here to name a few). The issue is very old, some of the bugs claim the issue goes back as far as Flash Player 8 plus it even plagues Adobe AIR. It's a pretty good size show stopper if you have a site requiring authentication. After the jump I'll talk about two of the work arounds I found to work well with a PHP server side.

My first method to resolve this issue may not be as reliable as the last one but it works and is good if you want a quick inline fix. As I said before this is based off using PHP server side scripts and a traditional session. But I do know that the bug does effect ASP.net and jsp.

First thing I did was write a JavaScript function to get the PHPSESSID cookie from document.cookie sense ActionScript 3 it self cannot nativity access browser cookies this was the quickest method. This method is supposed to be called using ExternalInterface.call in ActionScript, basically it searches through document.cookie for the last index of "PHPSESSID=" which will give you the start of the PHPSESSID cookie data. I know I could have gotten way with using indexOf because the cookie should be over ridden everytime a new one is set, but why not be safe and look for the last index. The next thing that this function does is find the next index of ";" which is the seperating character for cookies in document.cookie. Next the function checks to see if the end marker is -1 which means that PHPSESSID is eather the last cookie or the only cookie set. If it is not -1 then I use the end marker and the start marker to get the value of the PHPSESSID cookie. If it is -1 then I substitute the end marker with the lenth of document.cookie. Then after all that I simply return the value.

JavaScript:

function getPHPSessionId() {
    var start=document.cookie.lastIndexOf('PHPSESSID=');
    var cookies=document.cookie.substr(start+10,document.cookie.length);
    var end=cookies.indexOf(';');

    if(end!=-1) {
    	return cookies.substr(0,end);
    }else {
    	return document.cookie.substr(start,document.cookie.length);
    }
}

In your code you simply have to add the PHP Session id to the url when you build your URLRequest just like the code bellow. I used the "s" to store my PHP Session id but you can use what ever you like. Then make sure you set the request method to post I don't think you have to for uploads I imagine FileReference.upload() does it anyways. After that just call your file reference instance's upload method passing in the URLRequest you just created.

ActionScript 3

var request:URLRequest=new URLRequest('fileHandler.php?s='+ExternalInterface.call('getPHPSessionId'));
request.method=URLRequestMethod.POST;

fileReference.upload(request);


Lastly in your PHP handler file you'd put a check to see if your url parameter exists in the $_REQUEST array and pass it into session_id(). This will tell PHP to use the session id you set in your url. Then call session_start(), the order of this is very important because you cannot use session_id() to set the session id after the session has been started. I used $_REQUEST in this example to ensure that you get the url parameter you set, although if ActionScript 3 handles it correctly it should also reside in the $_GET superglobal as well.

PHP:

if(array_key_exists('s', $_REQUEST)) {
    session_id($_REQUEST['s']);
}

session_start();

Alternative method for AMF:

For anyone using AMF to communicate with your PHP backend you might be asking why would I go through all the trouble to do it that way when I could just get the information from the server directly. Well your right, in the end I didn't end up using this method in another project I've been working on that allows users to upload their avatar to the server. I'm not going to tell you how to create an AMF server or connect to one for that matter but this simple method bellow will get the session id for you. It's mostly a wrapper for calling session_id() again but this time it doesn't pass any information into the function it just gets the session id and returns it.

PHP:

function getSessionId() {
    return session_id();
}

One final note I'd advise against using session_regenerate_id() when your request is to upload a file, just because I cannot confirm whether or not the PHPSESSID cookie is actually sent back through FileReference.upload() or even if it is whether the new id is being set in the browser.

Edit: Thanks to Mark for spotting my js error, should work much better now.

Posted in: Articles |