Merge pull request #3202 from eclipse-vertx/issues/same-site-cookie

Proposal for same site cookie value
This commit is contained in:
Paulo Lopes
2020-02-18 19:39:36 +01:00
committed by GitHub
6 changed files with 148 additions and 1 deletions

View File

@@ -26,6 +26,32 @@ Require client to present authentication, if not presented then negotiations wil
+++
|===
[[CookieSameSite]]
== CookieSameSite
++++
Represents the Cookie SameSite policy to be used. For more info <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#SameSite_cookies">https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#SameSite_cookies</a>.
++++
'''
[cols=">25%,75%"]
[frame="topbot"]
|===
^|Name | Description
|[[NONE]]`NONE`|+++
The browser will send cookies with both cross-site requests and same-site requests.
+++
|[[STRICT]]`STRICT`|+++
The browser will only send cookies for same-site requests (requests originating from the site that set the cookie).
If the request originated from a different URL than the URL of the current location, none of the cookies tagged
with the Strict attribute will be included.
+++
|[[LAX]]`LAX`|+++
Same-site cookies are withheld on cross-site subrequests, such as calls to load images or frames, but will be sent
when a user navigates to the URL from an external site; for example, by following a link.
+++
|===
[[DnsResponseCode]]
== DnsResponseCode

View File

@@ -349,6 +349,19 @@ browser can store them.
Cookies are described by instances of {@link io.vertx.core.http.Cookie}. This allows you to retrieve the name,
value, domain, path and other normal cookie properties.
Same Site Cookies let servers require that a cookie shouldn't be sent with cross-site (where Site is defined by the
registrable domain) requests, which provides some protection against cross-site request forgery attacks. This kind
of cookies are enabled using the setter: {@link io.vertx.core.http.Cookie#setSameSite(CookieSameSite)}.
Same site cookies can have one of 3 values:
* None - The browser will send cookies with both cross-site requests and same-site requests.
* Strict - he browser will only send cookies for same-site requests (requests originating from the site that set the
cookie). If the request originated from a different URL than the URL of the current location, none of the cookies
tagged with the Strict attribute will be included.
* Lax - Same-site cookies are withheld on cross-site subrequests, such as calls to load images or frames, but will be
sent when a user navigates to the URL from an external site; for example, by following a link.
Here's an example of querying and adding cookies:
[source,$lang]

View File

@@ -118,6 +118,15 @@ public interface Cookie {
@Fluent
Cookie setHttpOnly(boolean httpOnly);
/**
* Sets the same site of this cookie.
*
* @param policy The policy should be one of {@link CookieSameSite}.
* @return a reference to this, so the API can be used fluently
*/
@Fluent
Cookie setSameSite(CookieSameSite policy);
/**
* Encode the cookie to a string. This is what is used in the Set-Cookie header
*

View File

@@ -0,0 +1,54 @@
/*
* Copyright (c) 2011-2019 Contributors to the Eclipse Foundation
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
* which is available at https://www.apache.org/licenses/LICENSE-2.0.
*
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
*/
package io.vertx.core.http;
import io.vertx.codegen.annotations.VertxGen;
/**
* Represents the Cookie SameSite policy to be used. For more info <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#SameSite_cookies">https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#SameSite_cookies</a>.
*
* @author <a href="mailto:plopes@redhat.com">Paulo Lopes</a>
*/
@VertxGen
public enum CookieSameSite {
/**
* The browser will send cookies with both cross-site requests and same-site requests.
*/
NONE("None"),
/**
* The browser will only send cookies for same-site requests (requests originating from the site that set the cookie).
* If the request originated from a different URL than the URL of the current location, none of the cookies tagged
* with the Strict attribute will be included.
*/
STRICT("Strict"),
/**
* Same-site cookies are withheld on cross-site subrequests, such as calls to load images or frames, but will be sent
* when a user navigates to the URL from an external site; for example, by following a link.
*/
LAX("Lax");
/**
* Just use a human friendly label instead of the capitalized name.
*/
private final String label;
CookieSameSite(String label) {
this.label = label;
}
@Override
public String toString() {
return label;
}
}

View File

@@ -15,6 +15,7 @@ import io.netty.handler.codec.http.cookie.DefaultCookie;
import io.netty.handler.codec.http.cookie.ServerCookieDecoder;
import io.netty.handler.codec.http.cookie.ServerCookieEncoder;
import io.vertx.core.http.Cookie;
import io.vertx.core.http.CookieSameSite;
import java.util.HashMap;
import java.util.Map;
@@ -63,6 +64,8 @@ public class CookieImpl implements ServerCookie {
private final io.netty.handler.codec.http.cookie.Cookie nettyCookie;
private boolean changed;
private boolean fromUserAgent;
// extension features
private CookieSameSite sameSite;
public CookieImpl(String name, String value) {
this.nettyCookie = new DefaultCookie(name, value);
@@ -136,9 +139,20 @@ public class CookieImpl implements ServerCookie {
return this;
}
@Override
public Cookie setSameSite(final CookieSameSite sameSite) {
this.sameSite = sameSite;
this.changed = true;
return this;
}
@Override
public String encode() {
return ServerCookieEncoder.STRICT.encode(nettyCookie);
if (sameSite != null) {
return ServerCookieEncoder.STRICT.encode(nettyCookie) + "; SameSite=" + sameSite.toString();
} else {
return ServerCookieEncoder.STRICT.encode(nettyCookie);
}
}
public boolean isChanged() {

View File

@@ -5656,6 +5656,37 @@ public abstract class HttpTest extends HttpTestBase {
assertEquals("foo=bar; Path=/somepath; Domain=foo.com; Secure; HTTPOnly", cookie.encode());
}
@Test
public void testCookieSameSiteFieldEncoding() throws Exception {
Cookie cookie = Cookie.cookie("foo", "bar").setSameSite(CookieSameSite.LAX);
assertEquals("foo", cookie.getName());
assertEquals("bar", cookie.getValue());
assertEquals("foo=bar; SameSite=Lax", cookie.encode());
cookie.setSecure(true);
assertEquals("foo=bar; Secure; SameSite=Lax", cookie.encode());
cookie.setHttpOnly(true);
assertEquals("foo=bar; Secure; HTTPOnly; SameSite=Lax", cookie.encode());
}
@Test
public void testCookieSameSiteFieldValidation() throws Exception {
Cookie cookie = Cookie.cookie("foo", "bar");
try {
cookie.setSameSite(CookieSameSite.LAX);
// OK
cookie.setSameSite(CookieSameSite.STRICT);
// OK
cookie.setSameSite(CookieSameSite.NONE);
// OK
cookie.setSameSite(null);
// OK
} catch (RuntimeException e) {
fail();
}
}
@Test
public void testRemoveCookies() throws Exception {
testCookies("foo=bar", req -> {