views:

41

answers:

1

Hello,

1st the background - so that you see, that I'm not trying anything malicious: I have a flash app in the russian social network vkontakte.ru which displays user avatars. Until now my .swf file has been hosted at their domain, so that fetching and scaling avatars worked well.

Now I'd like to switch my app to an iframe-type, so that the .swf will be hosted at my domain and thus I'm not able to scale the avatars in my .swf anymore: neither my domain, nor "*" are listed in http://vkontakte.ru/crossdomain.xml and thus the .swf can download and display the avatars, but can't scale them anymore (accessing myLoader.content throws SecurityError).

I've decided to write a proxy script in PHP which would fetch the image specified in scripts ?img= parameter and then just pass it to the stdout (after some checks and without storing anything):

<?php

define('MAX_SIZE', 1024 * 1024);

$img = $_GET['img'];
if (strpos($img, '..') !== false ||
    !preg_match(',^http://[\w.]*vkontakte\.ru/[\w./?]+$,i', $img))
        exit();

$opts = array(
        'http'=>array(
                'method' => 'GET',
                'header' => "Accept-language: en\r\n" .
                            "Cookie: foo=bar\r\n"
        )
);

$ctx = stream_context_create($opts);
stream_context_set_params($ctx, array('notification' => 'callback'));
$fp = fopen($img, 'r', false, $ctx);
fpassthru($fp);
fclose($fp);

function callback($code, $severity, $message, $message_code, $bytes_transferred, $bytes_max) {
        if ($code == STREAM_NOTIFY_FILE_SIZE_IS && $bytes_max > MAX_SIZE)
                exit();

        if ($code == STREAM_NOTIFY_PROGRESS && $bytes_transferred > MAX_SIZE)
                exit();

        if ($code == STREAM_NOTIFY_MIME_TYPE_IS) {
                $mime = strtolower($message);
                switch($message) {
                        case 'image/gif':
                        case 'image/png':
                        case 'image/jpg':
                        case 'image/jpeg':
                                // XXX this doesn't work XXX
                                header('Content-Type: ' . $mime);
                                break;
                        default:
                                exit();
                }
        }
}

?>

My problem is that the $mime header is never printed (or is printed too late?).

When I fetch for example my avatar directly: http://cs971.vkontakte.ru/u59751265/a_7567890a.jpg then I see the Content-Type: image/jpeg header being sent to the browser.

But when I fetch it through my proxy script, I don't see that header.

Maybe I should better use a different function instead of fopen()? I'm not very proficient in PHP. Also I'm worried if fopen() can be tricked into serving local files from my web server.

And as a bonus question: I'm worried that my .swf will be not the only app calling my proxy.php, but I can't figure out a good way to secure it (maybe there is no such way) - I can't store a secret in my .swf and in the .php - because the .swf can be disassembled.

Thank you, Alex

A: 

I believe your first assumption is right, you send the header too late or you are not getting the mime headers you expect.

First of all try logging the mimes using file_put_contents, then if you find out that it is indeed too late to be of any use, you can look at output buffering.

If however you dont get to the header at all, you might be in the wrong context, HTTP perhaps?

For your bonus question; the page of the iframe is hosted on a server, on that server you are probably running PHP, create a session with session_start(), set $_SESSION['gotvisit'] = TRUE (or something like that). Then retrieve the session id using $id = session_id(), this id you can now pass to your flash with standard flash variables. Have flash pass that variable in the request for the image, next up in your img script, do;

session_id($_GET['ses']);
session_start();
if(!isset($_SESSION['gotvisit']))
    die('no access');

Good luck to you.

Kristoffer S Hansen