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

36,534

Solution 1

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.

Solution 2

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.

Share:
36,534
turbonerd
Author by

turbonerd

Senior DevOps Engineer working with AWS, Kubernetes and CircleCI.

Updated on July 17, 2022

Comments

  • turbonerd
    turbonerd almost 2 years

    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,

    • Ray Nicholus
      Ray Nicholus about 11 years
      This really doesn't seem to be a Fine Uploader issue to me. You might want to change one of your tags to IIS. Fine Uploader is not involved (much) here at all. The user agent sends the OPTIONS (preflight) request, not Fine Uploader. For some reason, your server is rejecting this request with a 403. You might want to take a look at your server logs to see why it is responding with 403.
    • Bigood
      Bigood about 11 years
      Once again, +1 for the well asked question!
    • turbonerd
      turbonerd about 11 years
      @RayNicholus you're correct, it's not an issue with Fine Uploader, but I think it'd be wrong to say that it isn't relevant. Does Fine Uploader determine the headers that the preflight request sends?
    • Ray Nicholus
      Ray Nicholus about 11 years
      @dunc No, Fine Uploader has no involvement in the preflight request. It is handled completely by the user agent.
    • turbonerd
      turbonerd about 11 years
      OK @RayNicholus, thanks. Do you know, then, what determines the headers for the preflight request?
    • Ray Nicholus
      Ray Nicholus about 11 years
      @dunc Are you asking about the Access-Control-Request-Headers value? Any non-standard headers in the proposed CORS request will be in this header field. This is one thing that triggers the UA to pre-flight the request (non-standard headers in a CORS request). I wrote a blog post on CORS support in Fine Uploader. I have also linked to an MDN article that talks about CORS in general in some more detail.
    • inf3rno
      inf3rno about 11 years
      Does your IIS serve OPTIONS request by non CORS requests? stackoverflow.com/questions/6656354/… I have a similar application with IIS7 without credentials, I checked your headers, these are the difference: Access-Control-Allow-Credentials, Access-Control-Allow-Headers, Access-Control-Expose-Headers, Access-Control-Request-Headers, so if the problem is with the headers you should check these... You should upgrade to IIS7 I think if it is a dev server, I have no problems with OPTIONS requests, but I never needed credentials.
    • turbonerd
      turbonerd about 11 years
      Thanks @inf3rno you were along the right lines - I've just posted the answer.
  • turbonerd
    turbonerd about 11 years
    I have read both your blog and your article, but neither helps me rectify this problem. From what I can see in the requests/responses according to my browser, the preflight request should work - the header and method are both allowed by the server.
  • Ray Nicholus
    Ray Nicholus about 11 years
    @dunc Is your server expecting some cookies to be sent along with the request in order to authenticate the request? If so, this is your problem. Preflight requests will not include cookies, per the spec.
  • Ray Nicholus
    Ray Nicholus about 11 years
  • turbonerd
    turbonerd about 11 years
    Hi again Ray. I've tried setting sendCredentials to false and removing the credentials header from IIS, but I'm getting exactly the same results.
  • Ray Nicholus
    Ray Nicholus about 11 years
    @dunc it doesn't sound like you have understood my last comment. Let me rephrase. My guess is that your server is expecting credentials to be sent with the options request. This will never happen, per the spec. This would perhaps explain the 403.
  • turbonerd
    turbonerd about 11 years
    Hm, you're right then, I don't. What determines whether the server expects credentials to be sent?
  • turbonerd
    turbonerd about 11 years
    The code is nothing other than that provided by Fine Uploader, Ray. I haven't amended either that JavaScript or the PHP at this point. I've used the exact php.php file bundled in the server-side examples on your GitHub page. As such, I have literally no clue where to begin.
  • Ray Nicholus
    Ray Nicholus about 11 years
    Since you had the same exact issue pre-Fine Uploader, perhaps there is something server-side that your are overlooking. There's no question that your server is rejecting the OPTIONS request. I'm not sure anyone can give you any additional suggestions. You will need to figure out what code or settings on your server are rejecting this request, but the credential-related suggestion I have given seems like a logical explanation.
  • turbonerd
    turbonerd about 11 years
    I'm sure someone that understands CORS (and potentially PHP, if it's a server-side issue) could point me in the direction of where the code may be going wrong. The code written in my previous question was also a pre-written file bundled with pluploader and I'm sure that both it and Fine Uploader's PHP script worked correctly on other systems... so what could be the difference between those systems and my own? That's what I'm now looking for help with.
  • Ray Nicholus
    Ray Nicholus about 11 years
    I understand CORS pretty well. This is, without a doubt, a server-side issue. I'm not sure what other advice can be offered. It seems like a simple problem. Your server is rejecting the OPTIONS request. You are simply going to have to spend some time and follow the request as it hits your server and figure out why your server is responding with a 403.
  • inf3rno
    inf3rno about 11 years
    Ye, but this issue is a duplicate, as I linked before: stackoverflow.com/questions/6656354/… Only MSIE 10+ supports CORS, that's why it is not working in IE... Below that version you have only an useless XDomainRequest with very limited functions...
  • turbonerd
    turbonerd about 11 years
    You posted your link, seemingly, as I was writing my answer out.. :) I'm sure you're right, but the file upload cross-domain is now working in every browser with FineUploader, so I'm happy.
  • Ray Nicholus
    Ray Nicholus about 11 years
    @inf3rno This issue is not similar to the issue you have linked to. Fine Uploader doesn't use xhr or xdomain request for file uploading in ie9 or older. Instead, a form inside a hidden iframe is submitted. Message passing is used to get around the cross domain issue when attempting to parse the response client side. This does mean that cors requests for uploads are not supported in ie7 and older though.
  • PAA
    PAA over 8 years
    Could anyone please respond on this error stackoverflow.com/questions/32725249/…? I really need help