Skip to content
This repository was archived by the owner on Dec 13, 2018. It is now read-only.

Commit 769da5f

Browse files
committed
Add SameSitePolicy to CookiePolicyMiddleware
1 parent 2a4a7dd commit 769da5f

File tree

11 files changed

+165
-51
lines changed

11 files changed

+165
-51
lines changed

shared/Microsoft.AspNetCore.ChunkingCookieManager.Sources/ChunkingCookieManager.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ internal class ChunkingCookieManager
3333
/// <summary>
3434
/// The default maximum size of characters in a cookie to send back to the client.
3535
/// </summary>
36-
public const int DefaultChunkSize = 4070;
36+
public const int DefaultChunkSize = 4050;
3737

3838
private const string ChunkKeySuffix = "C";
3939
private const string ChunkCountPrefix = "chunks-";
@@ -42,7 +42,7 @@ public ChunkingCookieManager()
4242
{
4343
// Lowest common denominator. Safari has the lowest known limit (4093), and we leave little extra just in case.
4444
// See http://browsercookielimits.x64.me/.
45-
// Leave at least 20 in case CookiePolicy tries to add 'secure' and/or 'httponly'.
45+
// Leave at least 40 in case CookiePolicy tries to add 'secure', 'samesite=strict' and/or 'httponly'.
4646
ChunkSize = DefaultChunkSize;
4747
ThrowForPartialCookies = true;
4848
}
@@ -166,6 +166,7 @@ public void AppendResponseCookie(HttpContext context, string key, string value,
166166
{
167167
Domain = options.Domain,
168168
Expires = options.Expires,
169+
SameSite = (Net.Http.Headers.SameSiteMode)options.SameSite,
169170
HttpOnly = options.HttpOnly,
170171
Path = options.Path,
171172
Secure = options.Secure,
@@ -284,6 +285,7 @@ public void DeleteCookie(HttpContext context, string key, CookieOptions options)
284285
{
285286
Path = options.Path,
286287
Domain = options.Domain,
288+
SameSite = options.SameSite,
287289
Expires = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc),
288290
});
289291

@@ -297,6 +299,7 @@ public void DeleteCookie(HttpContext context, string key, CookieOptions options)
297299
{
298300
Path = options.Path,
299301
Domain = options.Domain,
302+
SameSite = options.SameSite,
300303
Expires = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc),
301304
});
302305
}

src/Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationHandler.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ private CookieOptions BuildCookieOptions()
179179
var cookieOptions = new CookieOptions
180180
{
181181
Domain = Options.CookieDomain,
182+
SameSite = Options.CookieSameSite,
182183
HttpOnly = Options.CookieHttpOnly,
183184
Path = Options.CookiePath ?? (OriginalPathBase.HasValue ? OriginalPathBase.ToString() : "/"),
184185
};

src/Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationOptions.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
using System;
55
using Microsoft.AspNetCore.DataProtection;
66
using Microsoft.AspNetCore.Http;
7-
using Microsoft.Extensions.Options;
87

98
namespace Microsoft.AspNetCore.Authentication.Cookies
109
{
@@ -23,6 +22,7 @@ public CookieAuthenticationOptions()
2322
ReturnUrlParameter = CookieAuthenticationDefaults.ReturnUrlParameter;
2423
ExpireTimeSpan = TimeSpan.FromDays(14);
2524
SlidingExpiration = true;
25+
CookieSameSite = SameSiteMode.Strict;
2626
CookieHttpOnly = true;
2727
CookieSecure = CookieSecurePolicy.SameAsRequest;
2828
Events = new CookieAuthenticationEvents();
@@ -57,6 +57,12 @@ public string CookieName
5757
/// </summary>
5858
public string CookiePath { get; set; }
5959

60+
/// <summary>
61+
/// Determines if the browser should allow the cookie to be attached to same-site or cross-site requests. The
62+
/// default is Strict, which means the cookie is only allowed to be attached to same-site requests.
63+
/// </summary>
64+
public SameSiteMode CookieSameSite { get; set; }
65+
6066
/// <summary>
6167
/// Determines if the browser should allow the cookie to be accessed by client-side javascript. The
6268
/// default is true, which means the cookie will only be passed to http requests and is not made available

src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectHandler.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -899,6 +899,7 @@ private void WriteNonceCookie(string nonce)
899899
new CookieOptions
900900
{
901901
HttpOnly = true,
902+
SameSite = Http.SameSiteMode.Lax,
902903
Secure = Request.IsHttps,
903904
Expires = Clock.UtcNow.Add(Options.ProtocolValidator.NonceLifetime)
904905
});
@@ -930,6 +931,7 @@ private string ReadNonceCookie(string nonce)
930931
var cookieOptions = new CookieOptions
931932
{
932933
HttpOnly = true,
934+
SameSite = Http.SameSiteMode.Lax,
933935
Secure = Request.IsHttps
934936
};
935937

