[libsoup/wip/tingping/same-site] Expose support for same-site cookies
- From: Patrick Griffis <pgriffis src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libsoup/wip/tingping/same-site] Expose support for same-site cookies
- Date: Tue, 22 Jan 2019 15:26:21 +0000 (UTC)
commit 40b93ce039eba4be44f72e23705ec14d6ee90c72
Author: Patrick Griffis <pgriffis igalia com>
Date: Tue Jan 22 10:21:13 2019 -0500
Expose support for same-site cookies
This adds API for web browsers to set extra information to support
same-site cookies.
docs/specs/README | 1 +
docs/specs/cookie-same-site.txt | 784 ++++++++++++++++++++++++++++++++++++++++
libsoup/meson.build | 1 +
libsoup/soup-cookie-jar-db.c | 17 +-
libsoup/soup-cookie-jar-text.c | 47 ++-
libsoup/soup-cookie-jar.c | 81 ++++-
libsoup/soup-cookie-jar.h | 8 +
libsoup/soup-cookie.c | 58 +++
libsoup/soup-cookie.h | 18 +
libsoup/soup-message-private.h | 3 +
libsoup/soup-message.c | 126 +++++++
libsoup/soup-message.h | 11 +
tests/cookies-test.c | 3 +-
13 files changed, 1139 insertions(+), 19 deletions(-)
---
diff --git a/docs/specs/README b/docs/specs/README
index 0dee62d0..83bc20a6 100644
--- a/docs/specs/README
+++ b/docs/specs/README
@@ -11,3 +11,4 @@ rfc2817 - Upgrading to TLS Within HTTP/1.1
rfc2818 - HTTP Over TLS
rfc2965 - HTTP State Management Mechanism (allegedly obsoletes 2109)
rfc3986 - Uniform Resource Identifiers (URI): Generic Syntax
+cookie-same-site.txt - Same-Site Cookies (draft)
diff --git a/docs/specs/cookie-same-site.txt b/docs/specs/cookie-same-site.txt
new file mode 100644
index 00000000..f1bc8d9f
--- /dev/null
+++ b/docs/specs/cookie-same-site.txt
@@ -0,0 +1,784 @@
+
+
+
+
+HTTP Working Group M. West
+Internet-Draft Google, Inc
+Updates: 6265 (if approved) M. Goodwin
+Intended status: Standards Track Mozilla
+Expires: December 22, 2016 June 20, 2016
+
+
+ Same-Site Cookies
+ draft-ietf-httpbis-cookie-same-site-00
+
+Abstract
+
+ This document updates RFC6265 by defining a "SameSite" attribute
+ which allows servers to assert that a cookie ought not to be sent
+ along with cross-site requests. This assertion allows user agents to
+ mitigate the risk of cross-origin information leakage, and provides
+ some protection against cross-site request forgery attacks.
+
+Note to Readers
+
+ Discussion of this draft takes place on the HTTP working group
+ mailing list (ietf-http-wg w3 org), which is archived at
+ https://lists.w3.org/Archives/Public/ietf-http-wg/ .
+
+ Working Group information can be found at http://httpwg.github.io/ ;
+ source code and issues list for this draft can be found at
+ https://github.com/httpwg/http-extensions/labels/cookie-same-site .
+
+Status of This Memo
+
+ This Internet-Draft is submitted in full conformance with the
+ provisions of BCP 78 and BCP 79.
+
+ Internet-Drafts are working documents of the Internet Engineering
+ Task Force (IETF). Note that other groups may also distribute
+ working documents as Internet-Drafts. The list of current Internet-
+ Drafts is at http://datatracker.ietf.org/drafts/current/.
+
+ Internet-Drafts are draft documents valid for a maximum of six months
+ and may be updated, replaced, or obsoleted by other documents at any
+ time. It is inappropriate to use Internet-Drafts as reference
+ material or to cite them other than as "work in progress."
+
+ This Internet-Draft will expire on December 22, 2016.
+
+
+
+
+
+
+
+West & Goodwin Expires December 22, 2016 [Page 1]
+
+Internet-Draft Same-Site Cookies June 2016
+
+
+Copyright Notice
+
+ Copyright (c) 2016 IETF Trust and the persons identified as the
+ document authors. All rights reserved.
+
+ This document is subject to BCP 78 and the IETF Trust's Legal
+ Provisions Relating to IETF Documents
+ (http://trustee.ietf.org/license-info) in effect on the date of
+ publication of this document. Please review these documents
+ carefully, as they describe your rights and restrictions with respect
+ to this document. Code Components extracted from this document must
+ include Simplified BSD License text as described in Section 4.e of
+ the Trust Legal Provisions and are provided without warranty as
+ described in the Simplified BSD License.
+
+Table of Contents
+
+ 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 3
+ 1.1. Goals . . . . . . . . . . . . . . . . . . . . . . . . . . 3
+ 1.2. Examples . . . . . . . . . . . . . . . . . . . . . . . . 4
+ 2. Terminology and notation . . . . . . . . . . . . . . . . . . 4
+ 2.1. "Same-site" and "cross-site" Requests . . . . . . . . . . 5
+ 2.1.1. Document-based requests . . . . . . . . . . . . . . . 5
+ 2.1.2. Worker-based requests . . . . . . . . . . . . . . . . 6
+ 3. Server Requirements . . . . . . . . . . . . . . . . . . . . . 7
+ 3.1. Grammar . . . . . . . . . . . . . . . . . . . . . . . . . 7
+ 3.2. Semantics of the "SameSite" Attribute (Non-Normative) . . 8
+ 4. User Agent Requirements . . . . . . . . . . . . . . . . . . . 8
+ 4.1. The "SameSite" attribute . . . . . . . . . . . . . . . . 8
+ 4.1.1. "Strict" and "Lax" enforcement . . . . . . . . . . . 9
+ 4.2. Monkey-patching the Storage Model . . . . . . . . . . . . 9
+ 4.3. Monkey-patching the "Cookie" header . . . . . . . . . . . 10
+ 5. Authoring Considerations . . . . . . . . . . . . . . . . . . 10
+ 5.1. Defense in depth . . . . . . . . . . . . . . . . . . . . 10
+ 5.2. Top-level Navigations . . . . . . . . . . . . . . . . . . 10
+ 5.3. Mashups and Widgets . . . . . . . . . . . . . . . . . . . 11
+ 6. Privacy Considerations . . . . . . . . . . . . . . . . . . . 11
+ 6.1. Server-controlled . . . . . . . . . . . . . . . . . . . . 11
+ 6.2. Pervasive Monitoring . . . . . . . . . . . . . . . . . . 12
+ 7. References . . . . . . . . . . . . . . . . . . . . . . . . . 12
+ 7.1. Normative References . . . . . . . . . . . . . . . . . . 12
+ 7.2. Informative References . . . . . . . . . . . . . . . . . 13
+ Appendix A. Acknowledgements . . . . . . . . . . . . . . . . . . 14
+ Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . 14
+
+
+
+
+
+
+
+West & Goodwin Expires December 22, 2016 [Page 2]
+
+Internet-Draft Same-Site Cookies June 2016
+
+
+1. Introduction
+
+ Section 8.2 of [RFC6265] eloquently notes that cookies are a form of
+ ambient authority, attached by default to requests the user agent
+ sends on a user's behalf. Even when an attacker doesn't know the
+ contents of a user's cookies, she can still execute commands on the
+ user's behalf (and with the user's authority) by asking the user
+ agent to send HTTP requests to unwary servers.
+
+ Here, we update [RFC6265] with a simple mitigation strategy that
+ allows servers to declare certain cookies as "same-site", meaning
+ they should not be attached to "cross-site" requests (as defined in
+ section 2.1).
+
+ Note that the mechanism outlined here is backwards compatible with
+ the existing cookie syntax. Servers may serve these cookies to all
+ user agents; those that do not support the "SameSite" attribute will
+ simply store a cookie which is attached to all relevant requests,
+ just as they do today.
+
+1.1. Goals
+
+ These cookies are intended to provide a solid layer of defense-in-
+ depth against attacks which require embedding an authenticated
+ request into an attacker-controlled context:
+
+ 1. Timing attacks which yield cross-origin information leakage (such
+ as those detailed in [pixel-perfect]) can be substantially
+ mitigated by setting the "SameSite" attribute on authentication
+ cookies. The attacker will only be able to embed unauthenticated
+ resources, as embedding mechanisms such as "<iframe>" will yield
+ cross-site requests.
+ 2. Cross-site script inclusion (XSSI) attacks are likewise mitigated
+ by setting the "SameSite" attribute on authentication cookies.
+ The attacker will not be able to include authenticated resources
+ via "<script>" or "<link>", as these embedding mechanisms will
+ likewise yield cross-site requests.
+ 3. Cross-site request forgery (CSRF) attacks which rely on top-level
+ navigation (HTML "<form>" POSTs, for instance) can also be
+ mitigated by treating these navigational requests as "cross-
+ site".
+ 4. Same-site cookies have some marginal value for policy or
+ regulatory purposes, as cookies which are not delivered with
+ cross-site requests cannot be directly used for tracking
+ purposes. It may be valuable for an origin to assert that its
+ cookies should not be sent along with cross-site requests in
+ order to limit its exposure to non-technical risk.
+
+
+
+
+West & Goodwin Expires December 22, 2016 [Page 3]
+
+Internet-Draft Same-Site Cookies June 2016
+
+
+1.2. Examples
+
+ Same-site cookies are set via the "SameSite" attribute in the "Set-
+ Cookie" header field. That is, given a server's response to a user
+ agent which contains the following header field:
+
+
+ Set-Cookie: SID=31d4d96e407aad42; SameSite=Strict
+
+ Subsequent requests from that user agent can be expected to contain
+ the following header field if and only if both the requested resource
+ and the resource in the top-level browsing context match the cookie.
+
+2. Terminology and notation
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in [RFC2119].
+
+ This specification uses the Augmented Backus-Naur Form (ABNF)
+ notation of [RFC5234].
+
+ Two sequences of octets are said to case-insensitively match each
+ other if and only if they are equivalent under the "i;ascii-casemap"
+ collation defined in [RFC4790].
+
+ The terms "active document", "ancestor browsing context", "browsing
+ context", "document", "WorkerGlobalScope", "sandboxed origin browsing
+ context flag", "parent browsing context", "the worker's Documents",
+ "nested browsing context", and "top-level browsing context" are
+ defined in [HTML].
+
+ "Service Workers" are defined in the Service Workers specification
+ [SERVICE-WORKERS].
+
+ The term "origin", the mechanism of deriving an origin from a URI,
+ and the "the same" matching algorithm for origins are defined in
+ [RFC6454].
+
+ "Safe" HTTP methods include "GET", "HEAD", "OPTIONS", and "TRACE", as
+ defined in Section 4.2.1 of [RFC7231].
+
+ The term "public suffix" is defined in a note in Section 5.3 of
+ [RFC6265] as "a domain that is controlled by a public registry". For
+ example, "example.com"'s public suffix is "com". User agents SHOULD
+ use an up-to-date public suffix list, such as the one maintained by
+ Mozilla at [PSL].
+
+
+
+
+West & Goodwin Expires December 22, 2016 [Page 4]
+
+Internet-Draft Same-Site Cookies June 2016
+
+
+ An origin's "registrable domain" is the origin's host's public suffix
+ plus the label to its left. That is, "https://www.example.com"'s
+ registrable domain is "example.com". This concept is defined more
+ rigorously in [PSL].
+
+ The term "request", as well as a request's "client", "current url",
+ "method", and "target browsing context", are defined in [FETCH].
+
+2.1. "Same-site" and "cross-site" Requests
+
+ A request is "same-site" if its target's URI's origin's registrable
+ domain is an exact match for the request's initiator's "site for
+ cookies", and "cross-site" otherwise. To be more precise, for a
+ given request ("request"), the following algorithm returns "same-
+ site" or "cross-site":
+
+ 1. If "request"'s client is "null", return "same-site".
+ 2. Let "site" be "request"'s client's "site for cookies" (as defined
+ in the following sections).
+ 3. Let "target" be the registrable domain of "request"'s current
+ url.
+ 4. If "site" is an exact match for "target", return "same-site".
+ 5. Return "cross-site".
+
+2.1.1. Document-based requests
+
+ The URI displayed in a user agent's address bar is the only security
+ context directly exposed to users, and therefore the only signal
+ users can reasonably rely upon to determine whether or not they trust
+ a particular website. The registrable domain of that URI's origin
+ represents the context in which a user most likely believes
+ themselves to be interacting. We'll label this domain the "top-level
+ site".
+
+ For a document displayed in a top-level browsing context, we can stop
+ here: the document's "site for cookies" is the top-level site.
+
+ For documents which are displayed in nested browsing contexts, we
+ need to audit the origins of each of a document's ancestor browsing
+ contexts' active documents in order to account for the "multiple-
+ nested scenarios" described in Section 4 of [RFC7034]. These
+ document's "site for cookies" is the top-level site if and only if
+ the document and each of its ancestor documents' origins have the
+ same registrable domain as the top-level site. Otherwise its "site
+ for cookies" is the empty string.
+
+
+
+
+
+
+West & Goodwin Expires December 22, 2016 [Page 5]
+
+Internet-Draft Same-Site Cookies June 2016
+
+
+ Given a Document ("document"), the following algorithm returns its
+ "site for cookies" (either a registrable domain, or the empty
+ string):
+
+ 1. Let "top-document" be the active document in "document"'s
+ browsing context's top-level browsing context.
+ 2. Let "top-origin" be the origin of "top-document"'s URI if "top-
+ document"'s sandboxed origin browsing context flag is set, and
+ "top-document"'s origin otherwise.
+ 3. Let "documents" be a list containing "document" and each of
+ "document"'s ancestor browsing contexts' active documents.
+ 4. For each "item" in "documents":
+
+ 1. Let "origin" be the origin of "item"'s URI if "item"'s
+ sandboxed origin browsing context flag is set, and "item"'s
+ origin otherwise.
+ 2. If "origin"'s host's registrable domain is not an exact match
+ for "top-origin"'s host's registrable domain, return the
+ empty string.
+ 5. Return "top-site".
+
+2.1.2. Worker-based requests
+
+ Worker-driven requests aren't as clear-cut as document-driven
+ requests, as there isn't a clear link between a top-level browsing
+ context and a worker. This is especially true for Service Workers
+ [SERVICE-WORKERS], which may execute code in the background, without
+ any document visible at all.
+
+ Note: The descriptions below assume that workers must be same-origin
+ with the documents that instantiate them. If this invariant changes,
+ we'll need to take the worker's script's URI into account when
+ determining their status.
+
+2.1.2.1. Dedicated and Shared Workers
+
+ Dedicated workers are simple, as each dedicated worker is bound to
+ one and only one document. Requests generated from a dedicated
+ worker (via "importScripts", "XMLHttpRequest", "fetch()", etc) define
+ their "site for cookies" as that document's "site for cookies".
+
+ Shared workers may be bound to multiple documents at once. As it is
+ quite possible for those documents to have distinct "site for cookie"
+ values, the worker's "site for cookies" will be the empty string in
+ cases where the values diverge, and the shared value in cases where
+ the values agree.
+
+
+
+
+
+West & Goodwin Expires December 22, 2016 [Page 6]
+
+Internet-Draft Same-Site Cookies June 2016
+
+
+ Given a WorkerGlobalScope ("worker"), the following algorithm returns
+ its "site for cookies" (either a registrable domain, or the empty
+ string):
+
+ 1. Let "site" be "worker"'s origin's host's registrable domain.
+ 2. For each "document" in "worker"'s Documents:
+
+ 1. Let "document-site" be "document"'s "site for cookies" (as
+ defined in Section 2.1.1).
+ 2. If "document-site" is not an exact match for "site", return
+ the empty string.
+ 3. Return "site".
+
+2.1.2.2. Service Workers
+
+ Service Workers are more complicated, as they act as a completely
+ separate execution context with only tangential relationship to the
+ Document which registered them.
+
+ Requests which simply pass through a service worker will be handled
+ as described above: the request's client will be the Document or
+ Worker which initiated the request, and its "site for cookies" will
+ be those defined in Section 2.1.1 and Section 2.1.2.1
+
+ Requests which are initiated by the Service Worker itself (via a
+ direct call to "fetch()", for instance), on the other hand, will have
+ a client which is a ServiceWorkerGlobalScope. Its "site for cookies"
+ will be the registrable domain of the Service Worker's URI.
+
+ Given a ServiceWorkerGlobalScope ("worker"), the following algorithm
+ returns its "site for cookies" (either a registrable domain, or the
+ empty string):
+
+ 1. Return "worker"'s origin's host's registrable domain.
+
+3. Server Requirements
+
+ This section describes extensions to [RFC6265] necessary to implement
+ the server-side requirements of the "SameSite" attribute.
+
+3.1. Grammar
+
+ Add "SameSite" to the list of accepted attributes in the "Set-Cookie"
+ header field's value by replacing the "cookie-av" token definition in
+ Section 4.1.1 of [RFC6265] with the following ABNF grammar:
+
+
+
+
+
+
+West & Goodwin Expires December 22, 2016 [Page 7]
+
+Internet-Draft Same-Site Cookies June 2016
+
+
+ cookie-av = expires-av / max-age-av / domain-av /
+ path-av / secure-av / httponly-av /
+ samesite-av / extension-av
+ samesite-av = "SameSite" / "SameSite=" samesite-value
+ samesite-value = "Strict" / "Lax"
+
+3.2. Semantics of the "SameSite" Attribute (Non-Normative)
+
+ The "SameSite" attribute limits the scope of the cookie such that it
+ will only be attached to requests if those requests are "same-site",
+ as defined by the algorithm in Section 2.1. For example, requests
+ for "https://example.com/sekrit-image" will attach same-site cookies
+ if and only if initiated from a context whose "site for cookies" is
+ "example.com".
+
+ If the "SameSite" attribute's value is "Strict", or if the value is
+ invalid, the cookie will only be sent along with "same-site"
+ requests. If the value is "Lax", the cookie will be sent with "same-
+ site" requests, and with "cross-site" top-level navigations, as
+ described in Section 4.1.1.
+
+ The changes to the "Cookie" header field suggested in Section 4.3
+ provide additional detail.
+
+4. User Agent Requirements
+
+ This section describes extensions to [RFC6265] necessary in order to
+ implement the client-side requirements of the "SameSite" attribute.
+
+4.1. The "SameSite" attribute
+
+ The following attribute definition should be considered part of the
+ the "Set-Cookie" algorithm as described in Section 5.2 of [RFC6265]:
+
+ If the "attribute-name" case-insensitively matches the string
+ "SameSite", the user agent MUST process the "cookie-av" as follows:
+
+ 1. If "cookie-av"'s "attribute-value" is not a case-insensitive
+ match for "Strict" or "Lax", ignore the "cookie-av".
+ 2. Let "enforcement" be "Lax" if "cookie-av"'s "attribute-value" is
+ a case-insensitive match for "Lax", and "Strict" otherwise.
+ 3. Append an attribute to the "cookie-attribute-list" with an
+ "attribute-name" of "SameSite" and an "attribute-value" of
+ "enforcement".
+
+
+
+
+
+
+
+West & Goodwin Expires December 22, 2016 [Page 8]
+
+Internet-Draft Same-Site Cookies June 2016
+
+
+4.1.1. "Strict" and "Lax" enforcement
+
+ By default, same-site cookies will not be sent along with top-level
+ navigations. As discussed in Section 5.2, this might or might not be
+ compatible with existing session management systems. In the
+ interests of providing a drop-in mechanism that mitigates the risk of
+ CSRF attacks, developers may set the "SameSite" attribute in a "Lax"
+ enforcement mode that carves out an exception which sends same-site
+ cookies along with cross-site requests if and only if they are top-
+ level navigations which use a "safe" (in the [RFC7231] sense) HTTP
+ method.
+
+ Lax enforcement provides reasonable defense in depth against CSRF
+ attacks that rely on unsafe HTTP methods (like "POST"), but do not
+ offer a robust defense against CSRF as a general category of attack:
+
+ 1. Attackers can still pop up new windows or trigger top-level
+ navigations in order to create a "same-site" request (as
+ described in section 2.1), which is only a speedbump along the
+ road to exploitation.
+ 2. Features like "<link rel='prerender'>" [prerendering] can be
+ exploited to create "same-site" requests without the risk of user
+ detection.
+
+ When possible, developers should use a session management mechanism
+ such as that described in Section 5.2 to mitigate the risk of CSRF
+ more completely.
+
+4.2. Monkey-patching the Storage Model
+
+ Note: There's got to be a better way to specify this. Until I figure
+ out what that is, monkey-patching!
+
+ Alter Section 5.3 of [RFC6265] as follows:
+
+ 1. Add "samesite-flag" to the list of fields stored for each cookie.
+ This field's value is one of "None", "Strict", or "Lax".
+ 2. Before step 11 of the current algorithm, add the following:
+
+ 1. If the "cookie-attribute-list" contains an attribute with an
+ "attribute-name" of "SameSite", set the cookie's "samesite-
+ flag" to "attribute-value" ("Strict" or "Lax"). Otherwise,
+ set the cookie's "samesite-flag" to "None".
+ 2. If the cookie's "samesite-flag" is not "None", and the
+ request which generated the cookie's client's "site for
+ cookies" is not an exact match for "request-uri"'s host's
+ registrable domain, then abort these steps and ignore the
+ newly created cookie entirely.
+
+
+
+West & Goodwin Expires December 22, 2016 [Page 9]
+
+Internet-Draft Same-Site Cookies June 2016
+
+
+4.3. Monkey-patching the "Cookie" header
+
+ Note: There's got to be a better way to specify this. Until I figure
+ out what that is, monkey-patching!
+
+ Alter Section 5.4 of [RFC6265] as follows:
+
+ 1. Add the following requirement to the list in step 1:
+
+ * If the cookie's "samesite-flag" is not "None", and the HTTP
+ request is cross-site (as defined in Section 2.1 then exclude
+ the cookie unless all of the following statements hold:
+
+ 1. "samesite-flag" is "Lax"
+ 2. The HTTP request's method is "safe".
+ 3. The HTTP request's target browsing context is a top-level
+ browsing context.
+
+ Note that the modifications suggested here concern themselves only
+ with the "site for cookies" of the request's client, and the
+ registrable domain of the resource being requested. The cookie's
+ "domain", "path", and "secure" attributes do not come into play for
+ these comparisons.
+
+5. Authoring Considerations
+
+5.1. Defense in depth
+
+ "SameSite" cookies offer a robust defense against CSRF attack when
+ deployed in strict mode, and when supported by the client. It is,
+ however, prudent to ensure that this designation is not the extent of
+ a site's defense against CSRF, as same-site navigations and
+ submissions can certainly be executed in conjunction with other
+ attack vectors such as cross-site scripting.
+
+ Developers are strongly encouraged to deploy the usual server-side
+ defenses (CSRF tokens, ensuring that "safe" HTTP methods are
+ idempotent, etc) to mitigate the risk more fully.
+
+ Additionally, client-side techniques such as those described in
+ [app-isolation] may also prove effective against CSRF, and are
+ certainly worth exploring in combination with "SameSite" cookies.
+
+5.2. Top-level Navigations
+
+ Setting the "SameSite" attribute in "strict" mode provides robust
+ defense in depth against CSRF attacks, but has the potential to
+ confuse users unless sites' developers carefully ensure that their
+
+
+
+West & Goodwin Expires December 22, 2016 [Page 10]
+
+Internet-Draft Same-Site Cookies June 2016
+
+
+ session management systems deal reasonably well with top-level
+ navigations.
+
+ Consider the scenario in which a user reads their email at MegaCorp
+ Inc's webmail provider "https://example.com/". They might expect
+ that clicking on an emailed link to "https://projects.com/secret/
+ project" would show them the secret project that they're authorized
+ to see, but if "projects.com" has marked their session cookies as
+ "SameSite", then this cross-site navigation won't send them along
+ with the request. "projects.com" will render a 404 error to avoid
+ leaking secret information, and the user will be quite confused.
+
+ Developers can avoid this confusion by adopting a session management
+ system that relies on not one, but two cookies: one conceptualy
+ granting "read" access, another granting "write" access. The latter
+ could be marked as "SameSite", and its absence would provide a
+ reauthentication step before executing any non-idempotent action.
+ The former could drop the "SameSite" attribute entirely, or choose
+ the "Lax" version of enforcement, in order to allow users access to
+ data via top-level navigation.
+
+5.3. Mashups and Widgets
+
+ The "SameSite" attribute is inappropriate for some important use-
+ cases. In particular, note that content intended for embedding in a
+ cross-site contexts (social networking widgets or commenting
+ services, for instance) will not have access to such cookies. Cross-
+ site cookies may be required in order to provide seamless
+ functionality that relies on a user's state.
+
+ Likewise, some forms of Single-Sign-On might require authentication
+ in a cross-site context; these mechanisms will not function as
+ intended with same-site cookies.
+
+6. Privacy Considerations
+
+6.1. Server-controlled
+
+ Same-site cookies in and of themselves don't do anything to address
+ the general privacy concerns outlined in Section 7.1 of [RFC6265].
+ The attribute is set by the server, and serves to mitigate the risk
+ of certain kinds of attacks that the server is worried about. The
+ user is not involved in this decision. Moreover, a number of side-
+ channels exist which could allow a server to link distinct requests
+ even in the absence of cookies. Connection and/or socket pooling,
+ Token Binding, and Channel ID all offer explicit methods of
+ identification that servers could take advantage of.
+
+
+
+
+West & Goodwin Expires December 22, 2016 [Page 11]
+
+Internet-Draft Same-Site Cookies June 2016
+
+
+6.2. Pervasive Monitoring
+
+ As outlined in [RFC7258], pervasive monitoring is an attack. Cookies
+ play a large part in enabling such monitoring, as they are
+ responsible for maintaining state in HTTP connections. We considered
+ restricting same-site cookies to secure contexts [secure-contexts] as
+ a mitigation but decided against doing so, as this feature should
+ result in a strict reduction in the number of cookies floating around
+ in cross-site contexts. That is, even if "http://not-example.com"
+ embeds a resource from "http://example.com/", that resource will not
+ be "same-site", and "http://example.com"'s cookies simply cannot be
+ used to correlate user behavior across distinct origins.
+
+7. References
+
+7.1. Normative References
+
+ [FETCH] van Kesteren, A., "Fetch", n.d.,
+ <https://fetch.spec.whatwg.org/>.
+
+ [HTML] Hickson, I., Pieters, S., van Kesteren, A., Jaegenstedt,
+ P., and D. Denicola, "HTML", n.d.,
+ <https://html.spec.whatwg.org/>.
+
+ [PSL] "Public Suffix List", n.d., <https://publicsuffix.org/
+ list/>.
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119,
+ DOI 10.17487/RFC2119, March 1997,
+ <http://www.rfc-editor.org/info/rfc2119>.
+
+ [RFC4790] Newman, C., Duerst, M., and A. Gulbrandsen, "Internet
+ Application Protocol Collation Registry", RFC 4790,
+ DOI 10.17487/RFC4790, March 2007,
+ <http://www.rfc-editor.org/info/rfc4790>.
+
+ [RFC5234] Crocker, D., Ed. and P. Overell, "Augmented BNF for Syntax
+ Specifications: ABNF", STD 68, RFC 5234,
+ DOI 10.17487/RFC5234, January 2008,
+ <http://www.rfc-editor.org/info/rfc5234>.
+
+ [RFC6265] Barth, A., "HTTP State Management Mechanism", RFC 6265,
+ DOI 10.17487/RFC6265, April 2011,
+ <http://www.rfc-editor.org/info/rfc6265>.
+
+
+
+
+
+
+West & Goodwin Expires December 22, 2016 [Page 12]
+
+Internet-Draft Same-Site Cookies June 2016
+
+
+ [RFC6454] Barth, A., "The Web Origin Concept", RFC 6454,
+ DOI 10.17487/RFC6454, December 2011,
+ <http://www.rfc-editor.org/info/rfc6454>.
+
+ [RFC7231] Fielding, R., Ed. and J. Reschke, Ed., "Hypertext Transfer
+ Protocol (HTTP/1.1): Semantics and Content", RFC 7231,
+ DOI 10.17487/RFC7231, June 2014,
+ <http://www.rfc-editor.org/info/rfc7231>.
+
+ [RFC7258] Farrell, S. and H. Tschofenig, "Pervasive Monitoring Is an
+ Attack", BCP 188, RFC 7258, DOI 10.17487/RFC7258, May
+ 2014, <http://www.rfc-editor.org/info/rfc7258>.
+
+ [SERVICE-WORKERS]
+ Russell, A., Song, J., and J. Archibald, "Service
+ Workers", n.d., <http://www.w3.org/TR/service-workers/>.
+
+7.2. Informative References
+
+ [app-isolation]
+ Chen, E., Bau, J., Reis, C., Barth, A., and C. Jackson,
+ "App Isolation - Get the Security of Multiple Browsers
+ with Just One", n.d.,
+ <http://www.collinjackson.com/research/papers/
+ appisolation.pdf>.
+
+ [pixel-perfect]
+ Stone, P., "Pixel Perfect Timing Attacks with HTML5",
+ n.d., <http://www.contextis.com/documents/2/
+ Browser_Timing_Attacks.pdf>.
+
+ [prerendering]
+ Bentzel, C., "Chrome Prerendering", n.d.,
+ <https://www.chromium.org/developers/design-documents/
+ prerender>.
+
+ [RFC7034] Ross, D. and T. Gondrom, "HTTP Header Field X-Frame-
+ Options", RFC 7034, DOI 10.17487/RFC7034, October 2013,
+ <http://www.rfc-editor.org/info/rfc7034>.
+
+ [samedomain-cookies]
+ Goodwin, M. and J. Walker, "SameDomain Cookie Flag", 2011,
+ <http://people.mozilla.org/~mgoodwin/SameDomain/
+ samedomain-latest.txt>.
+
+ [secure-contexts]
+ West, M., "Secure Contexts", n.d., <https://w3c.github.io/
+ webappsec-secure-contexts/>.
+
+
+
+West & Goodwin Expires December 22, 2016 [Page 13]
+
+Internet-Draft Same-Site Cookies June 2016
+
+
+Appendix A. Acknowledgements
+
+ The same-site cookie concept documented here is indebited to Mark
+ Goodwin's and Joe Walker's [samedomain-cookies]. Michal Zalewski,
+ Artur Janc, Ryan Sleevi, and Adam Barth provided particularly
+ valuable feedback on this document.
+
+Authors' Addresses
+
+ Mike West
+ Google, Inc
+
+ Email: mkwst google com
+ URI: https://mikewest.org/
+
+
+ Mark Goodwin
+ Mozilla
+
+ Email: mgoodwin mozilla com
+ URI: https://www.computerist.org/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+West & Goodwin Expires December 22, 2016 [Page 14]
diff --git a/libsoup/meson.build b/libsoup/meson.build
index 5f2a2156..0cf863a7 100644
--- a/libsoup/meson.build
+++ b/libsoup/meson.build
@@ -295,6 +295,7 @@ if enable_introspection or enable_vapi
export_packages : libsoup_api_name,
extra_args : soup_gir_args,
includes : 'Gio-2.0',
+ link_with: libsoup,
install : true,
header: join_paths(meson.project_name(), 'soup.h'),
)
diff --git a/libsoup/soup-cookie-jar-db.c b/libsoup/soup-cookie-jar-db.c
index 0274038d..86aff1e6 100644
--- a/libsoup/soup-cookie-jar-db.c
+++ b/libsoup/soup-cookie-jar-db.c
@@ -128,9 +128,9 @@ soup_cookie_jar_db_new (const char *filename, gboolean read_only)
NULL);
}
-#define QUERY_ALL "SELECT id, name, value, host, path, expiry, lastAccessed, isSecure, isHttpOnly FROM
moz_cookies;"
-#define CREATE_TABLE "CREATE TABLE moz_cookies (id INTEGER PRIMARY KEY, name TEXT, value TEXT, host TEXT,
path TEXT,expiry INTEGER, lastAccessed INTEGER, isSecure INTEGER, isHttpOnly INTEGER)"
-#define QUERY_INSERT "INSERT INTO moz_cookies VALUES(NULL, %Q, %Q, %Q, %Q, %d, NULL, %d, %d);"
+#define QUERY_ALL "SELECT id, name, value, host, path, expiry, lastAccessed, isSecure, isHttpOnly, sameSite
FROM moz_cookies;"
+#define CREATE_TABLE "CREATE TABLE moz_cookies (id INTEGER PRIMARY KEY, name TEXT, value TEXT, host TEXT,
path TEXT, expiry INTEGER, lastAccessed INTEGER, isSecure INTEGER, isHttpOnly INTEGER, sameSite INTEGER)"
+#define QUERY_INSERT "INSERT INTO moz_cookies VALUES(NULL, %Q, %Q, %Q, %Q, %d, NULL, %d, %d, %d);"
#define QUERY_DELETE "DELETE FROM moz_cookies WHERE name=%Q AND host=%Q;"
enum {
@@ -143,6 +143,7 @@ enum {
COL_LAST_ACCESS,
COL_SECURE,
COL_HTTP_ONLY,
+ COL_SAME_SITE_POLICY,
N_COL,
};
@@ -157,6 +158,7 @@ callback (void *data, int argc, char **argv, char **colname)
time_t now;
int max_age;
gboolean http_only = FALSE, secure = FALSE;
+ SoupSameSitePolicy same_site_policy;
now = time (NULL);
@@ -172,6 +174,7 @@ callback (void *data, int argc, char **argv, char **colname)
http_only = (g_strcmp0 (argv[COL_HTTP_ONLY], "1") == 0);
secure = (g_strcmp0 (argv[COL_SECURE], "1") == 0);
+ same_site_policy = g_ascii_strtoll (argv[COL_SAME_SITE_POLICY], NULL, 0);
cookie = soup_cookie_new (name, value, host, path, max_age);
@@ -179,6 +182,8 @@ callback (void *data, int argc, char **argv, char **colname)
soup_cookie_set_secure (cookie, TRUE);
if (http_only)
soup_cookie_set_http_only (cookie, TRUE);
+ if (same_site_policy)
+ soup_cookie_set_same_site_policy (cookie, same_site_policy);
soup_cookie_jar_add_cookie (jar, cookie);
@@ -241,6 +246,9 @@ open_db (SoupCookieJar *jar)
sqlite3_free (error);
}
+ /* We simply always run this, it will safely handle a column with the same name existing */
+ sqlite3_exec (priv->db, "ALTER TABLE moz_cookies ADD COLUMN sameSite INTEGER DEFAULT 0", NULL, NULL,
NULL);
+
return FALSE;
}
@@ -291,7 +299,8 @@ soup_cookie_jar_db_changed (SoupCookieJar *jar,
new_cookie->path,
expires,
new_cookie->secure,
- new_cookie->http_only);
+ new_cookie->http_only,
+ new_cookie->same_site_policy);
exec_query_with_try_create_table (priv->db, query, NULL, NULL);
sqlite3_free (query);
}
diff --git a/libsoup/soup-cookie-jar-text.c b/libsoup/soup-cookie-jar-text.c
index 46e12e54..3241fcbb 100644
--- a/libsoup/soup-cookie-jar-text.c
+++ b/libsoup/soup-cookie-jar-text.c
@@ -121,6 +121,38 @@ soup_cookie_jar_text_new (const char *filename, gboolean read_only)
NULL);
}
+static SoupSameSitePolicy
+string_to_same_site_policy (const char *string)
+{
+ if (strcmp (string, "Lax") == 0)
+ return SOUP_SAME_SITE_POLICY_LAX;
+ else if (strcmp (string, "Strict") == 0)
+ return SOUP_SAME_SITE_POLICY_STRICT;
+ else if (strcmp (string, "None") == 0)
+ return SOUP_SAME_SITE_POLICY_NONE;
+ else {
+ g_assert_not_reached ();
+ return SOUP_SAME_SITE_POLICY_NONE;
+ }
+}
+
+static const char *
+same_site_policy_to_string (SoupSameSitePolicy policy)
+{
+ switch (policy)
+ {
+ case SOUP_SAME_SITE_POLICY_STRICT:
+ return "Strict";
+ case SOUP_SAME_SITE_POLICY_LAX:
+ return "Lax";
+ case SOUP_SAME_SITE_POLICY_NONE:
+ return "None";
+ }
+
+ g_assert_not_reached ();
+ return "None";
+}
+
static SoupCookie*
parse_cookie (char *line, time_t now)
{
@@ -129,7 +161,7 @@ parse_cookie (char *line, time_t now)
gboolean http_only;
gulong expire_time;
int max_age;
- char *host, *path, *secure, *expires, *name, *value;
+ char *host, *path, *secure, *expires, *name, *value, *samesite = NULL;
if (g_str_has_prefix (line, "#HttpOnly_")) {
http_only = TRUE;
@@ -140,7 +172,7 @@ parse_cookie (char *line, time_t now)
http_only = FALSE;
result = g_strsplit (line, "\t", -1);
- if (g_strv_length (result) != 7)
+ if (g_strv_length (result) < 7)
goto out;
/* Check this first */
@@ -164,8 +196,14 @@ parse_cookie (char *line, time_t now)
name = result[5];
value = result[6];
+ if (g_strv_length (result) == 8)
+ samesite = result[7];
+
cookie = soup_cookie_new (name, value, host, path, max_age);
+ if (samesite != NULL)
+ soup_cookie_set_same_site_policy (cookie, string_to_same_site_policy (samesite));
+
if (strcmp (secure, "FALSE") != 0)
soup_cookie_set_secure (cookie, TRUE);
if (http_only)
@@ -219,7 +257,7 @@ write_cookie (FILE *out, SoupCookie *cookie)
{
fseek (out, 0, SEEK_END);
- fprintf (out, "%s%s\t%s\t%s\t%s\t%lu\t%s\t%s\n",
+ fprintf (out, "%s%s\t%s\t%s\t%s\t%lu\t%s\t%s\t%s\n",
cookie->http_only ? "#HttpOnly_" : "",
cookie->domain,
*cookie->domain == '.' ? "TRUE" : "FALSE",
@@ -227,7 +265,8 @@ write_cookie (FILE *out, SoupCookie *cookie)
cookie->secure ? "TRUE" : "FALSE",
(gulong)soup_date_to_time_t (cookie->expires),
cookie->name,
- cookie->value);
+ cookie->value,
+ same_site_policy_to_string (cookie->same_site_policy));
}
static void
diff --git a/libsoup/soup-cookie-jar.c b/libsoup/soup-cookie-jar.c
index b2b78909..3dc887c9 100644
--- a/libsoup/soup-cookie-jar.c
+++ b/libsoup/soup-cookie-jar.c
@@ -12,6 +12,7 @@
#include <string.h>
#include "soup-cookie-jar.h"
+#include "soup-message-private.h"
#include "soup-misc-private.h"
#include "soup.h"
@@ -297,8 +298,29 @@ compare_cookies (gconstpointer a, gconstpointer b, gpointer jar)
return aserial - bserial;
}
+static gboolean
+cookie_is_valid_for_same_site_policy (SoupCookie *cookie, const char *method, SoupURI *uri, SoupURI
*top_level, SoupURI *cookie_uri, gboolean is_top_level_navigation, gboolean for_http)
+{
+ SoupSameSitePolicy policy = soup_cookie_get_same_site_policy (cookie);
+
+ if (policy == SOUP_SAME_SITE_POLICY_NONE)
+ return TRUE;
+
+ if (top_level == NULL)
+ return TRUE;
+
+ if (policy == SOUP_SAME_SITE_POLICY_LAX && is_top_level_navigation &&
+ (SOUP_METHOD_IS_SAFE (method) || for_http == FALSE))
+ return TRUE;
+
+ if (is_top_level_navigation && cookie_uri == NULL)
+ return FALSE;
+
+ return soup_host_matches_host (soup_uri_get_host (cookie_uri ? cookie_uri : top_level),
soup_uri_get_host (uri));
+}
+
static GSList *
-get_cookies (SoupCookieJar *jar, SoupURI *uri, gboolean for_http, gboolean copy_cookies)
+get_cookies (SoupCookieJar *jar, SoupURI *uri, SoupURI *top_level, SoupURI *site_for_cookies, const char
*method, gboolean for_http, gboolean is_top_level_navigation, gboolean copy_cookies)
{
SoupCookieJarPrivate *priv;
GSList *cookies, *domain_cookies;
@@ -332,6 +354,7 @@ get_cookies (SoupCookieJar *jar, SoupURI *uri, gboolean for_http, gboolean copy_
g_strdup (cur),
new_head);
} else if (soup_cookie_applies_to_uri (cookie, uri) &&
+ cookie_is_valid_for_same_site_policy (cookie, method, uri, top_level,
site_for_cookies, is_top_level_navigation, for_http) &&
(for_http || !cookie->http_only))
cookies = g_slist_append (cookies, copy_cookies ? soup_cookie_copy (cookie) :
cookie);
@@ -386,7 +409,7 @@ soup_cookie_jar_get_cookies (SoupCookieJar *jar, SoupURI *uri,
g_return_val_if_fail (SOUP_IS_COOKIE_JAR (jar), NULL);
g_return_val_if_fail (uri != NULL, NULL);
- cookies = get_cookies (jar, uri, for_http, FALSE);
+ cookies = get_cookies (jar, uri, NULL, NULL, NULL, for_http, FALSE, FALSE);
if (cookies) {
char *result = soup_cookies_to_cookie_header (cookies);
@@ -430,7 +453,39 @@ soup_cookie_jar_get_cookie_list (SoupCookieJar *jar, SoupURI *uri, gboolean for_
g_return_val_if_fail (SOUP_IS_COOKIE_JAR (jar), NULL);
g_return_val_if_fail (uri != NULL, NULL);
- return get_cookies (jar, uri, for_http, TRUE);
+ return get_cookies (jar, uri, NULL, NULL, NULL, for_http, FALSE, TRUE);
+}
+
+/**
+ * soup_cookie_jar_get_cookie_list_full:
+ * @jar: a #SoupCookieJar
+ * @uri: a #SoupURI
+ * @top_level: (nullable): a #SoupURI for the top level document
+ * @site_for_cookies: (nullable): a #SoupURI
+ * @method: (nullable): the HTTP method requesting the cookies, this
+ * should only be %NULL when @for_http is %FALSE
+ * @for_http: whether or not the return value is being passed directly
+ * to an HTTP operation
+ * @is_top_level_navigation: whether or not the http request is part of
+ * top level navigation
+ *
+ * This is an extended version of soup_cookie_jar_get_cookie_list() that
+ * provides more information required to use SameSite cookies. See the
+ * [SameSite cookies spec](https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-00)
+ * for more detailed information.
+ *
+ * Return value: (transfer full) (element-type Soup.Cookie): a #GSList
+ * with the cookies in the @jar that would be sent with a request to @uri.
+ *
+ * Since: 2.66
+ */
+GSList *
+soup_cookie_jar_get_cookie_list_full (SoupCookieJar *jar, SoupURI *uri, SoupURI *top_level, SoupURI
*site_for_cookies, const char *method, gboolean for_http, gboolean is_top_level_navigation)
+{
+ g_return_val_if_fail (SOUP_IS_COOKIE_JAR (jar), NULL);
+ g_return_val_if_fail (uri != NULL, NULL);
+
+ return get_cookies (jar, uri, top_level, site_for_cookies, g_intern_string (method), for_http,
is_top_level_navigation, TRUE);
}
/**
@@ -690,15 +745,21 @@ static void
msg_starting_cb (SoupMessage *msg, gpointer feature)
{
SoupCookieJar *jar = SOUP_COOKIE_JAR (feature);
- char *cookies;
+ GSList *cookies;
- cookies = soup_cookie_jar_get_cookies (jar, soup_message_get_uri (msg), TRUE);
- if (cookies) {
- soup_message_headers_replace (msg->request_headers,
- "Cookie", cookies);
- g_free (cookies);
- } else
+ cookies = soup_cookie_jar_get_cookie_list_full (jar, soup_message_get_uri (msg),
+ soup_message_get_first_party (msg),
+ soup_message_get_site_for_cookies (msg),
+ msg->method,
+ TRUE, soup_message_get_is_toplevel_navigation
(msg));
+ if (cookies != NULL) {
+ char *cookie_header = soup_cookies_to_cookie_header (cookies);
+ soup_message_headers_replace (msg->request_headers, "Cookie", cookie_header);
+ g_free (cookie_header);
+ g_slist_free_full (cookies, (GDestroyNotify)soup_cookie_free);
+ } else {
soup_message_headers_remove (msg->request_headers, "Cookie");
+ }
}
static void
diff --git a/libsoup/soup-cookie-jar.h b/libsoup/soup-cookie-jar.h
index d9c6850e..0c7b99dd 100644
--- a/libsoup/soup-cookie-jar.h
+++ b/libsoup/soup-cookie-jar.h
@@ -59,6 +59,14 @@ SOUP_AVAILABLE_IN_2_40
GSList * soup_cookie_jar_get_cookie_list (SoupCookieJar *jar,
SoupURI *uri,
gboolean for_http);
+SOUP_AVAILABLE_IN_2_66
+GSList * soup_cookie_jar_get_cookie_list_full (SoupCookieJar *jar,
+ SoupURI *uri,
+ SoupURI *top_level,
+ SoupURI
*site_for_cookies,
+ const char *method,
+ gboolean for_http,
+ gboolean
is_top_level_navigation);
SOUP_AVAILABLE_IN_2_24
void soup_cookie_jar_set_cookie (SoupCookieJar *jar,
SoupURI *uri,
diff --git a/libsoup/soup-cookie.c b/libsoup/soup-cookie.c
index 7cea82e5..67b691b1 100644
--- a/libsoup/soup-cookie.c
+++ b/libsoup/soup-cookie.c
@@ -88,6 +88,7 @@ soup_cookie_copy (SoupCookie *cookie)
copy->expires = soup_date_copy(cookie->expires);
copy->secure = cookie->secure;
copy->http_only = cookie->http_only;
+ copy->same_site_policy = cookie->same_site_policy;
return copy;
}
@@ -238,6 +239,19 @@ parse_one_cookie (const char *header, SoupURI *origin)
cookie->secure = TRUE;
if (has_value)
parse_value (&p, FALSE);
+ } else if (MATCH_NAME ("samesite")) {
+ if (has_value) {
+ char *policy = parse_value (&p, TRUE);
+ if (g_ascii_strcasecmp (policy, "Lax") == 0) {
+ soup_cookie_set_same_site_policy (cookie, SOUP_SAME_SITE_POLICY_LAX);
+ g_free (policy);
+ continue;
+ }
+ g_free (policy);
+ }
+
+ /* "Strict" and any invalid value are treated the same */
+ cookie->same_site_policy = SOUP_SAME_SITE_POLICY_STRICT;
} else {
/* Ignore unknown attributes, but we still have
* to skip over the value.
@@ -743,12 +757,56 @@ serialize_cookie (SoupCookie *cookie, GString *header, gboolean set_cookie)
g_string_append (header, "; domain=");
g_string_append (header, cookie->domain);
}
+ if (cookie->same_site_policy) {
+ g_string_append (header, "; SameSite=");
+ if (cookie->same_site_policy == SOUP_SAME_SITE_POLICY_LAX)
+ g_string_append (header, "Lax");
+ else
+ g_string_append (header, "Strict");
+ }
if (cookie->secure)
g_string_append (header, "; secure");
if (cookie->http_only)
g_string_append (header, "; HttpOnly");
}
+/**
+ * soup_cookie_set_same_site_policy:
+ * @cookie: a #SoupCookie
+ * @policy: a #SoupSameSitePolicy
+ *
+ * Since: 2.66
+ **/
+void
+soup_cookie_set_same_site_policy (SoupCookie *cookie,
+ SoupSameSitePolicy policy)
+{
+ switch (policy)
+ {
+ case SOUP_SAME_SITE_POLICY_NONE:
+ case SOUP_SAME_SITE_POLICY_STRICT:
+ case SOUP_SAME_SITE_POLICY_LAX:
+ cookie->same_site_policy = policy;
+ break;
+ default:
+ g_return_if_reached ();
+ }
+}
+
+/**
+ * soup_cookie_get_same_site_policy:
+ * @cookie: a #SoupCookie
+ *
+ * Returns a #SoupSameSitePolicy
+ *
+ * Since: 2.66
+ **/
+SoupSameSitePolicy
+soup_cookie_get_same_site_policy (SoupCookie *cookie)
+{
+ return cookie->same_site_policy;
+}
+
/**
* soup_cookie_to_set_cookie_header:
* @cookie: a #SoupCookie
diff --git a/libsoup/soup-cookie.h b/libsoup/soup-cookie.h
index 21973a49..53a298ac 100644
--- a/libsoup/soup-cookie.h
+++ b/libsoup/soup-cookie.h
@@ -10,6 +10,17 @@
G_BEGIN_DECLS
+/**
+ * SoupSameSitePolicy:
+ *
+ * Since: 2.66
+ */
+typedef enum {
+ SOUP_SAME_SITE_POLICY_NONE,
+ SOUP_SAME_SITE_POLICY_STRICT,
+ SOUP_SAME_SITE_POLICY_LAX,
+} SoupSameSitePolicy;
+
struct _SoupCookie {
char *name;
char *value;
@@ -18,6 +29,7 @@ struct _SoupCookie {
SoupDate *expires;
gboolean secure;
gboolean http_only;
+ SoupSameSitePolicy same_site_policy;
};
SOUP_AVAILABLE_IN_2_24
@@ -80,6 +92,12 @@ SOUP_AVAILABLE_IN_2_24
void soup_cookie_set_http_only (SoupCookie *cookie,
gboolean http_only);
+SOUP_AVAILABLE_IN_2_66
+void soup_cookie_set_same_site_policy (SoupCookie *cookie,
+ SoupSameSitePolicy policy);
+SOUP_AVAILABLE_IN_2_66
+SoupSameSitePolicy soup_cookie_get_same_site_policy (SoupCookie *cookie);
+
SOUP_AVAILABLE_IN_2_24
char *soup_cookie_to_set_cookie_header (SoupCookie *cookie);
SOUP_AVAILABLE_IN_2_24
diff --git a/libsoup/soup-message-private.h b/libsoup/soup-message-private.h
index 2f3d58a7..954586e3 100644
--- a/libsoup/soup-message-private.h
+++ b/libsoup/soup-message-private.h
@@ -36,6 +36,7 @@ typedef struct {
GSList *disabled_features;
SoupURI *first_party;
+ SoupURI *site_for_cookies;
GTlsCertificate *tls_certificate;
GTlsCertificateFlags tls_errors;
@@ -43,6 +44,8 @@ typedef struct {
SoupRequest *request;
SoupMessagePriority priority;
+
+ gboolean is_toplevel_navigation;
} SoupMessagePrivate;
#define SOUP_MESSAGE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_MESSAGE,
SoupMessagePrivate))
diff --git a/libsoup/soup-message.c b/libsoup/soup-message.c
index e4d78476..a0fbf603 100644
--- a/libsoup/soup-message.c
+++ b/libsoup/soup-message.c
@@ -144,6 +144,8 @@ enum {
PROP_TLS_CERTIFICATE,
PROP_TLS_ERRORS,
PROP_PRIORITY,
+ PROP_SITE_FOR_COOKIES,
+ PROP_IS_TOP_LEVEL_NAVIGATION,
LAST_PROP
};
@@ -174,6 +176,7 @@ soup_message_finalize (GObject *object)
g_clear_pointer (&priv->uri, soup_uri_free);
g_clear_pointer (&priv->first_party, soup_uri_free);
+ g_clear_pointer (&priv->site_for_cookies, soup_uri_free);
g_clear_object (&priv->addr);
g_clear_object (&priv->auth);
@@ -207,6 +210,9 @@ soup_message_set_property (GObject *object, guint prop_id,
case PROP_URI:
soup_message_set_uri (msg, g_value_get_boxed (value));
break;
+ case PROP_SITE_FOR_COOKIES:
+ soup_message_set_site_for_cookies (msg, g_value_get_boxed (value));
+ break;
case PROP_HTTP_VERSION:
soup_message_set_http_version (msg, g_value_get_enum (value));
break;
@@ -270,6 +276,9 @@ soup_message_get_property (GObject *object, guint prop_id,
case PROP_URI:
g_value_set_boxed (value, priv->uri);
break;
+ case PROP_SITE_FOR_COOKIES:
+ g_value_set_boxed (value, priv->site_for_cookies);
+ break;
case PROP_HTTP_VERSION:
g_value_set_enum (value, priv->http_version);
break;
@@ -805,6 +814,18 @@ soup_message_class_init (SoupMessageClass *message_class)
"The URI loaded in the application when the message was requested.",
SOUP_TYPE_URI,
G_PARAM_READWRITE));
+ /**
+ * SoupMessage:site-for-cookkies:
+ *
+ * Since: 2.66
+ */
+ g_object_class_install_property (
+ object_class, PROP_SITE_FOR_COOKIES,
+ g_param_spec_boxed (SOUP_MESSAGE_SITE_FOR_COOKIES,
+ "Site for cookies",
+ "The URI for the site to compare cookies against",
+ SOUP_TYPE_URI,
+ G_PARAM_READWRITE));
/**
* SOUP_MESSAGE_REQUEST_BODY:
*
@@ -1890,6 +1911,111 @@ soup_message_set_first_party (SoupMessage *msg,
g_object_notify (G_OBJECT (msg), SOUP_MESSAGE_FIRST_PARTY);
}
+/**
+ * soup_message_get_site_for_cookies:
+ * @msg: a #SoupMessage
+ *
+ * Gets @msg's site for cookies #SoupURI
+ *
+ * Returns: (transfer none): the @msg's site for cookies #SoupURI
+ *
+ * Since: 2.66
+ **/
+SoupURI *
+soup_message_get_site_for_cookies (SoupMessage *msg)
+{
+ SoupMessagePrivate *priv;
+
+ g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
+
+ priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+ return priv->site_for_cookies;
+}
+
+/**
+ * soup_message_set_site_for_cookies:
+ * @msg: a #SoupMessage
+ * @site_for_cookies: (nullable): the #SoupURI for the @msg's site for cookies
+ *
+ * Sets @site_for_cookies as the policy URL for same-site cookies for @msg.
+ *
+ * It is either the URL of the top-level document or %NULL depending on whether the registrable
+ * domain of this document's URL matches the registrable domain of its parent's/opener's
+ * URL. For the top-level document it is set to the document's URL.
+ *
+ * See the [same-site spec](https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-00)
+ * for more information.
+ *
+ * Since: 2.66
+ **/
+void
+soup_message_set_site_for_cookies (SoupMessage *msg,
+ SoupURI *site_for_cookies)
+{
+ SoupMessagePrivate *priv;
+
+ g_return_if_fail (SOUP_IS_MESSAGE (msg));
+
+ priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+
+ if (priv->site_for_cookies == site_for_cookies)
+ return;
+
+ if (priv->site_for_cookies) {
+ if (site_for_cookies && soup_uri_equal (priv->site_for_cookies, site_for_cookies))
+ return;
+
+ soup_uri_free (priv->site_for_cookies);
+ }
+
+ priv->site_for_cookies = site_for_cookies ? soup_uri_copy (site_for_cookies) : NULL;
+ g_object_notify (G_OBJECT (msg), SOUP_MESSAGE_SITE_FOR_COOKIES);
+}
+
+/**
+ * soup_message_set_is_toplevel_navigation:
+ * @msg: a #SoupMessage
+ * @is_toplevel_navigation: if %TRUE indicate the current request is a toplevel navigation
+ *
+ * See the [same-site spec](https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-00)
+ * for more information.
+ *
+ * Since: 2.66
+ **/
+void
+soup_message_set_is_toplevel_navigation (SoupMessage *msg,
+ gboolean is_toplevel_navigation)
+{
+ SoupMessagePrivate *priv;
+
+ g_return_if_fail (SOUP_IS_MESSAGE (msg));
+
+ priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+
+ if (priv->is_toplevel_navigation == is_toplevel_navigation)
+ return;
+
+ priv->is_toplevel_navigation = is_toplevel_navigation;
+ /*g_object_notify (G_OBJECT (msg), SOUP_MESSAGE_SITE_FOR_COOKIES);*/
+}
+
+/**
+ * soup_message_get_is_toplevel_navigation:
+ * @msg: a #SoupMessage
+ *
+ * Since: 2.66
+ **/
+gboolean
+soup_message_get_is_toplevel_navigation (SoupMessage *msg)
+{
+ SoupMessagePrivate *priv;
+
+ g_return_val_if_fail (SOUP_IS_MESSAGE (msg), FALSE);
+
+ priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+ return priv->is_toplevel_navigation;
+}
+
void
soup_message_set_https_status (SoupMessage *msg, SoupConnection *conn)
{
diff --git a/libsoup/soup-message.h b/libsoup/soup-message.h
index 93778961..3c2df7c4 100644
--- a/libsoup/soup-message.h
+++ b/libsoup/soup-message.h
@@ -69,6 +69,7 @@ GType soup_message_get_type (void);
#define SOUP_MESSAGE_STATUS_CODE "status-code"
#define SOUP_MESSAGE_REASON_PHRASE "reason-phrase"
#define SOUP_MESSAGE_FIRST_PARTY "first-party"
+#define SOUP_MESSAGE_SITE_FOR_COOKIES "site-for-cookies"
#define SOUP_MESSAGE_REQUEST_BODY "request-body"
#define SOUP_MESSAGE_REQUEST_BODY_DATA "request-body-data"
#define SOUP_MESSAGE_REQUEST_HEADERS "request-headers"
@@ -126,6 +127,16 @@ SoupURI *soup_message_get_first_party (SoupMessage *msg);
SOUP_AVAILABLE_IN_2_30
void soup_message_set_first_party (SoupMessage *msg,
SoupURI *first_party);
+SOUP_AVAILABLE_IN_2_66
+SoupURI *soup_message_get_site_for_cookies (SoupMessage *msg);
+SOUP_AVAILABLE_IN_2_66
+void soup_message_set_site_for_cookies (SoupMessage *msg,
+ SoupURI *site_for_cookies);
+SOUP_AVAILABLE_IN_2_66
+void soup_message_set_is_toplevel_navigation (SoupMessage *msg,
+ gboolean is_toplevel_navigation);
+SOUP_AVAILABLE_IN_2_66
+gboolean soup_message_get_is_toplevel_navigation (SoupMessage *msg);
typedef enum {
SOUP_MESSAGE_NO_REDIRECT = (1 << 1),
diff --git a/tests/cookies-test.c b/tests/cookies-test.c
index e3f79cae..85ec84ed 100644
--- a/tests/cookies-test.c
+++ b/tests/cookies-test.c
@@ -241,7 +241,7 @@ do_cookies_parsing_test (void)
msg = soup_message_new_from_uri ("GET", first_party_uri);
soup_message_headers_append (msg->request_headers, "Echo-Set-Cookie",
- "three=3; httpONLY=Wednesday; max-age=100");
+ "three=3; httpONLY=Wednesday; max-age=100; SameSite=Lax");
soup_session_send_message (session, msg);
g_object_unref (msg);
@@ -263,6 +263,7 @@ do_cookies_parsing_test (void)
got3 = TRUE;
g_assert_true (soup_cookie_get_http_only (cookie));
g_assert_true (soup_cookie_get_expires (cookie) != NULL);
+ g_assert_cmpint (soup_cookie_get_same_site_policy (cookie), ==,
SOUP_SAME_SITE_POLICY_LAX);
} else {
soup_test_assert (FALSE, "got unexpected cookie '%s'",
soup_cookie_get_name (cookie));
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]