Default value for Access-Control-Allow-Methods - http

I just learned about the Access-Control-Allow-Methods header, e.g.
Access-Control-Allow-Methods: OPTIONS, HEAD, GET
I have never used this header (just Access-Control-Allow-Origin), but I have gotten CORS to work in the past.
Is the default to allow all methods, or have I gotten lucky with undefined behavior?

The Access-Control-Allow-Methods header indicates which HTTP methods are allowed on a particular endpoint for cross-origin requests. If you allow all HTTP methods, then its ok to set the value to something like Access-Control-Allow-Methods: GET, PUT, POST, DELETE, HEAD. However, if you want to limit the endpoint to only a few methods, you should only include those methods.
As to why you haven't been seeing this before, this header is only used on CORS preflight requests. Maybe your application didn't use CORS preflight, and then something changed to trigger a preflight. Does your application use any HTTP methods other than GET/POST, or any custom HTTP headers?
You can learn more about CORS preflight requests here: http://www.html5rocks.com/en/tutorials/cors/

The default of Access-Control-Allow-Methods is to allow through all simple methods, even on preflight requests. As the flow on https://www.w3.org/TR/cors/#preflight-request says (step 7 of successful preflight request):
If request method is not a case-sensitive match for any method in methods and is not a simple method, apply the cache and network error steps.
And the definition of simple method is:
A method is said to be a simple method if it is a case-sensitive match for one of the following: GET HEAD POST
So if you have a preflighted POST request (due to a custom HTTP header, say), and do not send a Access-Control-Allow-Methods response header, the request will still go ahead okay.

Related

HTTP Headers: Access-Control-Allow-Methods VS Allow

