2

I have an ASP.NET5/MVC6 website and a corresponding OData Service both hosted on azure.

As an experiment I am attempting to set a cookie on the website (xxxportal.azurewebsites.net) and access the cookie in the OData Service (xxxservice.azurewebsites.net).

I am setting the cookie in the AccountController with the following code.

  Response
    .Cookies
    .Append(
        "MyCookie", 
        "MyCookieValue", 
         new Microsoft.AspNet.Http.CookieOptions 
         { 
              Domain = "azurewebsites.net", 
              Path="/", 
              HttpOnly = true, 
              Expires = DateTime.MaxValue 
          });

When I run Fiddler I can see that the cookie is set.

HTTP/1.1 302 Found
Cache-Control: no-cache
Pragma: no-cache
Content-Length: 0
Expires: -1
Location: /
Server: Microsoft-IIS/8.0
Set-Cookie: .AspNet.Microsoft.AspNet.Identity.Application=CfDJ8B0ErUB73iFHtoSI2Xl7xKIM6bTPDudGXYwJ--ia_gE416t-asXaZS2_hg8C4OF1NqMLy39hehNKwRvJKpuYP44h_Cb5sLN3fMV2iVoIkHa7O9JNPXVI8EmZXhx3xQNUNnSo5zMWoIuZjXUJG4WoWYUFqPmUVbjaaAQgT6nCxSPi34kwBBLRXQbHwZxGVJgpTDDHFKSthzYCk8wYcBwh-Izjc7zja8mXuEtVDXyBWGWtFFkJSae2DltVWShWfPG7Z_jtD7909aLh0eGzpZQY7pDxftYgfoLZPwnbDZVvrx65RGhFk6A8N3jv3CMdIJDtxjO8m367fnsNJzve2IDn7npDf1OOvFCM9MIWfZ67Ns8w4vV3eIgGO6kAs_gop3mDphFH8ZRW8RwKhBiHhOoA76IVzgkeZWvZXvQeJDbUvTGfyMjgHQVnzWQJnvg5Gvz6JGNrpM8Yv7JxYrXVFrbtM6k7Aw83Cf9_JQAYBCSz6URYCKX3O_FApNCrhGsNtp4RkMdBa-uFnuQZ4ZgnRMMGuavMJLQ7zLz_KzPjBS3H3iyaYUaWVvXx5QgWWwHoWlIV0Yb_Ba_hxCVmoOSMQMXkkqSTHOxs2WZC5EcRF7zJprUVxR3FAR8c4_AhG3r9t5hAtwEE5l6T2oxOhgqe7Xkn1rY; path=/; httponly
Set-Cookie: MyCookie=MyCookieValue; expires=Fri, 31 Dec 9999 23:59:59 GMT; domain=azurewebsites.net; path=/; httponly
X-Powered-By: ASP.NET
Date: Wed, 30 Mar 2016 04:34:48 GMT