src/Microsoft.AspNetCore.Authentication.Twitter/TwitterHandler.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ protected override async Task<AuthenticateResult> HandleRemoteAuthenticateAsync(
8383
var cookieOptions = new CookieOptions
8484
{
8585
HttpOnly = true,
86+
SameSite = SameSiteMode.Lax,
8687
Secure = Request.IsHttps
8788
};
8889

@@ -160,6 +161,7 @@ protected override async Task HandleUnauthorizedAsync(ChallengeContext context)
160161
var cookieOptions = new CookieOptions
161162
{
162163
HttpOnly = true,
164+
SameSite = SameSiteMode.Lax,
163165
Secure = Request.IsHttps,
164166
Expires = Clock.UtcNow.Add(Options.RemoteAuthenticationTimeout),
165167
};

src/Microsoft.AspNetCore.Authentication/RemoteAuthenticationHandler.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ protected virtual void GenerateCorrelationId(AuthenticationProperties properties
203203
var cookieOptions = new CookieOptions
204204
{
205205
HttpOnly = true,
206+
SameSite = SameSiteMode.Lax,
206207
Secure = Request.IsHttps,
207208
Expires = Clock.UtcNow.Add(Options.RemoteAuthenticationTimeout),
208209
};
@@ -242,6 +243,7 @@ protected virtual bool ValidateCorrelationId(AuthenticationProperties properties
242243
var cookieOptions = new CookieOptions
243244
{
244245
HttpOnly = true,
246+
SameSite = SameSiteMode.Lax,
245247
Secure = Request.IsHttps
246248
};
247249
Response.Cookies.Delete(cookieName, cookieOptions);

src/Microsoft.AspNetCore.CookiePolicy/CookiePolicyMiddleware.cs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ public IResponseCookies Cookies
7474

7575
private bool PolicyRequiresCookieOptions()
7676
{
77-
return Policy.HttpOnly != HttpOnlyPolicy.None || Policy.Secure != CookieSecurePolicy.None;
77+
return Policy.MinimumSameSitePolicy != SameSiteMode.None || Policy.HttpOnly != HttpOnlyPolicy.None || Policy.Secure != CookieSecurePolicy.None;
7878
}
7979

8080
public void Append(string key, string value)
@@ -151,6 +151,22 @@ private void ApplyPolicy(CookieOptions options)
151151
default:
152152
throw new InvalidOperationException();
153153
}
154+
switch (Policy.MinimumSameSitePolicy)
155+
{
156+
case SameSiteMode.None:
157+
break;
158+
case SameSiteMode.Lax:
159+
if (options.SameSite == SameSiteMode.None)
160+
{
161+
options.SameSite = SameSiteMode.Lax;
162+
}
163+
break;
164+
case SameSiteMode.Strict:
165+
options.SameSite = SameSiteMode.Strict;
166+
break;
167+
default:
168+
throw new InvalidOperationException($"Unrecognized {nameof(SameSiteMode)} value {Policy.MinimumSameSitePolicy.ToString()}");
169+
}
154170
switch (Policy.HttpOnly)
155171
{
156172
case HttpOnlyPolicy.Always:
@@ -159,7 +175,7 @@ private void ApplyPolicy(CookieOptions options)
159175
case HttpOnlyPolicy.None:
160176
break;
161177
default:
162-
throw new InvalidOperationException();
178+
throw new InvalidOperationException($"Unrecognized {nameof(HttpOnlyPolicy)} value {Policy.HttpOnly.ToString()}");
163179
}
164180
}
165181
}

src/Microsoft.AspNetCore.CookiePolicy/CookiePolicyOptions.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ namespace Microsoft.AspNetCore.Builder
1212
/// </summary>
1313
public class CookiePolicyOptions
1414
{
15+
/// <summary>
16+
/// Affects the cookie's same site attribute.
17+
/// </summary>
18+
public SameSiteMode MinimumSameSitePolicy { get; set; } = SameSiteMode.Strict;
19+
1520
/// <summary>
1621
/// Affects whether cookies must be HttpOnly.
1722
/// </summary>

test/Microsoft.AspNetCore.Authentication.Test/CookieTests.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ public async Task SignInCausesDefaultCookieToBeCreated()
136136
Assert.StartsWith("TestCookie=", setCookie);
137137
Assert.Contains("; path=/", setCookie);
138138
Assert.Contains("; httponly", setCookie);
139+
Assert.Contains("; samesite=", setCookie);
139140
Assert.DoesNotContain("; expires=", setCookie);
140141
Assert.DoesNotContain("; domain=", setCookie);
141142
Assert.DoesNotContain("; secure", setCookie);
@@ -206,6 +207,7 @@ public async Task CookieOptionsAlterSetCookieHeader()
206207
o.CookiePath = "/foo";
207208
o.CookieDomain = "another.com";
208209
o.CookieSecure = CookieSecurePolicy.Always;
210+
o.CookieSameSite = SameSiteMode.None;
209211
o.CookieHttpOnly = true;
210212
}, SignInAsAlice, baseAddress: new Uri("http://example.com/base"));
211213

