Why withCredentials needed for same-site requests - xmlhttprequest

Doc https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/withCredentials says that for same site requests, XMLHttpRequest.withCredentials is not needed.
I have webpage at one subdomain & api backend at another subdomain. They are cross-origin but same-site.
So why it does not work with withCredentials = true ?
Thanks

Related

Grafana OAuth2 by Google and HTTPS

Trying to set up OAUTH 2 for access to Grafana (https by openssl)
Grafana v8.0.5 is hosted on AWS EC2 accessed via subdomain. Been working fine for months but time came to improve security, hence https & OAuth.
I followed this link and configured https access & SSL. Worked like a charm!
I followed this link to configure Google OAuth here
Below is the server options in grafana.ini
[server]
#Protocol (http, https, h2, socket)
protocol = https
#The ip address to bind to, empty will bind to all interfaces
;http_addr =
#The http port to use
http_port = 3000
#;http_port = 80
#The public facing domain name used to access grafana from a browser
domain = grafana.redacted.io
#Redirect to correct domain if host header does not match domain
#Prevents DNS rebinding attacks
;enforce_domain = false
#The full public facing url you use in browser, used for redirects and emails
#If you use reverse proxy and sub path specify full url (with sub path)
root_url = https://grafana.redacted.io/login/google/
#Serve Grafana from subpath specified in `root_url` setting. By default it is set to
`false` for compatibility reasons.
;serve_from_sub_path = false
Google.Auth options in grafana.ini are:
[auth.google]
enabled = true
allow_sign_up = false
client_id = theClientIdFromGoogleCloudConsole
client_secret = theClientSecretFromGoogleCloudConsole
scopes = https://www.googleapis.com/auth/userinfo.profile
https://www.googleapis.com/auth/userinfo.email
auth_url = https://accounts.google.com/o/oauth2/auth
token_url = https://accounts.google.com/o/oauth2/token
api_url = https://www.googleapis.com/oauth2/v1/userinfo
allowed_domains = redacted.io
Google Console Settings
Authorised JS Origins: https://grafana.redacted.io:3000
Authorised redirect URIs: https://grafana.redacted.io/login/google
grafana service restarted and instance restarted.
Visiting URL:
https://grafana.redacted.io:3000 forwards to https://grafana.redacted.io:3000/login/google/login - page won't load
https://grafana.redacted.io:3000/login loads a page with text telling me "If you host grafana under subpath make sure your grafana.ini root_url setting includes subpath. If not using a reverse proxy make sure to set serve_from_sub_path to true."
Try:
serve_from_sub_path = true
Then revisit URL 2, the login page will load with the Google Auth button. Clicking it, loads "Error 400: redirect_uri_mismatch". This is obviously not what correct and reacding the specs I know I need this to be false
redirect_uri: https://grafana.redacted.io/login/google/login/google
Try:
root_url = https://grafana.redacted.io:3000/login/google
serve_from_sub_path = false
Update Google Console Redirect URL to equal root_url. Revisit URL 2 (above) and login screen loads again; click Google, safari error:
Too many redirects occurred trying to open "https://grafana.redacted.io:3000/login/google/login"
Why the extra /login again?
OK, so I can get all the way through to signing in with Google:
Try:
root_url = https://grafana.redacted.io/
serve_from_sub_path = false
Google Console left as: https://grafana.redacted.io/login/google
Visit link 2 above, page loads, sign in screen appears, 2FA on google all good, then
"Safari can't open the page "https://grafana.redacted.io/login/google?state...etc.etc.""
Why, when all redirects are set equal is a /login being appended to the URL?
Spent ages on this, and would really appreciate some help
Thanks
In grafana.ini (v8.0.5) whenever I set the root_url to that directed by the Grafana Google OAuth2 docs it added stuff to the url and this through a redirect mismatch error.
Thanks #DalmTo for the video link. I took this and experimented...follow below
Grafana.ini
root_url = https://grafana.redacted.io:3000/xxxx/ &
serve_from_sub_path = true
The "xxxx" can be anything except the word "login". I've tried some random stuff and it all works, except using the word login; I'm actually using "google" lol. Ensure to append the final /
Google Console: set the Redirect URI to https://grafana.redacted.io:3000/xxxx/login/google
Grafana UI will be available at https://grafana.redacted.io:3000/
It's not really a solution, more a work around.

Safari Cross Site Ajax call does not store cookie

