SameSite cookies and the Open Web Interface for .NET (OWIN)
SameSite
is an IETF draft designed to provide some protection against cross-site request forgery (CSRF) attacks. The SameSite 2019 draft:
- Treats cookies as
SameSite=Lax
by default. - States cookies that explicitly assert
SameSite=None
in order to enable cross-site delivery should be marked asSecure
.
Lax
works for most app cookies. Some forms of authentication like OpenID Connect (OIDC) and WS-Federation default to POST based redirects. The POST based redirects trigger the SameSite
browser protections, so SameSite
is disabled for these components. Most OAuth logins aren't affected due to differences in how the request flows. All other components do not set SameSite
by default and use the clients default behavior (old or new).
The None
parameter causes compatibility problems with clients that implemented the prior 2016 draft standard (for example, iOS 12). See Supporting older browsers in this document.
Each OWIN component that emits cookies needs to decide if SameSite
is appropriate.
For the ASP.NET 4.x version of this article, see Work with SameSite cookies in ASP.NET.
API usage with SameSite
Microsoft.Owin
has its own SameSite
implementation:
- That is not directly dependent on the one in
System.Web
. SameSite
works on all versions targetable by theMicrosoft.Owin
packages, .NET 4.5 and later.- Only the SystemWebCookieManager component directly interacts with the
System.Web
HttpCookie
class.
SystemWebCookieManager
depends on the .NET 4.7.2 System.Web
APIs to enable SameSite
support, and the patches to change the behavior.
The reasons to use SystemWebCookieManager
are outlined in OWIN and System.Web response cookie integration issues. SystemWebCookieManager
is recommended when running on System.Web
.
The following code sets SameSite
to Lax
:
owinContext.Response.Cookies.Append("My Key", "My Value", new CookieOptions()
{
SameSite = SameSiteMode.Lax
});
The following APIs use SameSite
:
- Microsoft.Owin.SameSiteMode
- CookieOptions.SameSite
- CookieAuthenticationOptions Class
- CookieAuthenticationOptions.CookieSameSite
- ICookieManager
- SystemWebCookieManager
- SystemWebChunkingCookieManager
- CookieAuthenticationOptions.CookieManager
- OpenIdConnectAuthenticationOptions.CookieManager
History and changes
Microsoft.Owin never supported the SameSite
2016 draft standard.
Support for the SameSite 2019 draft is only available in Microsoft.Owin
4.1.0 and later. There are no patches for prior versions.
The 2019 draft of the SameSite
specification:
- Is not backwards compatible with the 2016 draft. For more information, see Supporting older browsers in this document.
- Specifies cookies are treated as
SameSite=Lax
by default. - Specifies cookies that explicitly assert
SameSite=None
in order to enable cross-site delivery should be marked asSecure
.None
is a new entry to opt out. - Is scheduled to be enabled by Chrome by default in Feb 2020. Browsers started moving to this standard in 2019.
- Is supported by patches issued as described in KB articles. For more information, see KB articles that support SameSite in .NET Framework.
Supporting older browsers
The 2016 SameSite
standard mandated that unknown values must be treated as SameSite=Strict
values. Apps accessed from older browsers which support the 2016 SameSite
standard may break when they get a SameSite
property with a value of None
. Web apps must implement browser detection if they intend to support older browsers. ASP.NET doesn't implement browser detection because User-Agents values are highly volatile and change frequently. An extension point in ICookieManager allows plugging in User-Agent specific logic.
In Startup.Configuration
, add code similar to the following:
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
// … Your preexisting options …
CookieManager = new SameSiteCookieManager(
new SystemWebCookieManager())
});
// Remaining code removed for brevity.
The preceding code requires the .NET 4.7.2 or later SameSite
patch.
The following code shows an example implementation of SameSiteCookieManager
:
public class SameSiteCookieManager : ICookieManager
{
private readonly ICookieManager _innerManager;
public SameSiteCookieManager() : this(new CookieManager())
{
}
public SameSiteCookieManager(ICookieManager innerManager)
{
_innerManager = innerManager;
}
public void AppendResponseCookie(IOwinContext context, string key, string value,
CookieOptions options)
{
CheckSameSite(context, options);
_innerManager.AppendResponseCookie(context, key, value, options);
}
public void DeleteCookie(IOwinContext context, string key, CookieOptions options)
{
CheckSameSite(context, options);
_innerManager.DeleteCookie(context, key, options);
}
public string GetRequestCookie(IOwinContext context, string key)
{
return _innerManager.GetRequestCookie(context, key);
}
private void CheckSameSite(IOwinContext context, CookieOptions options)
{
if (options.SameSite == Microsoft.Owin.SameSiteMode.None
&& DisallowsSameSiteNone(context))
{
options.SameSite = null;
}
}
In the preceding sample, DisallowsSameSiteNone
is called in the CheckSameSite
method. DisallowsSameSiteNone
is a user method that detects if the user agent doesn't support SameSite
None
:
private void CheckSameSite(IOwinContext context, CookieOptions options)
{
if (options.SameSite == Microsoft.Owin.SameSiteMode.None
&& DisallowsSameSiteNone(context))
{
options.SameSite = null;
}
}
The following code shows a sample DisallowsSameSiteNone
method:
Warning
The following code is for demonstration only:
- It should not be considered complete.
- It is not maintained or supported.
public static bool DisallowsSameSiteNone(IOwinContext context)
{
var userAgent = context.Request.Headers["User-Agent"];
if (string.IsNullOrEmpty(userAgent))
{
return false;
}
// Cover all iOS based browsers here. This includes:
// - Safari on iOS 12 for iPhone, iPod Touch, iPad
// - WkWebview on iOS 12 for iPhone, iPod Touch, iPad
// - Chrome on iOS 12 for iPhone, iPod Touch, iPad
// All of which are broken by SameSite=None, because they use the iOS
// networking stack.
if (userAgent.Contains("CPU iPhone OS 12") ||
userAgent.Contains("iPad; CPU OS 12"))
{
return true;
}
// Cover Mac OS X based browsers that use the Mac OS networking stack.
// This includes:
// - Safari on Mac OS X.
// This does not include:
// - Chrome on Mac OS X
// Because they do not use the Mac OS networking stack.
if (userAgent.Contains("Macintosh; Intel Mac OS X 10_14") &&
userAgent.Contains("Version/") && userAgent.Contains("Safari"))
{
return true;
}
// Cover Chrome 50-69, because some versions are broken by SameSite=None,
// and none in this range require it.
// Note: this covers some pre-Chromium Edge versions,
// but pre-Chromium Edge does not require SameSite=None.
if (userAgent.Contains("Chrome/5") || userAgent.Contains("Chrome/6"))
{
return true;
}
return false;
}
Test apps for SameSite problems
Apps that interact with remote sites such as through third-party login need to:
- Test the interaction on multiple browsers.
- Apply the browser detection and mitigation discussed in this document.
Test web apps using a client version that can opt-in to the new SameSite
behavior. Chrome, Firefox, and Chromium Edge all have new opt-in feature flags that can be used for testing. After your app applies the SameSite
patches, test it with older client versions, especially Safari. For more information, see Supporting older browsers in this document.
Test with Chrome
Chrome 78+ gives misleading results because it has a temporary mitigation in place. The Chrome 78+ temporary mitigation allows cookies less than two minutes old. Chrome 76 or 77 with the appropriate test flags enabled provides more accurate results. To test the new SameSite
behavior toggle chrome://flags/#same-site-by-default-cookies
to Enabled. Older versions of Chrome (75 and below) are reported to fail with the new None
setting. See Supporting older browsers in this document.
Google does not make older chrome versions available. Follow the instructions at Download Chromium to test older versions of Chrome. Do not download Chrome from links provided by searching for older versions of chrome.
Test with Safari
Safari 12 strictly implemented the prior draft and fails when the new None
value is in a cookie. None
is avoided via the browser detection code Supporting older browsers in this document. Test Safari 12, Safari 13, and WebKit based OS style logins using MSAL or whatever library you are using. The problem is dependent on the underlying OS version. OSX Mojave (10.14) and iOS 12 are known to have compatibility problems with the new SameSite
behavior. Upgrading the OS to OSX Catalina (10.15) or iOS 13 fixes the problem. Safari does not currently have an opt-in flag for testing the new spec behavior.
Test with Firefox
Firefox support for the new standard can be tested on version 68+ by opting in on the about:config
page with the feature flag network.cookie.sameSite.laxByDefault
. There haven't been reports of compatibility issues with older versions of Firefox.
Test with Edge browser
Edge supports the old SameSite
standard. Edge version 44 doesn't have any known compatibility problems with the new standard.
Test with Edge (Chromium)
SameSite
flags are set on the edge://flags/#same-site-by-default-cookies
page. No compatibility issues were discovered with Edge Chromium.
Test with Electron
Versions of Electron include older versions of Chromium. For example, the version of Electron used by Teams is Chromium 66, which exhibits the older behavior. You must perform your own compatibility testing with the version of Electron your product uses. See Supporting older browsers in the following section.