Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CORS preflight request returning "403 Forbidden"; subsequent request then only sending in Chrome

After failure using pluploader in this question, I'm now trying FineUploader.

After reading up on CORS, I've implemented various headers on my IIS6 server.

What seems to happen is that my script fires the first (preflight) authorisation request, which fails, but Chrome allows the second (standard) request to send anyway - Firefox does not. I presume this is actually a bug on behalf of Chrome, but at least it has allowed me to work out that my script is probably working correctly.

Here is the first (preflight) request as seen in Chrome and FF:

OPTIONS /frog/LOTS/upload/php.php HTTP/1.1
Host: staff.curriculum.local
User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:14.0) Gecko/20100101 Firefox/14.0.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Origin: http://frogserver.curriculum.local
Access-Control-Request-Method: POST
Access-Control-Request-Headers: cache-control,x-requested-with
Pragma: no-cache
Cache-Control: no-cache

The Access-Control... headers are those that I've added to IIS.

And here are my response headers:

HTTP/1.1 403 Forbidden
Content-Length: 1758
Content-Type: text/html
Server: Microsoft-IIS/6.0
x-powered-by: ASP.NET
Access-Control-Allow-Origin: http://frogserver.curriculum.local
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, Cache-Control
Access-Control-Allow-Methods: OPTIONS, GET, POST
Access-Control-Expose-Headers: Origin, X-Requested-With
Date: Mon, 22 Apr 2013 15:19:20 GMT

I've tried to compare the two side by side but I can't find any missing headers which would cause the preflight request to return a 403 Forbidden error.

403 Forbidden

I haven't included my PHP source as it's a lot of code. Suffice to say that it does work in Chrome and that the file is correctly uploaded, so the script should be correct. The only thing which may be worth mentioning is that I've got a header("Content-Type: text/plain"); at the start of my script. Changing that to text/html makes no difference to Chrome nor FireFox.

The JavaScript is quite straightforward:

$('#jquery-wrapped-fine-uploader').fineUploader({
    request: {
        endpoint: 'http://staff.curriculum.local/frog/LOTS/upload/php.php'
    },
    cors: {
        expected: true, //all requests are expected to be cross-domain requests
        sendCredentials: true //if you want cookies to be sent along with the request
    }
});

Can anyone help? I've spent literally 8 hours on this single problem today and I'm >< close to ripping my own face off....!!

Thanks in advance,

like image 349
turbonerd Avatar asked Apr 22 '13 15:04

turbonerd


2 Answers

As mentioned in my comments, this appears to be an issue with your server. For some reason, it is rejecting the initial OPTIONS request. You will need to look at your server logs to see why your server is responding to this request with a 403.

The user agent sends this initial OPTIONS (pre-flight) request. Fine Uploader does not send this request directly, the user agent sends it to be in compliance with the CORS spec. If you have specific questions about CORS, you can see my blog post on how Fine Uploader handles CORS, or/and you can read this excellent MDN article on CORS.

like image 161
Ray Nicholus Avatar answered Oct 14 '22 12:10

Ray Nicholus


It's taken me a week, but I've finally found the problem.

By default, IIS6 does not support the OPTIONS verb on .php files (or .asp(x) for that matter).

As such, it wasn't recognising the OPTIONS preflight call at all.

To change this value in IIS6, follow these steps:

  1. In the IIS Manager, go to your root web site directory. Right-click it and select "Properties"
  2. Go to the Home Directory tab, then select the "Configuration" button at the bottom
  3. Find the relevant file extension of the script you're trying to communicate with, such as .php or .asp and click "edit"
  4. Add OPTIONS to the list of available verbs (should now display something like REQUEST, GET, POST, OPTIONS)
  5. Add the code below to your PHP script to determine responses from IE

I couldn't get Internet Explorer working without the following code in my PHP script:

/* Is the request from Internet Explorer? */
if( !isset( $_SERVER['HTTP_X_REQUESTED_WITH'] )
    || ( isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] != "XMLHttpRequest" ) ) {

    /* If so, we need to send a UUID and iframe XSS response script... */
    header("Content-Type: text/html");

    /* This needs some extra security, for sure */
    if( $result["success"] == "true" )
        $result["uuid"] = $_POST["qquuid"];

    echo htmlspecialchars(json_encode($result), ENT_NOQUOTES);
    echo "<script src='iframe.xss.response-3.4.1.js'></script>";
} else {
    /* Otherwise, we can just echo the json'd result */
    echo htmlspecialchars(json_encode($result), ENT_NOQUOTES);
}

I've given Ray Nicholus the 50 point bounty as although I didn't find his manner particularly helpful, he was right all along. However, for purposes of others viewing this post with a similar issue, I'll mark my answer as correct.

like image 44
turbonerd Avatar answered Oct 14 '22 12:10

turbonerd