Flash FileReference loose cookies during upload issue (1)

This is a very famous issue since 2005:  Flash FileReference loose cookies during upload under Firefox/Chrome/Safari. Exists in Windows and Mac OS.

Refer: https://bugs.adobe.com/jira/browse/FP-78

Root Cause: These browsers not provide API for Flash to get session cookies while Microsoft IE support it.

First I will give solutions for PHP applications. Then I will give Java EE version in the following articles.

PHP: very easy to verify it, a very simple test page

<?php
  // initialize a session
  session_start();
                    
  // print value
  $_SESSION['animal'] = 'cat';

  echo '<object width="550" height="400">';
  echo '<param name="movie" value="uploadTest.swf">';
  echo '<embed src="uploadTest.swf" width="550" height="400">';
  echo '</embed>';
  echo '</object>';
?>
dispatcher.addEventListener(Event.SELECT, selectHandler);

function selectHandler(event:Event):void
{
    var file:FileReference = FileReference(event.target);
    file.upload(kUrlReq);
}
<?php
session_start();
if ($_SESSION['animal'] != 'cat')
{
    header("HTTP/1.1 403 Forbidden");
    exit();
}
else
{
    header("HTTP/1.1 200 OK");
    exit();
}
?> 

Then it will always go to "HTTP/1.1 403 Forbidden" which lead to Error #2038 in Flash, means that Flash loose session cookies during upload.

How to resolve it? We can’t wait for browsers add support for Flash. So we can get PHPSESSID first, then pass it to server side via GET/POST.

How to get PHPSESSID cookie value using ActionScript3? You can use JavaScript with the help of ExternalInterface:

function getCookie(name:String):String
{
    name = name.toLowerCase();
    var cookies:String = ExternalInterface.call("eval","document.cookie");
    var cookies_array:Array = cookies.split(";");
    if (cookies_array!=null)
    {
        for (var i:int=0;i<cookies_array.length;i++)
        {
            var cookie:String = cookies_array[i] as String;
            if (cookie!=null)
            {
                var NameValue:Array = cookie.split("=");
                if (NameValue!=null && NameValue.length==2)
                {
                    var cookie_name:String = NameValue[0] as String;    
                    var cookie_value:String = NameValue[1] as String;
                    cookie_name = cookie_name.toLowerCase();
                    cookie_name = trim(cookie_name);
                    cookie_value = trim(cookie_value);
                    if (cookie_name==name)
                    {
                        return cookie_value;
                    }
                }
            }                
        }    
    }
    return null;
}
        
function trim(value:String):String
{
    var start:int = 0;
    var end:int = value.length;
    var i:int;
            
    for (i=0;i<value.length;i++)
    {
        if (value.charAt(i)!=" ")
        break;    
    }
    start = i;
    for (i=value.length-1;i>=0;i--)
    {
        if (value.charAt(i)!=" ")
        break;    
    }
    end = i+1;
    if (end<start)
    return "";
    var result:String = value.substring(start,end);
    return result;
}

Then you can pass this value to server side:

function selectHandler(event:Event):void
{
    var phpCookie:String = getCookie("PHPSESSID");
    kUrlReq.url += "?SID="+phpCookie;
    var file:FileReference = FileReference(event.target);
    file.upload(kUrlReq);
}

Then you add this line before session start:

Session_id($_GET['SID']);
session_start();

It works in any SSO PHP applications now. And you can also get the $_SESSION[‘animal’].

The other workaround is as follows, but with this method you won’t be able to show the upload progress:

1 – open the file with filereference

2 – get the file data into a bytearray

3 – encode the bytearray with base 64 encoder

4 – send the file via a post variable to your server

How does cookie and session works in PHP?

可以用 setcookie() 或 setrawcookie() 函数来设置 cookie. 也可以通过向客户端直接发送http头来设置: header("Set-Cookie:name=$value");.

cookie工作机理:

a) 服务器通过随着响应发送一个HTTP的Set-Cookie头,在客户机中设置一个cookie(多个cookie要多个头).

b) 客户端自动向服务器端发送一个HTTP的cookie头,服务器接收读取.

HTTP/1.x 200 OK
X-Powered-By: PHP/5.2.1
Set-Cookie: TestCookie=something from somewhere; path=/
Expires: Thu, 19 Nov 2007 18:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-type: text/html

收到Set-Cookie: TestCookie=something from somewhere; path=/

浏览器将在客户端的磁盘上创建一个cookie文件,并在里面写入:

TestCookie=something from somewhere;

这一行就是我们用setcookie(‘TestCookie’,’something from somewhere’,’/’);的结果.也就是用header(‘Set-Cookie: TestCookie=something from somewhere; path=/’);的结果.

session工作机理:

session使用过期时间设为0的cookie,并且将一个称为session ID的唯一标识符(一长串字符串),在服务器端同步生成一些session文件(可以自己定义session的保存类型),与用户机关联起来.web应用程序存贮与这些session相关的数据,并且让数据随着用户在页面之间传递.

访问网站的来客会被分配一个唯一的标识符,即所谓的会话 ID。它要么存放在客户端的 cookie,要么经由 URL 传递。

会话支持允许用户注册任意数目的变量并保留给各个请求使用。当来客访问网站时,PHP 会自动(如果 session.auto_start 被设为 1)或在用户请求时(由 session_start() 明确调用或 session_register() 暗中调用)检查请求中是否发送了特定的会话 ID。如果是,则之前保存的环境就被重建。

使用session_start()调用session,服务器端在生成session文件的同时,生成session ID哈希值和默认值为PHPSESSID的session name,并向客户端发送变量为(默认的是)PHPSESSID(session name),值为一个128位的哈希值.服务器端将通过该cookie与客户端进行交互.

session变量的值经php内部序列化后保存在服务器机器上的文本文件中,和客户端的变量名默认情况下为PHPSESSID的cookie进行对应交互.即服务器自动发送了http头:header(‘Set-Cookie: session_name()=session_id(); path=/’);即setcookie(session_name(),session_id());

当从该页跳转到的新页面并调用session_start()后,PHP将检查与给定ID相关联的服务器端存贮的session数据,如果没找到,则新建一个数据集.

Refer: http://www.phpweblog.net/fuyongjie/archive/2008/09/18/5808.html

发表评论

电子邮件地址不会被公开。 必填项已用*标注