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
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