1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
|
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef NET_DEVICE_BOUND_SESSIONS_COOKIE_CRAVING_H_
#define NET_DEVICE_BOUND_SESSIONS_COOKIE_CRAVING_H_
#include <optional>
#include <string>
#include "base/time/time.h"
#include "net/base/net_export.h"
#include "net/cookies/cookie_base.h"
#include "net/cookies/cookie_constants.h"
#include "net/cookies/cookie_partition_key.h"
namespace net {
class URLRequest;
class CanonicalCookie;
class FirstPartySetMetadata;
}
namespace net::device_bound_sessions {
namespace proto {
class CookieCraving;
}
// This class represents the need for a certain cookie to be present. It is not
// a cookie itself, but rather represents a requirement which can be satisfied
// by a real cookie (i.e. a CanonicalCookie). Each CookieCraving is specified by
// and associated with a DBSC (Device Bound Session Credentials) session.
//
// In general, CookieCraving behavior is intended to be as close as possible to
// CanonicalCookie, especially the inclusion logic, since they need to be
// matched up. However, some notable differences include:
//
// CookieCraving does not have a value field, i.e. they only have a name (and
// other attributes). The name can be the empty string. The name of a cookie is
// needed to identify it, but the value of a cookie is not relevant to its
// inclusion or exclusion, so CookieCraving omits it.
//
// CookieCraving does not have an expiry date. The expiry date of a
// CanonicalCookie often depends upon the creation time (if it is set via
// Max-Age), and a DBSC session config is not necessarily created at the same
// time as its corresponding Set-Cookie header, so we cannot guarantee that
// they'd match. DBSC also does not require a specific expiry date for the
// cookies whose presence it guarantees.
//
// CookieCraving does not implement lax-allow-unsafe behavior (it does not
// set a non-zero age threshold for it). The default CanonicalCookie
// lax-allow-unsafe behavior is problematic because it can result in two
// identical set-cookie lines (set from the same URL) exhibiting different
// inclusion results, if they happen to be on opposite sides of the
// lax-allow-unsafe age threshold. By not implementing lax-allow-unsafe,
// CookieCraving may sometimes be excluded even when a corresponding
// CanonicalCookie would be included for being under its lax-allow-unsafe age
// threshold. This means that servers deploying DBSC with SameSite-unspecified
// cookies SHOULD NOT rely on the presence of SameSite-unspecified cookies
// within 2 minutes of their creation time on cross-site POST and other unsafe
// request types, as DBSC cannot make any such guarantee.
class NET_EXPORT CookieCraving : public CookieBase {
public:
// Creates a new CookieCraving in the context of `url`, given a `name` and
// associated cookie `attributes`. (Note that CookieCravings do not have a
// "value".) `url` must be valid. `creation_time` may not be null. May return
// nullopt if an attribute value is invalid. If a CookieCraving is returned,
// it will satisfy IsValid(). If there is leading or trailing whitespace in
// `name`, it will get trimmed.
//
// `cookie_partition_key` only needs to be present if the attributes contain
// the Partitioned attribute. std::nullopt indicates an unpartitioned
// CookieCraving will be created. If there is a partition key but the
// attributes do not specify Partitioned, the resulting CookieCraving will be
// unpartitioned. If the partition_key is nullopt, the CookieCraving will
// always be unpartitioned even if the attributes specify Partitioned.
//
// SameSite and HttpOnly related parameters are not checked here,
// so creation of CookieCravings with e.g. SameSite=Strict from a cross-site
// context is allowed. Create() also does not check whether `url` has a secure
// scheme if attempting to create a Secure cookie. The Secure, SameSite, and
// HttpOnly related parameters should be checked when deciding CookieCraving
// inclusion for a given request/context.
//
// In general this is intended to closely mirror CanonicalCookie::Create.
// However, there are some simplifying assumptions made*, and metrics are not
// (currently) logged so as to not interfere with CanonicalCookie metrics.
// There is also no (current) need for a CookieInclusionStatus to be returned.
//
// * Simplifying assumptions (differing from CanonicalCookie):
// - The Domain() member of a CookieCraving is required to be non-empty,
// which CanonicalCookie does not require.
// - Cookie name prefixes (__Host- and __Secure-) are always checked
// case-insensitively, unlike CanonicalCookie which reads a Feature value
// to decide whether to check insensitively.
// - CanonicalCookie allows non-cryptographic URLs to create a cookie with a
// secure source_scheme, if that cookie was Secure, on the basis that that
// URL might be trustworthy when checked later. CookieCraving does not
// allow this.
static std::optional<CookieCraving> Create(
const GURL& url,
const std::string& name,
const std::string& attributes,
base::Time creation_time,
std::optional<CookiePartitionKey> cookie_partition_key);
CookieCraving(const CookieCraving& other);
CookieCraving(CookieCraving&& other);
CookieCraving& operator=(const CookieCraving& other);
CookieCraving& operator=(CookieCraving&& other);
~CookieCraving() override;
// Returns whether all CookieCraving fields are consistent, in canonical form,
// etc. (Mostly analogous to CanonicalCookie::IsCanonical, except without
// checks for access time.) Essentially, if this returns true, then this
// CookieCraving instance could have been created by Create().
// Other public methods of this class may not be called if IsValid() is false.
bool IsValid() const;
// Returns whether the given "real" cookie satisfies this CookieCraving, in
// the sense that DBSC will consider the required cookie present.
// The provided CanonicalCookie must be canonical.
bool IsSatisfiedBy(const CanonicalCookie& canonical_cookie) const;
std::string DebugString() const;
bool IsEqualForTesting(const CookieCraving& other) const;
// May return an invalid instance.
static CookieCraving CreateUnsafeForTesting(
std::string name,
std::string domain,
std::string path,
base::Time creation,
bool secure,
bool httponly,
CookieSameSite same_site,
std::optional<CookiePartitionKey> partition_key,
CookieSourceScheme source_scheme,
int source_port);
// Returns a protobuf object. May only be called for
// a valid CookieCraving object.
proto::CookieCraving ToProto() const;
// Creates a CookieCraving object from a protobuf
// object. If the protobuf contents are invalid,
// a std::nullopt is returned.
static std::optional<CookieCraving> CreateFromProto(
const proto::CookieCraving& proto);
// Whether the craving applies to the given `request`, with other
// arguments providing context for the access.
bool ShouldIncludeForRequest(
URLRequest* request,
const FirstPartySetMetadata& first_party_set_metadata,
const CookieOptions& options,
const CookieAccessParams& params) const;
private:
CookieCraving();
// Prefer Create() over this constructor. This may return non-valid instances.
CookieCraving(std::string name,
std::string domain,
std::string path,
base::Time creation,
bool secure,
bool httponly,
CookieSameSite same_site,
std::optional<CookiePartitionKey> partition_key,
CookieSourceScheme source_scheme,
int source_port);
using CookieBase::IncludeForRequestURL;
};
// Outputs a debug string, e.g. for more helpful test failure messages.
NET_EXPORT std::ostream& operator<<(std::ostream& os, const CookieCraving& cc);
} // namespace net::device_bound_sessions
#endif // NET_DEVICE_BOUND_SESSIONS_COOKIE_CRAVING_H_
|