What is the main difference between these two headers?
Access-Control-Allow-Methods is located in headers collection of the request while Allow can be found inside of Content.Headers collection.
Which one I should care about while handling OPTIONS requests?
Allow is a basic HTTP header which is used to describe which HTTP methods may be used to request a resource. This is in general and not specifically for JS. The header predates the existence of JS.
Access-Control-Allow-Headers is a CORS extension to HTTP which describes which HTTP methods may be used by client-side code to make cross-origin requests to a resource.
You must include an Allow header if you are making a 405 response. You may always include it.
You need to include Access-Control-Allow-Headers if you are making a response to a preflight OPTIONS request (unless you don't want to use it to grant the follow-up request any permissions).

Fetch API, custom request headers, CORS, and cross-origin redirects

I need to make an HTTP GET request with custom request headers in-browser and process the result as it streams in. The Fetch API is ideal for this:
fetch('https://example.com/resource', {
method: 'GET',
headers: {
'X-Brad-Test': 'true'
},
cache: 'no-store',
mode: 'cors'
}).then((res) => {
const reader = res.body.getReader();
// etc.
});
This works quite well. Since there are custom headers, the browser pre-flights the request with an OPTIONS request to /resource. I have configured my server to respond with a 204 No Content and the following headers:
Access-Control-Allow-Headers: X-Requested-With, Range, If-Range, X-Brad-Test
Access-Control-Allow-Origin: *
The browser is happy with this, then makes a GET request, the server returns a 200 OK with the data, and the browser allows me to access the response headers and body.
The problem comes in when there is a redirect. The OPTIONS request succeeds with the 204 No Content and the same headers as before. The browser makes the correct GET request, and on the server I send a 302 with a Location: header. Chrome throws the following error:
Fetch API cannot load https://example.com/resource. Redirect from 'https://example.com/resource' to 'http://some-other-origin/resource' has been blocked by CORS policy: Request requires preflight, which is disallowed to follow cross-origin redirect.
This was unexpected, and seems nonsensical to me. I expected the browser to follow the redirect, and do another pre-flight request for this new location, but it didn't do that.
Stranger still is that I can sort of hack around this client-side. I can make an HTTP request without my custom header, figure out where I ended up after redirects by looking at the Response object, then make a second request at the new target with my custom headers. This doesn't work in all cases of course, and I'd rather not rely on this hack. I'd rather find a proper way.
Two Questions:
What is the proper way to allow the client to follow redirects? Is there some sort of Access-Control-* header I can use?
Why does this restriction exist? What security issue is prevented by not following and running pre-flight on the followed URL?
Supporting redirects to requests that require a preflight is very recent change to Fetch (which defines CORS).
https://github.com/whatwg/fetch/commit/0d9a4db8bc02251cc9e391543bb3c1322fb882f2
I believe some implementations have started adjusting their implementations, but this will take some time to reach everyone.

Why is an OPTIONS request sent and can I disable it?

I am building a web API. I found whenever I use Chrome to POST, GET to my API, there is always an OPTIONS request sent before the real request, which is quite annoying. Currently, I get the server to ignore any OPTIONS requests. Now my question is what's good to send an OPTIONS request to double the server's load? Is there any way to completely stop the browser from sending OPTIONS requests?
edit 2018-09-13: added some precisions about this pre-flight request and how to avoid it at the end of this reponse.
OPTIONS requests are what we call pre-flight requests in Cross-origin resource sharing (CORS).
They are necessary when you're making requests across different origins in specific situations.
This pre-flight request is made by some browsers as a safety measure to ensure that the request being done is trusted by the server.
Meaning the server understands that the method, origin and headers being sent on the request are safe to act upon.
Your server should not ignore but handle these requests whenever you're attempting to do cross origin requests.
A good resource can be found here http://enable-cors.org/
A way to handle these to get comfortable is to ensure that for any path with OPTIONS method the server sends a response with this header
Access-Control-Allow-Origin: *
This will tell the browser that the server is willing to answer requests from any origin.
For more information on how to add CORS support to your server see the following flowchart
http://www.html5rocks.com/static/images/cors_server_flowchart.png
edit 2018-09-13
CORS OPTIONS request is triggered only in somes cases, as explained in MDN docs:
Some requests don’t trigger a CORS preflight. Those are called “simple requests” in this article, though the Fetch spec (which defines CORS) doesn’t use that term. A request that doesn’t trigger a CORS preflight—a so-called “simple request”—is one that meets all the following conditions:
The only allowed methods are:
GET
HEAD
POST
Apart from the headers set automatically by the user agent (for example, Connection, User-Agent, or any of the other headers with names defined in the Fetch spec as a “forbidden header name”), the only headers which are allowed to be manually set are those which the Fetch spec defines as being a “CORS-safelisted request-header”, which are:
Accept
Accept-Language
Content-Language
Content-Type (but note the additional requirements below)
DPR
Downlink
Save-Data
Viewport-Width
Width
The only allowed values for the Content-Type header are:
application/x-www-form-urlencoded
multipart/form-data
text/plain
No event listeners are registered on any XMLHttpRequestUpload object used in the request; these are accessed using the XMLHttpRequest.upload property.
No ReadableStream object is used in the request.
Have gone through this issue, below is my conclusion to this issue and my solution.
According to the CORS strategy (highly recommend you read about it) You can't just force the browser to stop sending OPTIONS request if it thinks it needs to.
There are two ways you can work around it:
Make sure your request is a "simple request"
Set Access-Control-Max-Age for the OPTIONS request
Simple request
A simple cross-site request is one that meets all the following conditions:
The only allowed methods are:
GET
HEAD
POST
Apart from the headers set automatically by the user agent (e.g. Connection, User-Agent, etc.), the only headers which are allowed to be manually set are:
Accept
Accept-Language
Content-Language
Content-Type
The only allowed values for the Content-Type header are:
application/x-www-form-urlencoded
multipart/form-data
text/plain
A simple request will not cause a pre-flight OPTIONS request.
Set a cache for the OPTIONS check
You can set a Access-Control-Max-Age for the OPTIONS request, so that it will not check the permission again until it is expired.
Access-Control-Max-Age gives the value in seconds for how long the response to the preflight request can be cached for without sending another preflight request.
Limitation Noted
For Chrome, the maximum seconds for Access-Control-Max-Age is 600 which is 10 minutes, according to chrome source code
Access-Control-Max-Age only works for one resource every time, for example, GET requests with same URL path but different queries will be treated as different resources. So the request to the second resource will still trigger a preflight request.
Please refer this answer on the actual need for pre-flighted OPTIONS request: CORS - What is the motivation behind introducing preflight requests?
To disable the OPTIONS request, below conditions must be satisfied for ajax request:
Request does not set custom HTTP headers like 'application/xml' or 'application/json' etc
The request method has to be one of GET, HEAD or POST. If POST, content type should be one of application/x-www-form-urlencoded, multipart/form-data, or text/plain
Reference:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
When you have the debug console open and the Disable Cache option turned on, preflight requests will always be sent (i.e. before each and every request). if you don't disable the cache, a pre-flight request will be sent only once (per server)
Yes it's possible to avoid options request. Options request is a preflight request when you send (post) any data to another domain. It's a browser security issue. But we can use another technology: iframe transport layer. I strongly recommend you forget about any CORS configuration and use readymade solution and it will work anywhere.
Take a look here:
https://github.com/jpillora/xdomain
And working example:
http://jpillora.com/xdomain/
For a developer who understands the reason it exists but needs to access an API that doesn't handle OPTIONS calls without auth, I need a temporary answer so I can develop locally until the API owner adds proper SPA CORS support or I get a proxy API up and running.
I found you can disable CORS in Safari and Chrome on a Mac.
Disable same origin policy in Chrome
Chrome: Quit Chrome, open an terminal and paste this command: open /Applications/Google\ Chrome.app --args --disable-web-security --user-data-dir
Safari: Disabling same-origin policy in Safari
If you want to disable the same-origin policy on Safari (I have 9.1.1), then you only need to enable the developer menu, and select "Disable Cross-Origin Restrictions" from the develop menu.
As mentioned in previous posts already, OPTIONS requests are there for a reason. If you have an issue with large response times from your server (e.g. overseas connection) you can also have your browser cache the preflight requests.
Have your server reply with the Access-Control-Max-Age header and for requests that go to the same endpoint the preflight request will have been cached and not occur anymore.
I have solved this problem like.
if($_SERVER['REQUEST_METHOD'] == 'OPTIONS' && ENV == 'devel') {
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Headers: X-Requested-With');
header("HTTP/1.1 200 OK");
die();
}
It is only for development. With this I am waiting 9ms and 500ms and not 8s and 500ms. I can do that because production JS app will be on the same machine as production so there will be no OPTIONS but development is my local.
You can't but you could avoid CORS using JSONP.
you can also use a API Manager (like Open Sources Gravitee.io) to prevent CORS issues between frontend app and backend services by manipulating headers in preflight.
Header used in response to a preflight request to indicate which HTTP headers can be used when making the actual request :
content-type
access-control-allow-header
authorization
x-requested-with
and specify the "allow-origin" = localhost:4200 for example
After spending a whole day and a half trying to work through a similar problem I found it had to do with IIS.
My Web API project was set up as follows:
// WebApiConfig.cs
public static void Register(HttpConfiguration config)
{
var cors = new EnableCorsAttribute("*", "*", "*");
config.EnableCors(cors);
//...
}
I did not have CORS specific config options in the web.config > system.webServer node like I have seen in so many posts
No CORS specific code in the global.asax or in the controller as a decorator
The problem was the app pool settings.
The managed pipeline mode was set to classic (changed it to integrated) and the Identity was set to Network Service (changed it to ApplicationPoolIdentity)
Changing those settings (and refreshing the app pool) fixed it for me.
OPTIONS request is a feature of web browsers, so it's not easy to disable it. But I found a way to redirect it away with proxy. It's useful in case that the service endpoint just cannot handle CORS/OPTIONS yet, maybe still under development, or mal-configured.
Steps:
Setup a reverse proxy for such requests with tools of choice (nginx, YARP, ...)
Create an endpoint just to handle the OPTIONS request. It might be easier to create a normal empty endpoint, and make sure it handles CORS well.
Configure two sets of rules for the proxy. One is to route all OPTIONS requests to the dummy endpoint above. Another to route all other requests to actual endpoint in question.
Update the web site to use proxy instead.
Basically this approach is to cheat browser that OPTIONS request works. Considering CORS is not to enhance security, but to relax the same-origin policy, I hope this trick could work for a while. :)
One solution I have used in the past - lets say your site is on mydomain.com, and you need to make an ajax request to foreigndomain.com
Configure an IIS rewrite from your domain to the foreign domain - e.g.
<rewrite>
<rules>
<rule name="ForeignRewrite" stopProcessing="true">
<match url="^api/v1/(.*)$" />
<action type="Rewrite" url="https://foreigndomain.com/{R:1}" />
</rule>
</rules>
</rewrite>
on your mydomain.com site - you can then make a same origin request, and there's no need for any options request :)
It can be solved in case of use of a proxy that intercept the request and write the appropriate headers.
In the particular case of Varnish these would be the rules:
if (req.http.host == "CUSTOM_URL" ) {
set resp.http.Access-Control-Allow-Origin = "*";
if (req.method == "OPTIONS") {
set resp.http.Access-Control-Max-Age = "1728000";
set resp.http.Access-Control-Allow-Methods = "GET, POST, PUT, DELETE, PATCH, OPTIONS";
set resp.http.Access-Control-Allow-Headers = "Authorization,Content-Type,Accept,Origin,User-Agent,DNT,Cache-Control,X-Mx-ReqToken,Keep-Alive,X-Requested-With,If-Modified-Since";
set resp.http.Content-Length = "0";
set resp.http.Content-Type = "text/plain charset=UTF-8";
set resp.status = 204;
}
}
What worked for me was to import "github.com/gorilla/handlers" and then use it this way:
router := mux.NewRouter()
router.HandleFunc("/config", getConfig).Methods("GET")
router.HandleFunc("/config/emcServer", createEmcServers).Methods("POST")
headersOk := handlers.AllowedHeaders([]string{"X-Requested-With", "Content-Type"})
originsOk := handlers.AllowedOrigins([]string{"*"})
methodsOk := handlers.AllowedMethods([]string{"GET", "HEAD", "POST", "PUT", "OPTIONS"})
log.Fatal(http.ListenAndServe(":" + webServicePort, handlers.CORS(originsOk, headersOk, methodsOk)(router)))
As soon as I executed an Ajax POST request and attaching JSON data to it, Chrome would always add the Content-Type header which was not in my previous AllowedHeaders config.

