22

I have set the following in web.config:

<system.web>
  <httpCookies httpOnlyCookies="true" requireSSL="true" />
</system.web>

When I hit the website using an HTTP connection, it redirects to my login page (specifying the scheme as HTTPS). When the browser fetches this page, the response sets some cookies (the ASP.NET session cookie, and the request verification token for my login form):

Set-Cookie: __RequestVerificationToken=IHx8a2zQU374d5CtsoEVW...YtIc1; path=/; HttpOnly Set-Cookie: ASP.NET_SessionId=pfbkkxx2seqhdrxxiodxfbmh; path=/; HttpOnly

These have the HttpOnly flag, which is good - but they do NOT have the secure flag as described here on Wikipedia.

If I then log in, an authentication cookie is created, and this does have the secure flag set:

Set-Cookie:MyWebSite.Authentication=RE3UD...BDW4; path=/; secure; HttpOnly

How can I ensure that the secure flag is set on all my cookies?


UPDATE: as requested, this is the cURL output I get (when fetching the login page directly):

curl https://www.mywebsite.com/Account/Login --verbose --insecure

gives:

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
*   Trying 194.73.98.116...
* Connected to www.mywebsite.com (111.11.11.111) port 443 (#0)
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* TLSv1.2 (OUT), TLS header, Certificate Status (22):
} [5 bytes data]
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
} [512 bytes data]
* TLSv1.2 (IN), TLS handshake, Server hello (2):
{ [85 bytes data]
* TLSv1.2 (IN), TLS handshake, Certificate (11):
{ [2618 bytes data]
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
{ [401 bytes data]
* TLSv1.2 (IN), TLS handshake, Server finished (14):
{ [4 bytes data]
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
} [138 bytes data]
* TLSv1.2 (OUT), TLS change cipher, Client hello (1):
} [1 bytes data]
* TLSv1.2 (OUT), TLS handshake, Finished (20):
} [16 bytes data]
* TLSv1.2 (IN), TLS change cipher, Client hello (1):
{ [1 bytes data]
* TLSv1.2 (IN), TLS handshake, Finished (20):
{ [16 bytes data]
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-SHA384
* ALPN, server did not agree to a protocol
* Server certificate:
*    subject: OU=Domain Control Validated; CN=*.mywebsite.com
*    start date: 2015-07-29 13:37:38 GMT
*    expire date: 2018-07-29 13:37:38 GMT
*    issuer: C=US; ST=Arizona; L=Scottsdale; O=GoDaddy.com, Inc.; OU=http://certs.godaddy.com/repository/; CN=Go Daddy Secure Certificate Authority - G2
*    SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
} [5 bytes data]
> GET /Account/Login HTTP/1.1
> Host: www.mywebsite.com
> User-Agent: curl/7.43.0
> Accept: */*
> 
{ [5 bytes data]
< HTTP/1.1 200 OK
< Cache-Control: no-cache, no-store, must-revalidate
< Pragma: no-cache
< Content-Type: text/html; charset=utf-8
< Expires: -1
< Server: Microsoft-IIS/8.5
< Strict-Transport-Security: max-age=31536000; includeSubdomains; preload
< X-Frame-Options: Deny
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Content-Security-Policy: default-src 'self';script-src 'self' www.google-analytics.com www.googletagmanager.com;object-src 'none';style-src 'self' fonts.googleapis.com;img-src 'self' www.google-analytics.com placehold.it placeholdit.imgix.net data:;media-src 'none';frame-src 'none';font-src 'self' fonts.gstatic.com;connect-src 'self';base-uri 'self';child-src 'none';frame-ancestors 'none';report-uri /WebResource.axd?cspReport=true
< X-Frame-Options: SAMEORIGIN
< X-Frame-Options: SAMEORIGIN
< Set-Cookie: __RequestVerificationToken=bPWxIp8e4F4I0Jt26t5oZyvDM6059tAWSRbgc-b6Df5IMjyYFDD9fJKgRsKVjbtN3EGgtFuHcf1sTjlYSwDWgnlhSUuNW1q5yv3cGMxmEwE1; path=/; HttpOnly
< Date: Fri, 04 Dec 2015 10:03:35 GMT
< Content-Length: 12596
< 
{ [12596 bytes data]
100 12596  100 12596    0     0  31101      0 --:--:-- --:--:-- --:--:-- 31101
* Connection #0 to host www.mywebsite.com left intact
Gary McGill
  • 26,400
  • 25
  • 118
  • 202

1 Answers1

30

The suggested way around this is to secure the session ID and form request cookies when handling page requests, e.g.

// This code will mark the forms authentication cookie and the
// session cookie as Secure.
if (Response.Cookies.Count > 0)
{
    foreach (string s in Response.Cookies.AllKeys)
    {
        if (s == FormsAuthentication.FormsCookieName || s.ToLower() == "asp.net_sessionid")
        {
             Response.Cookies[s].Secure = true;
        }
    }
}

as well as an additional line in the webconfig for securing form auth tokens:

<authentication mode="Forms">
   <forms ...  requireSSL="true" />
</authentication>

2020 - EDIT:

As requested in the comments, it is possible to configure this using only IIS rewrite rules as well, by checking the cookie for the secure flag and adding it if it's not found, e.g.:

<system.webServer>
  <rewrite>
    <outboundRules>
      <rule name="Use only secure cookies" preCondition="Unsecured cookie">
        <match serverVariable="RESPONSE_SET_COOKIE" pattern=".*" negate="false" />
        <action type="Rewrite" value="{R:0}; secure" />
      </rule>
      <preConditions>
        <preCondition name="Unsecured cookie">
          <add input="{RESPONSE_SET_COOKIE}" pattern="." />
          <add input="{RESPONSE_SET_COOKIE}" pattern="; secure" negate="true" />
        </preCondition>
      </preConditions>
    </outboundRules>
  </rewrite>
...
</system.webServer>

Sources: Securing Request-Response cookies - Secure forms authentication via Web.config - How to Enable Secure HttpOnly Cookies in IIS

Moby
  • 650
  • 6
  • 11
  • 1
    Thanks, I'll try this. Although I'm not using Forms authentication (or at least, not configured via web.config), this answer made me look for a similar configuration option within the **ASP.NET Identity** authentication setup code. It seems the `CookieAuthenticationOptions` class has `CookieHttpOnly` and `CookieSecure` properties, the latter of which sounds promising. – Gary McGill Dec 10 '15 at 10:03
  • @MalikKhalil: I beg to differ: https://msdn.microsoft.com/en-us/library/microsoft.owin.security.cookies.cookieauthenticationoptions(v=vs.113).aspx – Gary McGill May 08 '17 at 13:13
  • Ah, got it...Thanks for making me correct @GaryMcGill – Malik Khalil May 08 '17 at 16:57
  • 1
    for a legacy system without access to source code, is there a way to secure this without a code change, maybe even through IIS settings or web.config? @Moby – Malcolm Salvador Mar 11 '20 at 17:07
  • 1
    @MalcolmSalvador Yes there are some solutions using re-write rules. I've now updated my answer to include this if you haven't come across it already, hope it helps if not! – Moby Apr 30 '20 at 09:24
  • Heads up that preconditions pass if *any* header matches (when `negate="false"`) and if *no* header matches (when `negate="true"`) [(source)](https://learn.microsoft.com/en-us/iis/extensions/url-rewrite-module/creating-outbound-rules-for-url-rewrite-module). Using regular conditions as suggested in [this answer](https://stackoverflow.com/a/32904611/7117027) allows you to target specific `Set-Cookie` headers without affecting others. – 321_contact Jul 28 '23 at 19:29