Iframe a web app causes CSRF error only on Chrome but not on Firefox - http

I'm trying to show a third-party web page (with their approval) in an iframe. This site has a different domain to my parent site. The iframed site has set their frame-ancestor to include my domain and I've set my frame-src to include their domain, so the framing permission itself is fine.
The site displays fine on firefox.
However on Chrome I get an error saying that the CSRF token is mismatched. This validation is done on the backend by the client site.
My question is why does firefox work when chrome does not? If both browsers caused the same error I wouldn't even be asking this question.
Please excuse the naivety of this question as my web experience is minimal as I'm a backend developer.
Thank you.

The difference is the SameSite cookie attribute behaviour. I'm guessing that the CSRF protection is partially based on some kind of a cookie interaction in the embedded third-party site. The cookie must be sent (along with other things most probably) to the third-party site so it can check forged requests.
The SameSite attribute is likely not set to any value for the CSRF cookie.
The default behaviour in Chrome (and many major browsers now) is to default to Lax, which means the cookie will not be sent across origins, so I think this is why it doesn't work in Chrome.
However, Firefox defaults to None, which means the cookie will be sent in your case, so the page works correctly.
The solution to this is that the 3rd party site should set the CSRF cookie with an appropriate SameSite value explicitly, probably None in this case, if (and only if!) this does not compromise the security of the solution - it's not possible to tell without further details, but an effective CSRF protection cannot solely be based on a cookie (that's the whole point), so setting it as SameSite=none would probably be ok.

The problem is going to be a difference in HTTP headers between the browsers. You need to inspect both and compare the difference between them.
Their are few different ways you can see these headers, both browsers have network connection inspection tools that you can use, you could write some server code to dump them in a log, or finally use a tool like WireShark to intercept them.
The most likely issue is going to be cookies not being shared with the iframe.
Once you have worked out what the difference is between the two browsers you can set the correct headers in Chrome. This will likely mean reading the required value from the parent page, copying it to the iframe and then setting it.
Sorry their is not more detail in this answer, but I don’t really have much to go on in your question.