How to respond to an HTTP OPTIONS request?

The HTTP OPTIONS method is supposedly used to determine what other methods the server supports on a given resource. Given that, I have two questions:
What does this response look like? I have seen examples with CSV lists in Public, Allow, and even Access-Control-Allow-Methods headers. Are they all needed? What's the difference? RFC 2616 doesn't seem to be very helpful here.
Would it be appropriate to use this to list the actions that a resource supports in a non-REST-API environment? For example, if my ConversionController supports the action convert, would a response like this make sense:
Request:
OPTIONS /conversion HTTP/1.1
Response:
HTTP/1.1 200 OK
...
Allow: CONVERT
...
RFC 2616 defines "Allow" (http://greenbytes.de/tech/webdav/rfc2616.html#rfc.section.14.7). "Public" is not in use anymore. "Access-Control-Allow-Methods" is defined in the CORS specification (see http://www.w3.org/TR/cors/).
What is an HTTP OPTIONS request?
It is a request from the client to know what HTTP methods the server will allow, like GET, POST, etc.
Request
The request might look like this when asking about the options for a particular resource:
OPTIONS /index.html HTTP/1.1
or like this when asking about the server in general:
OPTIONS * HTTP/1.1
Response
The response would contain an Allow header with the allowed methods:
Allow: OPTIONS, GET, HEAD, POST
Why is the server receiving an HTTP OPTIONS request?
Some REST APIs need it (but if you are defining the API, you'd know that)
Browsers send it to servers as "preflighted" requests to see if the server understands CORS
Attackers send it to get more information about the API
How to respond to an HTTP OPTIONS request?
You could respond with an Allowed header and even document your API in the body.
You could respond with additional CORS defined Access-Control-Request-* headers.
You could respond with 405 Method Not Allowed or 501 Not Implemented.
How do I stop getting HTTP OPTIONS requests?
If it's coming from a browser then update your API so that it isn't doing anything "dangerous" (like PUT or DELETE, or POST with application/json). Only perform simple requests.
See also
RFC 2616 Section 9: Method definitions
MDN Web docs: OPTIONS
MDN Web docs: Cross-Origin Resource Sharing (CORS)
CORS - What is the motivation behind introducing preflight requests?
How to exploit HTTP Methods
In response to the title: "How to respond to an HTTP OPTIONS request?" To answer that, I'd want to know why you want to respond to an OPTIONS request? Who/what is sending you an OPTIONS request, and why? Many public servers respond with some form of "error" or "not allowed" (500, 501, 405). So, unless you're in a specific situation where your clients will be reasonably sending OPTIONS requests and expecting useful/meaningful information back (e.g., WebDAV, CORS), you probably want to respond with: "don't do that."
In terms of your question about the "OPTIONS /conversion HTTP/1.1" request: unless you know that there's some client of your server, a client which would send an OPTIONS request to "/conversion" and expect a response with "Allow: CONVERT," the answer is no: it wouldn't make sense to respond like that. I think that most implementations that do support OPTIONS and respond with "Allow," respond with standard HTTP methods.
Here's a great article on the topic.
Summary: OPTIONS is immediately problematic because it doesn't support caching. Alternatives: server-wide metadata: try well-known URI's. Resource-specific: try using a Link header on its responses, or a link in the representation format for that resource.
Lastly, if what you're after is a service description, have a look at WADL or RSDL.
EDIT:
dotnetguy makes a good point in the comment below: OPTIONS is undeniably valuable in certain contexts (e.g., CORS); I certainly didn't mean to suggest otherwise.

Http Option Method with Javascript request

I use backbone.js' s model. When i save the model, it sends HTTP OPTIONS method to server-side on firefox, but sends HTTP POST method with safari.
I know it is not an issue about backbone.js, it is about CORS. I will just check if method, GET, POST, PUT and DELETE on server-side, i will not do a job with HTTP OPTIONS method.
my requested url is my api: api.foo.com
and api requested from: bar.com
so, how can i control in all browsers request my api.foo.com with HTTP POST not OPTIONS?
and how can i share api.foo.com' s content with all request from any other domains?
Note: i have already changed response' s headers from server-side to: Access-Control-Allow-Origin: *
The OPTIONS request is actually the so called preflight request of the CORS specification. This preflight request is used by web browsers to check under what conditions the server would accept a request from the respective origin. If the response to the preflight request was satisfying, the browser will send the actual request.
So to comply with this specification, you need your server to reproduce the steps of preflight request processing.

Resources