But on the request to the ODataService the cookie is not being sent. (I also don't see it sent on the subsequent requests to the same site.)

GET http://xxxservice.azurewebsites.net/Patients HTTP/1.1
Host: xxxservice.azurewebsites.net
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
Accept: application/json, text/plain, */*
Origin: http://xxxportal.azurewebsites.net
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36
Referer: http://xxxportal.azurewebsites.net/
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8

What am I doing wrong?

EDIT: Additional Information I'm mostly testing from Chrome but seems to be the same from IE11. I can see the cookies in Chrome. Cookies from Chrome

RonnBlack
  • 3,570
  • 4
  • 23
  • 25
  • What happens if you remove the path? – Erik Philips Mar 30 '16 at 05:12
  • Removing Path defaults to "/" so it is the same. If I remove the Domain specification it is sent back to the same server on subsequent requests but is not sent to the OData Service (as expected). I think the problem may be that the "domain=azurewebsites.net" attribute is rendered to the output stream in lowercase. I think it is supposed to be "Domain=azurewebsites.net" – RonnBlack Mar 30 '16 at 05:28
  • 2
    I'm almost *positive* you cannot set the cookie on another domain (you're not setting it on a subdomain of the site you're hosted at). That would be a huge security flaw. – Rob Mar 30 '16 at 05:36
  • 3
    To expand, you could set a cookie for `xxxportal.azurewebsites.net` and then have your service up at `xxxservice.xxxportal.azurewebsites.net` - but you cannot set a cookie on `azurewebsite.net` from `xxxportal.azurewebsites.net`, neither can you set it on `xxxservice.azurewebsites.net` from `xxxportal.azurewebsites.net`. – Rob Mar 30 '16 at 05:39
  • 1
    @kienct89: using .azurewebsites.net (starting with ".") didn't make any difference and in Gumbo's answer to this post (http://stackoverflow.com/questions/5258126/domain-set-cookie-for-subdomain) RFC6265 indicates that it is handled the same. – RonnBlack Mar 30 '16 at 05:58
  • You should be able to share cookies, assuming you both set the domain correctly (they need the same top-level domain, and the domain needs to begin with a `.`) and that the cookies are encrypted using the same keys. You accomplish the latter bit in IIS (when self-hosting) by setting the [machineKey](https://msdn.microsoft.com/en-us/library/w8h3skw9%28v=vs.100%29.aspx) to be the same for both sites; I'm not sure how that works with Azure and/or OWIN. – Tieson T. Mar 30 '16 at 06:05
  • @Rob: You may be right but I don't see where it says that in RFC6265. If I look at the domain matching rules (5.1.3) and the domain attribute (5.2.3) in the RFC it looks like `domain=azurewebsites.net` from `xxxportal.azurewebsites.net` it should be ok. To test your example I changed my Set-Cookie to use Domain: `xxxportal.azurewebsites.net` and then put another call to `xxxservice.xxxportal.azurewebsites.net` (which currently doesn't exist) and looking at the network traffic I don't see it attempting to send the cookie. – RonnBlack Mar 30 '16 at 07:14
  • @RonnBlack What browser are you using? Are you able to inspect the actual cookies stored in your browser? If so, can you show the details of that? – Rob Mar 30 '16 at 07:31
  • Can you also try something like this: `Set-Cookie: SID=31d4d96e407aad42; Path=/; Domain=xxxportal.azurewebsites.net` (long shot, but possible) – Rob Mar 30 '16 at 07:36

3 Answers3

3

TL;DR

As founded by OP and answered here, he is falling in another case than the ones myi nitial answer was addressing. His case implies CORS, and probably a preflight request. Then his case can be handled by adding the Access-Control-Allow-Credentials in the resource preflight response and in the Set-Cookieresponse too. It seems to overrides the Cookie rules I explain below.

Here is my old answer, still true but not explaining the whole thing:

Microsoft has banned cookies on azurewebsites.net top domain by including it in public suffix list.

Full details

According to RFC 6265, setting a cookie for your site top domain is supported:

The user agent will reject cookies unless the Domain attribute specifies a scope for the cookie that would include the origin server. For example, the user agent will accept a cookie with a Domain attribute of "example.com" or of "foo.example.com" from foo.example.com, but the user agent will not accept a cookie with a Domain attribute of "bar.example.com" or of "baz.foo.example.com".

So, if your set-cookie on domain yourdomain.net occurs from a request on a yourdomain.net sub-domain, this cookie should be accepted.

And as written a bit above:

The Domain attribute specifies those hosts to which the cookie will be sent. For example, if the value of the Domain attribute is "example.com", the user agent will include the cookie in the Cookie header when making HTTP requests to example.com, www.example.com, and www.corp.example.com.

So your accepted cookie should be sent to any subdomain of yourdomain.net on subsequent requests.

But as per §5.3, point 5, a domain being a public suffix should be ignored if the http hostname does not match it entirely. (Following this rule depends on client choices and configuration, but browsers usually follow that rule.)

5.   If the user agent is configured to reject "public suffixes" and
     the domain-attribute is a public suffix:

        If the domain-attribute is identical to the canonicalized
        request-host:

           Let the domain-attribute be the empty string.

        Otherwise:

           Ignore the cookie entirely and abort these steps.

Public suffix list handled by Mozilla does list azurewebsites.net.

// Microsoft : http://microsoft.com
// Submitted by Barry Dorrans
azurewebsites.net
azure-mobile.net
cloudapp.net

So, and that is quite logic, Microsoft has actively banned cookies set on azurewebsites.net from sub domains.

Addressing OP findings and own answer:

Due to azurewebsites.net being in public prefix list, Set-Cookie on azurewebsites.net from a azurewebsites.net sub-domain should be ignored.

But if the response setting the cookie include additional CORS headers (Access-Control-Allow-Credentials along with more classical ones) allowing explicitly credential sharing, the CORS header seems to take precedence and causes the browser to accept the cookie even on public suffix.

I was unfortunately unable to find any spec about that behavior. If "CORS header allowing something forbidden by cookie rules" is actually an unspecified case, this may change, and it is then risky to rely on this.

If any preflight occurs on the request needing to transmit the shared cookie, then the preflight response will very probably have to include the Access-Control-Allow-Credentials header too.

Additional consideration

CORS spec recommends avoiding browser based automatic credentials forwarding mechanisms.

Given the difficulty of avoiding such vulnerabilities in multi-origin interactions it is recommended that, instead of using user credentials automatically attached to the request by the user agent, security tokens which specify the particular capabilities and resources authorized be passed as part of the explicit content of a request. OAuth again provides an example of such a pattern.

It would probably be more robust to follow this recommendation.

Frédéric
  • 8,372
  • 2
  • 51
  • 102
  • 1
    I initially thought this was the answer but it turns out to not be correct (Even though this is excellent information). The real problem turns out that the server needs to send the Access-Control-Allow-Credentials in addition to the Access-Control-Allow-Origin header in the response. – RonnBlack Apr 06 '16 at 04:15
  • I was believing this `Access-Control-Allow-Credentials` to be required only for transmitting `Authenticate` header, but [spec](https://www.w3.org/TR/cors/#user-credentials) does include in 'credentials' the `Cookie` header. But now look at this [site 1](https://champagnexavierloriot.plugwine.com/Login) and this other [site 2](https://chateaupesquie.plugwine.com/Vins). Create an account on site 1, browse site 2: you are already connected, because the auth cookie is on their root domain. And they do not emit any cors header. Recently re-tested with Chrome 49 and Firefox 45. – Frédéric Apr 06 '16 at 07:32
1

Initially thought @Frédéric had the correct answer but I was unable to get it to work with dedicated servers.

Eventually I came across this post Cross domain JQuery ajax call with credentials. The accepted answer by @Emily pointed out that the server must send the following Headers.

Access-Control-Allow-Origin: http://www.mywebsite.com
Access-Control-Allow-Credentials: true

Another important piece is that if you include the Allow-Credentials header then the Allow-Origin cannot be "*".

HTTP access control (CORS)

Community
  • 1
  • 1
RonnBlack
  • 3,570
  • 4
  • 23
  • 25
  • Interesting finding, thanks for your notice on my answer. My [answer comment example](/a/36315292/1178314) works without any CORS header. So I guess your case is the result of both my answer reasons and yours. I have edit my answer accordingly. – Frédéric Apr 06 '16 at 08:32
0

I couldn't find the actual doco on the set cookie header tag however.

The Domain and Path attributes define the scope of the cookie. They essentially tell the browser what website the cookie belongs to. For obvious security reasons, cookies can only be set on the current resource's top domain and its sub domains, and not for another domain and its sub domains. For example, the website example.org cannot set a cookie that has a domain of foo.com because this would allow the example.org website to control the cookies of foo.com.

The above is an extract from wikipedia. ->https://en.wikipedia.org/wiki/HTTP_cookie

I believe rob is correct in saying that you cant set a cookie from another domain as its a security issue.

Spaceman
  • 1,181
  • 11
  • 38