I have a website on VueJS and a backend on AWS.
Lets say the website is on www.mywebsite.com, on a hosting server with CPanel and my backend on aws runs under www.mybackend.com
When the user logs in using the website, it makes an axios/fetch call to the backend. The backend will return a set-cookie for the www.mywebsite.com domain.
Although Chrome and FF works fine. Safari does not store the cookie as it is a cross site cookie.
Is there any easy way to make Safari store the cookie and send it to the calls to the backend? Can I mask the backend url with a subdomain from my main domain? Any ideas?
Safari does behave differently from those other browsers. It will only allow cross-origin cookies if they are from the same cookie domain.
So you can get this to work but only if you're in a position to change the URL so that the domains match.
So if you have a website at:
www.mywebsite.com
and the backend at:
backend.mywebsite.com
You can then share the cookie by setting the Domain:
Set-Cookie: my-cookie=value; Domain=mywebsite.com
If the two sites are on totally unrelated domains and you can't change that then I'm not aware of any way to make that work with Safari.
I did a more complete write-up of using cookies with CORS (including the quirks with Safari) at https://cors-errors.info/faq#cdc8

S3 CORS policy for public bucket

It seems to be easy, but I don't know what I am missing.
I have a public bucket with a js script that I fetch from my web site. I noticed that I don't send Origin header to S3, it is not required and everything works without any CORS configurations.
What's more, even after I manually added Origin header to that GET call and explicitly disallowed GET and my domain via:
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
<AllowedOrigin>http://www.nonexistingdomain.com</AllowedOrigin>
<AllowedMethod>POST</AllowedMethod>
<AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>
I can still get the content. What's going on here?
Ok, after a conversation with Quentin, I think I understand where I am misinterpreting how CORS should work.
In Java world, it's a very common practice to actually reject requests when Origin doesn't match. Here is another thread where it's mentioned.
If we take Spring as an example(which is de-facto standard in Java world), here is what happens when CORS filter is added:
String allowOrigin = checkOrigin(config, requestOrigin);
...
if (allowOrigin == null) {
logger.debug("Reject: '" + requestOrigin + "' origin is not allowed");
rejectRequest(response);
return false;
}
where:
/**
* Invoked when one of the CORS checks failed.
*/
protected void rejectRequest(ServerHttpResponse response) {
response.setStatusCode(HttpStatus.FORBIDDEN);
}
You can find the code here.
But to my surprise, it is not such a common practice with other stacks and server-side technologies. Another common approach would be to send whatever CORS configuration they have to the browser and leave the decision to it.
S3 is even more tricky: it only sends CORS response headers when the bucket CORS rules match the CORS-enabled request(a request qith Origin header). Otherwise, there would be no CORS response headers.
The Same Origin Policy is a feature enforced by browsers which prevents JavaScript running on one website from reading data from a different website. (This stops random websites using JavaScript to use your browser to skip past your corporate firewall and access your intranet or read your GMail with your cookies).
CORS lets a website relax the Same Origin Policy to allow other websites to read data from it that way.
CORS is not authentication/authorisation. Your public bucket is public.
You aren't using JavaScript to read data from your bucket, you are loading the JS directly from the bucket.
Let's break down the problem and try to understand the fundamentals of CORS.
What is Cross-Origin Request & CORS?
Cross-Origin Request: A request for a resource (like
an image or a font) outside of the origin is known as a cross-origin
request.
CORS is helpful when you are requesting for a protected resource from another origin.
Cross-Origin Request Sharing: A request for a protected resource (like an image or a font or an XHR request) outside of the origin is known as a cross-origin request.
Why do we need CORS when the resources can be protected by using authentication/authorization tokens?
CORS is the first line of defense. When both the client (e.g., browsers) and servers are CORS-aware, clients will allow only requests from the specific origins to the servers as per instructed by the servers.
By default, browsers are supposed to implement same-origin policy security mechanism as per the guidelines on building the browser. Almost all the modern browser implement same-origin policy that instructs the browsers to allow requests to the servers if the origin is the same.
Same-origin policy is a security mechanism of a browser, you can read more about it here. It is because of this feature of browsers, the browser blocks all the request when the designation origin and the source origin are different. (Servers are not even aware that this is happening, Wow!)
For simpler use cases, when the assets(js, CSS, images, fonts), XHR resources are accessible with the same origin, there is no need to worry about CORS.
If assets are hosted on another origin or XHR resource are hosted on servers with a different domain that the source then browsers will not deny the request to cross-origin by default. Only with appropriate CORS request and response headers, browsers are allowed to make cross-origin requests.
Let's look at the request and response headers.
Request headers
Origin
Access-Control-Request-Method
Access-Control-Request-Headers
Response headers
Access-Control-Allow-Origin
Access-Control-Allow-Credentials
Access-Control-Expose-Headers
Access-Control-Max-Age
Access-Control-Allow-Methods Access-Control-Allow-Headers
For setting up CORS the Origin, and Access-Control-Allow-Origin headers are needed. Browsers will automatically add Origin header to every request, so a developer needs to configure only Access-Control-Allow-Origin response header.
For protecting access to resources only from specific domains, S3 provides an option to configure CORS rules. If the value of Access-Control-Allow-Origin header is * all the cross-origin requests are allowed, or else define a comma-separated list of domains.
There are a couple of things you need to be aware of when using CORS.
It's the first level of defense to a protected resource and not the ultimate defense.
You still need to implement appropriate authentication & authorization for the resource to perform CRUD operations on the server.
Implementing Same Origin Policy is a guideline for building the browser and are not mandatory.
CORS headers are useful only when clients accept the headers. Only modern browsers accept CORS headers. If you are not using browsers to make the resource request, then CROS do not apply.
If you type the link in the address bar of the browser, the CORS rules are not applied to because the browser does not send the Origin header to the server. The Origin header is sent by the browser only on the subsequent resource request (stylesheets, js files, fonts) and XHR requests by the origin.
If you access the resource file by directly typing the link in the address bar, the browser does not send Origin header to that request.
Also, if you want to restrict GET access use S3 pre-signed URL on a private bucket.