@@ -217,12 +219,14 @@ public async Task CookieOptionsAlterSetCookieHeader()
217219
Assert.Contains(" path=/foo", setCookie1);
218220
Assert.Contains(" domain=another.com", setCookie1);
219221
Assert.Contains(" secure", setCookie1);
222+
Assert.DoesNotContain(" samesite", setCookie1);
220223
Assert.Contains(" httponly", setCookie1);
221224

222225
var server2 = CreateServer(o =>
223226
{
224227
o.CookieName = "SecondCookie";
225228
o.CookieSecure = CookieSecurePolicy.None;
229+
o.CookieSameSite = SameSiteMode.Strict;
226230
o.CookieHttpOnly = false;
227231
}, SignInAsAlice, baseAddress: new Uri("http://example.com/base"));
228232

@@ -232,6 +236,7 @@ public async Task CookieOptionsAlterSetCookieHeader()
232236

233237
Assert.Contains("SecondCookie=", setCookie2);
234238
Assert.Contains(" path=/base", setCookie2);
239+
Assert.Contains(" samesite=strict", setCookie2);
235240
Assert.DoesNotContain(" domain=", setCookie2);
236241
Assert.DoesNotContain(" secure", setCookie2);
237242
Assert.DoesNotContain(" httponly", setCookie2);

test/Microsoft.AspNetCore.ChunkingCookieManager.Sources.Test/CookieChunkingTests.cs

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public void AppendLargeCookie_Appended()
1818
new ChunkingCookieManager() { ChunkSize = null }.AppendResponseCookie(context, "TestCookie", testString, new CookieOptions());
1919
var values = context.Response.Headers["Set-Cookie"];
2020
Assert.Equal(1, values.Count);
21-
Assert.Equal("TestCookie=" + testString + "; path=/", values[0]);
21+
Assert.Equal("TestCookie=" + testString + "; path=/; samesite=lax", values[0]);
2222
}
2323

2424
[Fact]
@@ -27,20 +27,20 @@ public void AppendLargeCookieWithLimit_Chunked()
2727
HttpContext context = new DefaultHttpContext();
2828

2929
string testString = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
30-
new ChunkingCookieManager() { ChunkSize = 30 }.AppendResponseCookie(context, "TestCookie", testString, new CookieOptions());
30+
new ChunkingCookieManager() { ChunkSize = 44 }.AppendResponseCookie(context, "TestCookie", testString, new CookieOptions());
3131
var values = context.Response.Headers["Set-Cookie"];
3232
Assert.Equal(9, values.Count);
3333
Assert.Equal<string[]>(new[]
3434
{
35-
"TestCookie=chunks-8; path=/",
36-
"TestCookieC1=abcdefgh; path=/",
37-
"TestCookieC2=ijklmnop; path=/",
38-
"TestCookieC3=qrstuvwx; path=/",
39-
"TestCookieC4=yz012345; path=/",
40-
"TestCookieC5=6789ABCD; path=/",
41-
"TestCookieC6=EFGHIJKL; path=/",
42-
"TestCookieC7=MNOPQRST; path=/",
43-
"TestCookieC8=UVWXYZ; path=/",
35+
"TestCookie=chunks-8; path=/; samesite=lax",
36+
"TestCookieC1=abcdefgh; path=/; samesite=lax",
37+
"TestCookieC2=ijklmnop; path=/; samesite=lax",
38+
"TestCookieC3=qrstuvwx; path=/; samesite=lax",
39+
"TestCookieC4=yz012345; path=/; samesite=lax",
40+
"TestCookieC5=6789ABCD; path=/; samesite=lax",
41+
"TestCookieC6=EFGHIJKL; path=/; samesite=lax",
42+
"TestCookieC7=MNOPQRST; path=/; samesite=lax",
43+
"TestCookieC8=UVWXYZ; path=/; samesite=lax",
4444
}, values);
4545
}
4646

@@ -116,14 +116,14 @@ public void DeleteChunkedCookieWithOptions_AllDeleted()
116116
Assert.Equal(8, cookies.Count);
117117
Assert.Equal(new[]
118118
{
119-
"TestCookie=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/",
120-
"TestCookieC1=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/",
121-
"TestCookieC2=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/",
122-
"TestCookieC3=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/",
123-
"TestCookieC4=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/",
124-
"TestCookieC5=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/",
125-
"TestCookieC6=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/",
126-
"TestCookieC7=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/",
119+
"TestCookie=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/; samesite=lax",
120+
"TestCookieC1=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/; samesite=lax",
121+
"TestCookieC2=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/; samesite=lax",
122+
"TestCookieC3=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/; samesite=lax",
123+
"TestCookieC4=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/; samesite=lax",
124+
"TestCookieC5=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/; samesite=lax",
125+
"TestCookieC6=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/; samesite=lax",
126+
"TestCookieC7=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/; samesite=lax",
127127
}, cookies);
128128
}
129129
}

0 commit comments

Comments
 (0)