I am building an Angular app that interacts with an API built with ASP.NET Web API 2. I am using Basic Authentication by sending an Authorization header with each request that requires authentication:
Angular snippet:
$http.defaults.headers.common['Authorization'] = authHeader;
Request:
Accept:application/json, text/javascript
Accept-Encoding:gzip, deflate, sdch
Accept-Language:en-US,en;q=0.8
Access-Control-Max-Age:1728000
Authorization:Basic [base64 encoded credential couplet here]
Connection:keep-alive
DNT:1
Host: blah.com
Origin:http://localhost:9000
Referer:http://localhost:9000/
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/53
This all works OKAY, but a preflight OPTIONS request is sent with every GET or POST request. This is majorly impacting the perceived speed of the application. I have done lots of reading on CORS "Simple Requests" and it seems that in order to avoid the dreaded preflight OPTIONS request is to avoid adding any custom headers in my requests. I've tried lots of other stuff like sending a Content-Type of text/plain, but it seems that the Authorization header is the thing that is violating the CORS "Simple request" requirement.
So it seems that I may have to move the API over to use token based authentication/authorization. In order to avoid preflight requests, it seems that I will need to place the token in the query string. This is okay as it is only a small internal web app which will only be accessed by a couple of users anyway. I intend to implement caching on controller responses. As each request to an controller action will have a different token in the querystring based on the currently authenticated user, will this render cacheing useless?
So:
How do I avoid preflight requests (using custom Authorization headers if at all possible)
If 1.) is not possible, and I move to token based auth, will I be unable to cache API responses for controller actions
What are the most widely used methods to avoid preflight requests but also to auth users securely?
n.b I know there are a couple of other threads on SO and elsewhere on the web regarding this, but none of them tend to provide a definitive answer on whether it is possible to avoid preflight requests for GETs and POSTs when using custom HTTP authorization headers.
I think this post (How to apply CORS preflight cache to an entire domain) pretty much says it all - there is not much you can do about this
The one simple solution is to add a reverse proxy to the proxy/webserver serving your angular app (e.g. nginx) to route your RESTful calls via the same domain, e.g. appdomain.com/api --> apidomain.com.
Another solution that seems to be working OK for me. Instead of setting up a proxy and needing to route to the same domain, it is possible to return the preflight request directly from nginx and therefore reducing the time required by the preflight request down to just a couple of milliseconds.
Here is a simple snippet that can be used with nginx.
location / {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}
}
Once the preflight request is successful, it is then possible to simple add "Access-Control-Allow-Origin" and other necessary stuff to the 'GET','POST' requests, etc...
Related
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).
I'm trying to make a GET request to a Context Broker instance from a browser.
I've enabled CORS on the CB using the -corsOrigin __ALL flag when starting the app, and I can see that this has worked by making a request in POSTMAN and seeing this header in the response: access-control-allow-origin →*.
I need to specify the Fiware-Service header in my GET request in order to get the correct entities, which I believe is making the request not simple, triggering an OPTIONS HTTP request.
Inspecting the outgoing request, Chrome reports that these headers are sent:
Access-Control-Request-Headers: fiware-service
Access-Control-Request-Method: GET
The response I get from the Context Broker is:
Request URL: http://xxx.xxx.xxx.xxx:1026/v2/entities/
Request Method: OPTIONS
Status Code: 405 Method Not Allowed
A previous answer by McMutton, to a similar question stated:
"do the necessary changes on your js code to make sure your request
falls within the scope of simple requests."
Which was directed at removing non-standard headers from the request. However, for me I cannot see any non-standard headers being sent.
Reading the Fiware documentation on Access-Control-Allow-Headers, there is a link to the source code where the allowed headers are specified. There, I can see the Fiware-Service header defined, but it does not case-match the headers being sent from the browser (the browser has converted my headers to all lower case).
Does anyone know if "the headers check" in the Context Broker is case-sensitive?
If not, what else could be the issue?
Edit: this issue seems to have been reported here:
https://github.com/telefonicaid/fiware-orion/issues/3453
Based in the discussion on the associated github issue it seems the problem is due to Context Broker is pretty old (version 1.7.0) and that feature wasn't developed yet in that version.
The solution is to update Context Broker to the most recent version (2.2.0 at this moment).
Thanks #fgalan, yes the feature is included in the latest Context Broker version. However, our system is currently quite fragile, so until we can confidently re-build and migrate to the newer version I'm going to mock the HTTP response for the options request using NGINX.
This configuration listens for requests on a different port to the Context Broker, and sends a success response when OPTIONS HTTP requests arrive.
If it's not an OPTIONS HTTP request, NGINX forwards the request to the Context Broker instance.
server {
listen 1885;
listen [::]:1885;
location / {
if ($request_method = OPTIONS ) {
add_header Content-Length 0;
add_header Content-Type text/plain;
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Fiware-Service';
return 204;
}
proxy_pass http://xxx.xxx.xxx.xxx:1026;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
I should make cross domain API with Symfony.
There is some bundle for that?
I have tried FOS Rest Bundle but did not seem have solved my problem.
I advise you to use NelmioCorsBundle:
https://github.com/nelmio/NelmioCorsBundle
This bundle allows you to send Cross-Origin Resource Sharing headers with ACL-style per-URL configuration.
Is very useful for CORS problem
I used Symfony 5 and Wordpress this code in the file public/index.php works perfectly.
header('Access-Control-Allow-Origin: *');
header("Access-Control-Allow-Headers: X-API-KEY, Origin, X-Requested-With, Content-Type, Accept, Access-Control-Request-Method");
header("Access-Control-Allow-Methods: GET, POST, OPTIONS, PUT, DELETE");
header("Allow: GET, POST, OPTIONS, PUT, DELETE");
$method = $_SERVER['REQUEST_METHOD'];
if ($method == "OPTIONS") {
die();
}
Also, I remove package cors .. This Bundle doesn't work for me
I'm not sure that's the right way, but I resolved for me:
Create new event subscriber (like ResponseSubscriber)
Listen KernelEvents::RESPONSE event
In your handler add the following:
if ($event->getRequest()->getMethod() === 'OPTIONS') {
$event->setResponse(
new Response('', 204, [
'Access-Control-Allow-Origin' => '*',
'Access-Control-Allow-Credentials' => 'true',
'Access-Control-Allow-Methods' => 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers' => 'DNT, X-User-Token, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type',
'Access-Control-Max-Age' => 1728000,
'Content-Type' => 'text/plain charset=UTF-8',
'Content-Length' => 0
])
);
return ;
}
https://github.com/nelmio/NelmioCorsBundle
or
if you want to write a cors package for yourself, here some tips:
the 'to Checked' Origin will be automaticly embedded by any browser in HTTP_ORIGIN
do not use a sole '*' wildcard
you can cut of the request processing early if u use a request & response listener (if you want)
do not only send it with OPTIONS methods (some browser may want it in GET or even POST requests.)
manage your Origin cors list in some config yaml files for example.
and validate the HTTP_ORIGIN if it matches your cors list. then send the HTTP_ORIGIN AS "VALID" back.
Access-Control-Allow-Origin: THE_HTTP_ORIGIN_HERE
+ the other Access-Control header.
see https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS for more in depth information.
in the end, this mostly the same workflow that https://github.com/nelmio/NelmioCorsBundle uses.
my advice: safe your time ;)
TLDR; don't take cors lightly by just use a wildcard, over a bad cors implementation every attacker site can fish a active session from your users.
step1: user uses your API, has a session cookie,
step2: user visits a random site, which embed code who just requested the browser to trigger a request directly to your api with the cookie credentals.
step3: your api must detect this as a attack and not answer with a *.
the victim browser, its not possible to send a forged HTTP_ORIGIN via javascript.
I am using cljs-http and I am not able add custom headers into http request. Adding code snippet with request creation:
arg {:basic-auth auth
:headers {
"Access-Control-Allow-Origin" " *"
"Access-Control-Allow-Headers" "Origin, X-Custom-Header, Content-Type, Accept"
"X-Custom-Header" "Custom header"}
resp (csp/<! (cljs-http/get url arg))
Output in chrome console:
XMLHttpRequest cannot load url. Request header field X-Custom-Header is not allowed by Access-Control-Allow-Headers.
Could you please suggest me how to do it ?
The problem was related not to clojurescript, but to server side.
The CORS headers were not configured.
While handling requests, server should attach Access-Control-Allow-Origin, Access-Control-Request-Method headers to response that's permit performing requests for frontend (cljs).
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.