As alluded in some of the answers the issue was Chrome setting the cookies, samesite value to 'Lax' by default for JSESSIONID cookie. So 3rd party sites did not get the cookie. The solution was to set the same site value to none. This was fine since we already have CSRF protections in place + the use of frame-ancestor CSP to restrict the iframe domains.
I added the following filter to add the samesite value (Setting samesite on server config level (Wildfly( didn't work for me)
private static final String SAME_SITE_ATTRIBUTE_VALUES = ";HttpOnly;Secure;SameSite=None";
private static final String SESSION_COOKIE_NAME = "JSESSIONID";
private static final String SESSION_PATH_ATTRIBUTE = ";Path=";
private static final String ROOT_CONTEXT = "/";
#Transactional
public void doFilter(HttpServletRequest req, HttpServletResponse res) {
Cookie[] cookies = req.getCookies();
if (cookies != null && cookies.length > 0) {
List<Cookie> cookieList = Arrays.asList(cookies);
Cookie sessionCookie = cookieList.stream()
.filter(cookie -> SESSION_COOKIE_NAME.equals(cookie.getName()))
.findFirst()
.orElse(null);
if (sessionCookie != null) {
String contextPath = req.getServletContext() != null
&& StringUtils.isNotBlank(req.getServletContext().getContextPath()) ? req.getServletContext().getContextPath() : ROOT_CONTEXT;
res.setHeader(HttpHeaders.SET_COOKIE, sessionCookie.getName() + "=" + sessionCookie.getValue()
+ SESSION_PATH_ATTRIBUTE + contextPath + SAME_SITE_ATTRIBUTE_VALUES);
}
}
}

Running into another problem now. The above filter solution worked fine on local testing environments.
The app was deployed to AWS, and now I see the cookie samesite is not being set. Is AWS overwriting it somewhere? So confused.

Related

What value should I send for `SameSite = None` and` Secure`?

Chrome update has given me great trouble
So far, I still haven't found anyone's answer in Chrome 80 that correctly answers SameSite issuess.
Leave the following questions for me and us.
In my html page, there is only provided by YouTube.
However, in Chrome, 'cross-site requests if they are set with SameSite = None and Secure' are displayed.
What should I do?
I only did YouTube iframe, what value should I send cookie value and session value?
I want a direct solution.
A fake method like 'cross-site-cookie = name' doesn't help me.
I need a direct way to experience.
I want about php version <7.3 and I want to solve the problem with php or javascript or .htaccess etc.
only youtube iframe tag
Chrome's new cookie policy requires applications to be served over HTTPS, if some iframe content involves usage of secure cookies.
This rule is enforced to disable unsafe requests accross sites, and reduce the risk of Cross Site Request Forgery (CSRF).
You can read more about possible use cases in this post:
https://web.dev/samesite-cookie-recipes/

Postman is not using cookie

I've been using Postman in my app development for some time and never had any issues. I typically use it with Google Chrome while I debug my ASP.NET API code.
About a month or so ago, I started having problems where Postman doesn't seem to send the cookie my site issued.
Through Fiddler, I inspect the call I'm making to my API and see that Postman is NOT sending the cookie issued by my API app. It's sending other cookies but not the one it is supposed to send -- see below:
Under "Cookies", I do see the cookie I issue i.e. .AspNetCore.mysite_cookie -- see below:
Any idea why this might be happening?
P.S. I think this issue started after I made some changes to my code to name my cookie. My API app uses social authentication and I decided to name both cookies i.e. the one I receive from Facebook/Google/LinkedIn once the user is authenticated and the one I issue to authenticated users. I call the cookie I get from social sites social_auth_cookie and the one I issue is named mysite_cookie. I think this has something to do with this issue I'm having.
The cookie in question cannot legally be sent over an HTTP connection because its secure attribute is set.
For some reason, mysite_cookie has its secure attribute set differently from social_auth_cookie, either because you are setting it in code...
var cookie = new HttpCookie("mysite_cookie", cookieValue);
cookie.Secure = true;
...or because the service is configured to automatically set it, e.g. with something like this in web.config:
<httpCookies httpOnlyCookies="true" requireSSL="true"/>
The flag could also potentially set by a network device (e.g. an SSL offloading appliance) in a production environment. But that's not very likely in your dev environment.
I suggest you try to same code base but over an https connection. If you are working on code that affects authentication mechanisms, you really really ought to set up your development environment with SSL anyway, or else you are going to miss a lot of bugs, and you won't be able to perform any meaningful pen testing or app scanning for potential threats.
You don't need to worry about cookies if you have them on your browser.
You can use your browser cookies by installing Postman Interceptor extension (left side of "In Sync" button).
I have been running into this issue recently with ASP.NET core 2.0. ASP.NET Core 1.1 however seems to be working just fine and the cookies are getting set in Postman
From what you have describe it seems like Postman is not picking up the cookie you want, because it doesn't recognize the name of the cookie or it is still pointing to use the old cookie.
Things you can try:
Undo all the name change and see if it works( just to get to the root of issue)
Rename one cookie and see if it still works, then proceed with other.
I hope by debugging in this way it will take you to the root cause of the issue.

Why can foo.example.com set cookies for example.com?

From both the documentation and this link, I already know that the fact is foo.example.com can set cookies for example.com by sending response with Domain = example.com in the Set-Cookie header. But why is this allowed?
For example, the fact is, a server (say, foo.example.com) cannot set cookies for its siblings (say, bar.example.com) or the domain names lower than it (also known as "its child", say, ide.foo.example.com), but it can set cookies for the domain names higher than it (also known as "its parents", in this case example.com.
Let me make the statement of the question even more clear by putting it into the real world. Just like apps on Google App Engine, foo.appspot.com obviously cannot set cookies for bar.appspot.com because they are two different apps, and they shouldn't affect each other's behavior. But why is it allowed for foo.appspot.com to set cookies for appspot.com by sending Domain = appspot.com in its response header? By doing this the foo.appspot.com app can actually affect other apps' behavior on Google App Engine, since the browser will send this cookie when visiting bar.appspot.com, the domain name of which is a child of appspot.com.
I learned all these things about cookies from the Web Development course on Udacity. But I'm really confused with this question. Can anybody help explain this? Thanks in advance. :-)
The link you provided is horribly outdated. Too bad people googling "cookie domain" will find it first.
I should write a better one; but for now, to quickly answer your question - it is about "public suffix" domain.
Can server "example.com" set a cookie for "com"? Nope, because "com" is a public suffix.
Can "foo.co.uk" set a cookie for "co.uk"? Nope, because "co.uk" is a public suffix.
It happens that "appspot.com" is also a public suffix; so "foo.appspot.com" cannot set a cookie with domain="appspot.com". (It can, but browsers will reject it)
Unfortunately, there's no algorithm to determine which is a public suffix. The list of all public suffix is maintained manually at https://publicsuffix.org/

ASP.NET authentication cookie disappears, only in IE, only from specific locations

Internet explorer is not keeping my authentication cookie after one page redirect.
Here is the situation:
I have an ASP.NET 2.0 web application running on a shared iis7 hosting. The application uses forms authentication to handle login and user identity and writing a cookie (.ASPXFORMSAUTH) on the client machine for that purpose.
in IE (checked with version 8, 9), from some locations, the authentication cookie is not being kept after the first page. The observed behavior is:
User name and password are submitted in login form
User is succesfuly redirected to the first-after-login page (and fiddler shows that the .ASPXFORMSAUTH cookie exists)
After clicking another link or hitting F5 for refresh, the user is credirected to login, and the authentication cookie (according to fiddler) doesn't exist anymore.
at the refresh / click, the authentication cookie is missing in the request headers.
This doesn't happen in Chrome / FF, and even in IE, it seems to be dependent on the location from which I am connected.
also, locally (using the internal dev server in VS2008), all works fine and reflects fine in fiddler as well.
I am banging my head at it for a few days now. Thought it may be some kind of a strange firewall problem, but couldn't determine anything conclusive.
Ideas will be appreciated.
IE suffers from a weird bug - for some reasons, if there are non-alphanumeric characters in the domain's name, IE won't persist cookies... and hence you'll have no persistent session between different calls.
Check if your domain has non-alphanumeric characters in it, such as test_domain or test-domain or the likes. Unfortunately, I don't know any fixes for this short of aliasing the incriminated domain or accessing it directly via the IP. The reason you've got no problems locally is that you're pointing to http://localhost, which is fine. As soon as you deploy to a non IE compliant domain you'll witness the problem.
Happened to me and it took hours to find out why. Hope this helps. Another reason to kill IE with fire.
My solution has been a combination of other solutions:
IE not saving asp.net authentication token / cookies
http://connect.microsoft.com/VisualStudio/feedback/details/662275/asp-net-user-agent-sniffing-and-ie10-internet-explorer-10
upgrade to .NET 4.0 adding the tag ticketCompatibilityMode="Framework40" in the web.xml: http://msdn.microsoft.com/en-us/library/1d3t3c61.aspx
Note that the real final solution was the 3rd.
Last but not least: once I set this flag above I had to change the logout method in the code behind because the old one did not logout any more:
protected void LoginStatusLink_LoggedOut(object sender, EventArgs e) {
// remove the authenticatation cookies from the browser
FormsAuthentication.SignOut();
// force a new 'expired' auth cookie
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName);
cookie.Expires = DateTime.Now.AddMonths(-1);
Response.Cookies.Add(cookie);
// delete roles cookie
Roles.DeleteCookie();
// clear and abandon session
Session.Clear();
Session.Abandon();
// this line just to leave (forget) the current page
this.Response.Redirect("~/");
}
1.try to create a persistant cookie
2.check your cookie settings for IE
Check the server's date. I had a situation where the server was 1 day behind the browser and so the authentication cookie essentially expired immediately. This affected IE, but not FF.

IE8 Won't Always Edit Cookie Value

I've run into a problem where login on my site is not working consistently in IE8. It works fine in Firefox/Chrome/Safari but not IE8.
On first login everything is fine. After logging out and trying to log back in it usually fails for a while. It will end up redirecting to the login page against after authentication.
So the authentication is successful, it returns true, but it seems IE8 is not accepting the new value for the session id which we set by returning:
Set-Cookie SESSIONID=........; Path=/
in the response header. But obviously this works with a clear cache and I can get in fine. But after its already there it fails to reset, so after authenticating and attempting to go to a new page it sees this is an old session id being sent from the browser and redirects to the login page.
I haven't found anything here or elsewhere that really solves this (besides clearing the cache). Most references to IE8 cookie problems are language/framework specific and don't answer this problem.
Is there something special I might need to do with the set-cookie to make this work?
UPDATE:
I've set IE8 to prompt before accepting any cookie. When the login works fine it prompts as expected. When it does not work there isn't even a prompt to except the cookie..
UPDATE 2:
I should have mentioned that the cookie is expected to be set after an ajax call:
$.get(authenticate_url, ....)
The url it requests a response from returns the header that sets the session id, then in the callback function the user is redirected to the main page -- assuming login is successful.
Is it a first party cookie or a 3rd party cookie. If the latter, ensure you're sending a P3P header.
Are you setting the HTTPOnly attribute?
Are you sure that the domain for the cookie is always the same? E.g. if you set the cookie when visiting "example.com" and tried to change it from "www.example.com" then you'll encounter problems.
I've hit similar symptoms when I have cookies set for both www.example.com and example.com. If there's no domain explicitly set, then session cookies get set for both.
However, higher-level domain cookies take precedence over the lower domian. So, if www.example.com tries to set a cookie, but example.com already has, then the example.com cookie stays in place and continues to apply for www.example.com.
There are two ways to approach this problem. One is not to allow access both with and without the www subdomain. Redirect one to the other. The second is to explicitly set the cookie domain, so that there aren't two versions lying around.
Of course, that might not actually be your issue. Experiment and find out :)

Resources