Safari CORS issue - host not allowed by Access-Control-Allow-Origin

Hi I am having an issue with CORS on safari. My request is working fine in every other browser except safari. I keep getting the error message [host] not allowed by Access-Control-Allow-Origin although the api specifically sets the request url in the response for both the OPTIONS request and the POST request.
I have researched this endlessly but nothing I have found has worked.
I have attached a screenshot from chrome which you can see all of the request and response headers and a screenshot from Safari where you can see the error. It is exactly the same request with exactly the same parameters.
Chrome:
Safari:
Thanks in advance!
Cross Origin Resource Sharing calls are generally blocked by browsers and thus API calls made from a website (in your case localhost:3004) to a remote host 52.85.173.227 (I think you have hosted it in Amazon's API Gateway).
What you need to do to enable CORS .
If you are using AMazon's API Gateway .
Click on the resources and in the Action , you have an option where you can enable CORS . Do that and it will add the headers to enable CORS .
Option on AWS API Gateway to enable CORS
Once you do this your response header of OPTIONS call will have "Access-Control-Allow-Origin" as "*" .
Thus your browser / web site will be able to make cross origin calls.
Hope this helps.

Redirecting HTTP POST requests to HTTPS POST requests

I recently just setup my server to run over HTTPS with an SSL certificate. The website is an image host and the developers at ShareX have included my site in their application.
My problem is, all HTTP requests are automatically redirected to HTTPS. The website works a charm, ShareX runs into a problem.
How can I tell nginx to redirect HTTP POST requests to HTTPS, but still make the POST request? Hope that's as informative as it sounds.
You cannot redirect. Either you send a POST request over HTTP or over HTTPS. This is consistent with RFC 7231, Section 4.3.3.
You can do this with a 307 response.
When fired via POSTMAN this will redirect the POST from HTTP to HTTPS.
This is some expressJS code to do this (but you can convert the logic to any language).
Essentially, the logic is:
IF a request comes in that is not over SSL and there is an SSL server running THEN
SET the location response header to the URL you want to redirect to
SEND the response code 307
END IF
This causes POSTMAN to re-post over SSL automatically where the response location header uses the HTTPS protocol. I believe this logic will also work with browsers. There may be security issues doing this however since someone could "sniff" the incoming HTTP request and therefore know what was sent over HTTPS.
USE this approach at your own risk!
let middleware = (req, res, next) => {
if (!req.secure && req.httpsServer) {
let redirectUrl = `https://${req.hostname}:${config.httpsPort}${req.url}`;
res.location(redirectUrl);
return res.sendStatus(307);
}
return next();
};
module.exports = middleware;