You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2019/05/20 08:15:38 UTC
[tomcat] branch 8.5.x updated: Implement same-site cookie header.
Patch provided by John Kelly.
This is an automated email from the ASF dual-hosted git repository.
markt pushed a commit to branch 8.5.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/8.5.x by this push:
new f02fe40 Implement same-site cookie header. Patch provided by John Kelly.
f02fe40 is described below
commit f02fe40ae481d73553d0d64d3e54f78d8cf8ad2c
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Mon May 20 09:15:08 2019 +0100
Implement same-site cookie header. Patch provided by John Kelly.
---
.../tomcat/util/http/CookieProcessorBase.java | 10 +++
.../tomcat/util/http/LegacyCookieProcessor.java | 8 ++
.../tomcat/util/http/LocalStrings.properties | 1 +
.../tomcat/util/http/Rfc6265CookieProcessor.java | 7 ++
.../apache/tomcat/util/http/SameSiteCookies.java | 59 +++++++++++++
.../util/http/TestCookieProcessorGeneration.java | 49 +++++++++++
.../tomcat/util/http/TestSameSiteCookies.java | 97 ++++++++++++++++++++++
webapps/docs/changelog.xml | 4 +
webapps/docs/config/cookie-processor.xml | 34 +++++++-
9 files changed, 267 insertions(+), 2 deletions(-)
diff --git a/java/org/apache/tomcat/util/http/CookieProcessorBase.java b/java/org/apache/tomcat/util/http/CookieProcessorBase.java
index dceb573..3cb5430 100644
--- a/java/org/apache/tomcat/util/http/CookieProcessorBase.java
+++ b/java/org/apache/tomcat/util/http/CookieProcessorBase.java
@@ -42,4 +42,14 @@ public abstract class CookieProcessorBase implements CookieProcessor {
static {
ANCIENT_DATE = COOKIE_DATE_FORMAT.get().format(new Date(10000));
}
+
+ private SameSiteCookies sameSiteCookies = SameSiteCookies.NONE;
+
+ public SameSiteCookies getSameSiteCookies() {
+ return sameSiteCookies;
+ }
+
+ public void setSameSiteCookies(String sameSiteCookies) {
+ this.sameSiteCookies = SameSiteCookies.fromString(sameSiteCookies);
+ }
}
diff --git a/java/org/apache/tomcat/util/http/LegacyCookieProcessor.java b/java/org/apache/tomcat/util/http/LegacyCookieProcessor.java
index 16cee6c..e792875 100644
--- a/java/org/apache/tomcat/util/http/LegacyCookieProcessor.java
+++ b/java/org/apache/tomcat/util/http/LegacyCookieProcessor.java
@@ -324,6 +324,14 @@ public final class LegacyCookieProcessor extends CookieProcessorBase {
if (cookie.isHttpOnly()) {
buf.append("; HttpOnly");
}
+
+ SameSiteCookies sameSiteCookiesValue = getSameSiteCookies();
+
+ if (!sameSiteCookiesValue.equals(SameSiteCookies.NONE)) {
+ buf.append("; SameSite=");
+ buf.append(sameSiteCookiesValue.getValue());
+ }
+
return buf.toString();
}
diff --git a/java/org/apache/tomcat/util/http/LocalStrings.properties b/java/org/apache/tomcat/util/http/LocalStrings.properties
index 4b91257..5eab413 100644
--- a/java/org/apache/tomcat/util/http/LocalStrings.properties
+++ b/java/org/apache/tomcat/util/http/LocalStrings.properties
@@ -26,6 +26,7 @@ parameters.noequal=Parameter starting at position [{0}] and ending at position [
parameters.fallToDebug=\n Note: further occurrences of Parameter errors will be logged at DEBUG level.
cookies.invalidCookieToken=Cookies: Invalid cookie. Value not a token or quoted value
+cookies.invalidSameSiteCookies=Unknown setting [{0}], must be one of: none, lax, strict. Default value is none.
cookies.invalidSpecial=Cookies: Unknown Special Cookie
cookies.fallToDebug=\n Note: further occurrences of Cookie errors will be logged at DEBUG level.
cookies.maxCountFail=More than the maximum allowed number of cookies, [{0}], were detected.
diff --git a/java/org/apache/tomcat/util/http/Rfc6265CookieProcessor.java b/java/org/apache/tomcat/util/http/Rfc6265CookieProcessor.java
index a0e54f3..0d81a9b 100644
--- a/java/org/apache/tomcat/util/http/Rfc6265CookieProcessor.java
+++ b/java/org/apache/tomcat/util/http/Rfc6265CookieProcessor.java
@@ -162,6 +162,13 @@ public class Rfc6265CookieProcessor extends CookieProcessorBase {
header.append("; HttpOnly");
}
+ SameSiteCookies sameSiteCookiesValue = getSameSiteCookies();
+
+ if (!sameSiteCookiesValue.equals(SameSiteCookies.NONE)) {
+ header.append("; SameSite=");
+ header.append(sameSiteCookiesValue.getValue());
+ }
+
return header.toString();
}
diff --git a/java/org/apache/tomcat/util/http/SameSiteCookies.java b/java/org/apache/tomcat/util/http/SameSiteCookies.java
new file mode 100644
index 0000000..c79fbc1
--- /dev/null
+++ b/java/org/apache/tomcat/util/http/SameSiteCookies.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.util.http;
+
+import org.apache.tomcat.util.res.StringManager;
+
+public enum SameSiteCookies {
+
+ /**
+ * Don't set the SameSite cookie attribute. Cookie is always sent
+ */
+ NONE("None"),
+
+ /**
+ * Cookie is only sent on same-site requests and cross-site top level navigation GET requests
+ */
+ LAX("Lax"),
+
+ /**
+ * Prevents the cookie from being sent by the browser in all cross-site requests
+ */
+ STRICT("Strict");
+
+ private static final StringManager sm = StringManager.getManager(SameSiteCookies.class);
+
+ private final String value;
+
+ SameSiteCookies(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public static SameSiteCookies fromString(String value) {
+ for (SameSiteCookies sameSiteCookies : SameSiteCookies.values()) {
+ if (sameSiteCookies.getValue().equalsIgnoreCase(value)) {
+ return sameSiteCookies;
+ }
+ }
+
+ throw new IllegalStateException(sm.getString("cookies.invalidSameSiteCookies", value));
+ }
+}
diff --git a/test/org/apache/tomcat/util/http/TestCookieProcessorGeneration.java b/test/org/apache/tomcat/util/http/TestCookieProcessorGeneration.java
index f8a1d58..96bc238 100644
--- a/test/org/apache/tomcat/util/http/TestCookieProcessorGeneration.java
+++ b/test/org/apache/tomcat/util/http/TestCookieProcessorGeneration.java
@@ -254,6 +254,55 @@ public class TestCookieProcessorGeneration {
doV1TestPath("exa\tmple", "foo=bar; Version=1; Path=\"exa\tmple\"", null);
}
+ @Test
+ public void testSameSiteCookies() {
+ LegacyCookieProcessor legacy = new LegacyCookieProcessor();
+ Rfc6265CookieProcessor rfc6265 = new Rfc6265CookieProcessor();
+
+ Cookie cookie = new Cookie("foo", "bar");
+
+ Assert.assertEquals("foo=bar", legacy.generateHeader(cookie));
+ Assert.assertEquals("foo=bar", rfc6265.generateHeader(cookie));
+
+ legacy.setSameSiteCookies("none");
+ rfc6265.setSameSiteCookies("none");
+
+ Assert.assertEquals("foo=bar", legacy.generateHeader(cookie));
+ Assert.assertEquals("foo=bar", rfc6265.generateHeader(cookie));
+
+ legacy.setSameSiteCookies("lax");
+ rfc6265.setSameSiteCookies("lax");
+
+ Assert.assertEquals("foo=bar; SameSite=Lax", legacy.generateHeader(cookie));
+ Assert.assertEquals("foo=bar; SameSite=Lax", rfc6265.generateHeader(cookie));
+
+ legacy.setSameSiteCookies("strict");
+ rfc6265.setSameSiteCookies("strict");
+
+ Assert.assertEquals("foo=bar; SameSite=Strict", legacy.generateHeader(cookie));
+ Assert.assertEquals("foo=bar; SameSite=Strict", rfc6265.generateHeader(cookie));
+
+ cookie.setSecure(true);
+ cookie.setHttpOnly(true);
+
+ legacy.setSameSiteCookies("none");
+ rfc6265.setSameSiteCookies("none");
+
+ Assert.assertEquals("foo=bar; Secure; HttpOnly", legacy.generateHeader(cookie));
+ Assert.assertEquals("foo=bar; Secure; HttpOnly", rfc6265.generateHeader(cookie));
+
+ legacy.setSameSiteCookies("lax");
+ rfc6265.setSameSiteCookies("lax");
+
+ Assert.assertEquals("foo=bar; Secure; HttpOnly; SameSite=Lax", legacy.generateHeader(cookie));
+ Assert.assertEquals("foo=bar; Secure; HttpOnly; SameSite=Lax", rfc6265.generateHeader(cookie));
+
+ legacy.setSameSiteCookies("strict");
+ rfc6265.setSameSiteCookies("strict");
+
+ Assert.assertEquals("foo=bar; Secure; HttpOnly; SameSite=Strict", legacy.generateHeader(cookie));
+ Assert.assertEquals("foo=bar; Secure; HttpOnly; SameSite=Strict", rfc6265.generateHeader(cookie));
+ }
private void doTest(Cookie cookie, String expected) {
doTest(cookie, expected, expected);
diff --git a/test/org/apache/tomcat/util/http/TestSameSiteCookies.java b/test/org/apache/tomcat/util/http/TestSameSiteCookies.java
new file mode 100644
index 0000000..60cc3a8
--- /dev/null
+++ b/test/org/apache/tomcat/util/http/TestSameSiteCookies.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.util.http;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+
+public class TestSameSiteCookies {
+
+ @Test
+ public void testNone() {
+ SameSiteCookies attribute = SameSiteCookies.NONE;
+
+ Assert.assertEquals("None", attribute.getValue());
+ Assert.assertEquals(SameSiteCookies.NONE, attribute);
+
+ Assert.assertNotEquals(SameSiteCookies.LAX, attribute);
+ Assert.assertNotEquals(SameSiteCookies.STRICT, attribute);
+ }
+
+ @Test
+ public void testLax() {
+ SameSiteCookies attribute = SameSiteCookies.LAX;
+
+ Assert.assertEquals("Lax", attribute.getValue());
+ Assert.assertEquals(SameSiteCookies.LAX, attribute);
+
+ Assert.assertNotEquals(SameSiteCookies.NONE, attribute);
+ Assert.assertNotEquals(SameSiteCookies.STRICT, attribute);
+ }
+
+ @Test
+ public void testStrict() {
+ SameSiteCookies attribute = SameSiteCookies.STRICT;
+
+ Assert.assertEquals("Strict", attribute.getValue());
+ Assert.assertEquals(SameSiteCookies.STRICT, attribute);
+
+ Assert.assertNotEquals(SameSiteCookies.NONE, attribute);
+ Assert.assertNotEquals(SameSiteCookies.LAX, attribute);
+ }
+
+ @Test
+ public void testToValidAttribute() {
+ Assert.assertEquals(SameSiteCookies.fromString("none"), SameSiteCookies.NONE);
+ Assert.assertEquals(SameSiteCookies.fromString("None"), SameSiteCookies.NONE);
+ Assert.assertEquals(SameSiteCookies.fromString("NONE"), SameSiteCookies.NONE);
+
+ Assert.assertEquals(SameSiteCookies.fromString("lax"), SameSiteCookies.LAX);
+ Assert.assertEquals(SameSiteCookies.fromString("Lax"), SameSiteCookies.LAX);
+ Assert.assertEquals(SameSiteCookies.fromString("LAX"), SameSiteCookies.LAX);
+
+ Assert.assertEquals(SameSiteCookies.fromString("strict"), SameSiteCookies.STRICT);
+ Assert.assertEquals(SameSiteCookies.fromString("Strict"), SameSiteCookies.STRICT);
+ Assert.assertEquals(SameSiteCookies.fromString("STRICT"), SameSiteCookies.STRICT);
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testToInvalidAttribute01() {
+ SameSiteCookies.fromString("");
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testToInvalidAttribute02() {
+ SameSiteCookies.fromString(" ");
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testToInvalidAttribute03() {
+ SameSiteCookies.fromString("Strict1");
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testToInvalidAttribute04() {
+ SameSiteCookies.fromString("foo");
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testToInvalidAttribute05() {
+ SameSiteCookies.fromString("Lax ");
+ }
+}
\ No newline at end of file
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index de11eb5..f016b00 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -87,6 +87,10 @@
Reduce the default HTTP/2 header list size from 4GB to 32kB to align
with typical HTTP/2 implementations. (markt)
</update>
+ <add>
+ Add support for same-site cookie attribute. Patch provided by John
+ Kelly. (markt)
+ </add>
</changelog>
</subsection>
<subsection name="Cluster">
diff --git a/webapps/docs/config/cookie-processor.xml b/webapps/docs/config/cookie-processor.xml
index f671283..f2d2e9c 100644
--- a/webapps/docs/config/cookie-processor.xml
+++ b/webapps/docs/config/cookie-processor.xml
@@ -94,8 +94,25 @@
<li>The cookie header is always preserved.</li>
</ul>
- <p>No additional attributes are supported by the <strong>RFC 6265 Cookie
- Processor</strong>.</p>
+ <p>The <strong>RFC 6265 Cookie Processor</strong> supports the following
+ additional attributes.</p>
+
+ <attributes>
+
+ <attribute name="sameSiteCookies" required="false">
+ <p>Enables setting same-site cookie attribute.</p>
+
+ <p>If value is <code>none</code> then the same-site cookie attribute
+ won't be set. This is the default value.</p>
+
+ <p>If value is <code>lax</code> then the browser only sends the cookie
+ in same-site requests and cross-site top level GET requests.</p>
+
+ <p>If value is <code>strict</code> then the browser prevents sending the
+ cookie in any cross-site request.</p>
+ </attribute>
+
+ </attributes>
</subsection>
@@ -154,6 +171,19 @@
</p>
</attribute>
+ <attribute name="sameSiteCookies" required="false">
+ <p>Enables setting same-site cookie attribute.</p>
+
+ <p>If value is <code>none</code> then the same-site cookie attribute
+ won't be set. This is the default value.</p>
+
+ <p>If value is <code>lax</code> then the browser only sends the cookie
+ in same-site requests and cross-site top level GET requests.</p>
+
+ <p>If value is <code>strict</code> then the browser prevents sending the
+ cookie in any cross-site request.</p>
+ </attribute>
+
</attributes>
</subsection>
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org