Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add RFC 6761–compliant localhost loopback checks so secure cookies work on localhost (fixes: #382) #498

Open
wants to merge 10 commits into
base: master
Choose a base branch
from

Conversation

Chriss4123
Copy link

fixes: #382

Description

This commit extends how cookies are treated in secure contexts by fully recognizing localhost and loopback IPs as trustworthy origins, matching the de facto behavior of all modern browsers and RFC 6761. Previously, tough-cookie defaulted secure to true only for https: and wss: URLs, causing cookies with secure to not work with localhost.

What Changed

  1. New secureContext.ts

    • Implements isPotentiallyTrustworthy(url) by checking:
      • Schemes: https or wss
      • Loopback IPs: 127.0.0.1/8 and ::1
      • Hostnames: localhost and *.localhost
    • Adapted from Chromium’s IsLocalhost, IsLoopback and HostNoBracketsPiece, located at:
  2. cookieJar.ts Update

  • Replaced the old check which only accounts for https and wss schemes:
const secure =
  context.protocol &&
  (context.protocol == 'https:' || context.protocol == 'wss:')
  • with the new isPotentiallyTrustworthy function which accounts for secure schemes, localhost addresses and loopback addresses.

No existing functionality is removed. Where the old check would return true, so does the new check. The only added functionality is treating localhost addresses and loopback addresses as secure contexts which was previously not present.

All modern browsers (Chrome et. al) support secure cookies on localhost so it only makes sense tough-cookie supports this too.

@colincasey
Copy link
Contributor

@Chriss4123 thanks for this pull request! I try to review it by the end of the week.

@colincasey colincasey self-assigned this Feb 18, 2025
const secure =
context.protocol &&
(context.protocol == 'https:' || context.protocol == 'wss:')
const secure = isPotentiallyTrustworthy(url);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you're already editing this line, could you also rename const secure to const secureConnection to align with the suggested rename of the isPotentiallyTrustworthy function?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

secureConnection would be misleading here as it can be interpreted as having established a connection and done a valid tls/ssl handshake when in fact we are only checking the scheme and/or localhost.

I think secure is fine, however I could also do potentiallyTrustworthy instead.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we please consider potentiallyTrustworthy or maybe even, potentiallyTrusted here? I think secure has the same issue as secureConnection, that you've identified.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we please consider potentiallyTrustworthy or maybe even, potentiallyTrusted here? I think secure has the same issue as secureConnection, that you've identified.

I like this solution. I'll also add a link to https://www.w3.org/TR/secure-contexts/#is-origin-trustworthy above the line.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I had considered maybe leaving it as just secure is fine but I think secureConnection would help to disambiguate between the cookie's Secure attribute and what the RFC is referring to here ("secure" connection) in the Storage Model implementation details.

Let's put a pin on this for now though while we discuss the overall terminology in #498 (comment)

* @returns `true` if the URL is potentially trustworthy, otherwise `false`.
* @see {@link https://w3c.github.io/webappsec-secure-contexts/#potentially-trustworthy-origin W3C Spec}
*/
export function isPotentiallyTrustworthy(inputUrl: string | URL): boolean {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
export function isPotentiallyTrustworthy(inputUrl: string | URL): boolean {
export function isSecureConnection(inputUrl: string | URL): boolean {

RFC6265bis-19 uses the terminology "secure" connection when referring to the behaviour being outlined in this PR. E.g.;

If the request-uri does not denote a "secure" connection (as defined by the user agent), and the cookie's secure-only-flag is true, then abort these steps and ignore the cookie entirely.

NOTE: The notion of a "secure" connection is not defined by this document. Typically, user agents consider a connection secure if the connection makes use of transport-layer security, such as SSL or TLS, or if the host is trusted. For example, most user agents consider "https" to be a scheme that denotes a secure protocol and "localhost" to be trusted host.

This function should reflect that terminology and be renamed to isSecureConnection.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Secure Contexts uses the terminology 'potentially trustworthy'. What you described is an actual secure connection, i.e. if the web server has an SSL certificate installed.

When a browser (i.e. Chromium) decides to send Secure cookies, it checks the scheme for https, wss, etc. If it then connects to the server and it doesn't have an SSL certificate the request will fail anyways thus the cookies won't get sent.

localhost is an exception to this as it is considered potentially trustworthy.

A potentially trustworthy origin indicates that the scheme is of secure nature and/or we are on localhost. An actual "secure" connection means the web server has an SSL certificate which we can't check.

Therefore I believe isPotentiallyTrustworthy is more suitable here as it would be misleading to label it as secure when we in fact don't know that.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Understood @Chriss4123, though my intention here was to ground the naming in language referenced in RFC6265bis-19 which is deliberately vague about how it's defining this concept. Again, I'll point to the following:

NOTE: The notion of a "secure" connection is not defined by this document. Typically, user agents consider a connection secure if the connection makes use of transport-layer security, such as SSL or TLS, or if the host is trusted. For example, most user agents consider "https" to be a scheme that denotes a secure protocol and "localhost" to be trusted host.

Based on the examples given in the above text, the concept of a "secure connection" from this RFC can cover both the scheme and host.

You point out in your reply, that this is inaccurate and the terminology from Secure Contexts is more suitable. I don't disagree with you but I still prefer the terminology of "secure connection" to be used in the code because:

  • In RFC6265bis-19 you will find the phrase "secure" connection referenced at multiple points in the implementation details in both the Retrieval Algorithm and the Storage Model where the code from your PR is called
  • The phrase potentially trustworthy origin can be found buried in Appendix A of RFC6265bis-19 but there's no mention of Secure Contexts in the list of references.

So I would ask you to consider this from the perspective of a maintainer. It's easier to compare the implementation to the RFC when the language used in both matches. Even if that language is vague and somewhat misleading (which the RFC explicitly calls out) 😅

Mind you, I'm not going to die on this hill. There's enough overlap in the terminology to backup both arguments (httpwg/http-extensions#2605, httpwg/http-extensions#2759). If we keep the naming of isPotentiallyTrustworthy then some extra comments will need to be added to connect the dots between how Secure Contexts relates to the implementation details of RFC6265bis-19.

Copy link
Author

@Chriss4123 Chriss4123 Feb 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Understood @Chriss4123, though my intention here was to ground the naming in language referenced in RFC6265bis-19 which is deliberately vague about how it's defining this concept. Again, I'll point to the following:

NOTE: The notion of a "secure" connection is not defined by this document. Typically, user agents consider a connection secure if the connection makes use of transport-layer security, such as SSL or TLS, or if the host is trusted. For example, most user agents consider "https" to be a scheme that denotes a secure protocol and "localhost" to be trusted host.

Based on the examples given in the above text, the concept of a "secure connection" from this RFC can cover both the scheme and host.

You point out in your reply, that this is inaccurate and the terminology from Secure Contexts is more suitable. I don't disagree with you but I still prefer the terminology of "secure connection" to be used in the code because:

* In [RFC6265bis-19](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-19) you will find the phrase `"secure" connection` referenced at multiple points in the implementation details in both the Retrieval Algorithm and the Storage Model where the code from your PR is called

* The phrase `potentially trustworthy origin` can be found buried in [Appendix A of RFC6265bis-19](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-19#appendix-A-1.7.1) but there's no mention of [Secure Contexts](https://www.w3.org/TR/secure-contexts/) in the list of references.

So I would ask you to consider this from the perspective of a maintainer. It's easier to compare the implementation to the RFC when the language used in both matches. Even if that language is vague and somewhat misleading (which the RFC explicitly calls out) 😅

Mind you, I'm not going to die on this hill. There's enough overlap in the terminology to backup both arguments (httpwg/http-extensions#2605, httpwg/http-extensions#2759). If we keep the naming of isPotentiallyTrustworthy then some extra comments will need to be added to connect the dots between how Secure Contexts relates to the implementation details of RFC6265bis-19.

RFC6265bis-19 deliberately avoids rigidly defining what constitutes a “secure” connection for cookies, instead deferring that decision to user agents. In the spec’s definition of the Secure attribute, it explicitly notes that “’secure’ is defined by the user agent”. The cookie retrieval algorithm also requires that a Secure cookie be sent only if the request’s URL is over a “secure” channel – again left to the UA’s definition. An informative note in the spec:

“NOTE: The notion of a ‘secure’ connection is not defined by this document. Typically, user agents consider a connection secure if the connection makes use of transport-layer security, such as SSL or TLS, or if the host is trusted. For example, most user agents consider ‘https’ to be a scheme that denotes a secure protocol and ‘localhost’ to be [a] trusted host.” (draft-ietf-httpbis-rfc6265bis-19)

This makes it clear the IETF spec intentionally refrains from hard-coding a definition of “secure connection.” Instead, it acknowledges common UA behavior (treating TLS-protected schemes as secure, and even treating certain trusted hosts like localhost as secure-equivalent), but leaves the exact criteria up to implementations.

RFC6265bis-19 expects browsers to apply their own notion of what is secure. Modern browsers have, in practice, converged on the concept of “potentially trustworthy origins” to make this determination, rather than a naive check of scheme or transport alone.

Potentially Trustworthy Origins

Broadly, the phrase “secure connection” is less precisely defined, whereas “potentially trustworthy origin” (and the related concept of “secure contexts”) is the well-defined, normative standard used to gate security-sensitive behaviors. Multiple authoritative specifications define and use the concept of “potentially trustworthy” in place of ad-hoc notions of security:

  • W3C Secure Contexts Specification: This spec defines what it means for an origin to be potentially trustworthy. It provides an algorithm enumerating conditions under which an origin is considered secure enough. The Secure Contexts spec’s core definition is: “A potentially trustworthy origin is one which a user agent can generally trust as delivering data securely.” This formalizes the intuitive idea mentioned in RFC6265bis’s note that https and localhost are considered secure into a normative algorithm.

  • WHATWG HTML Standard: HTML integrates the secure contexts concept to determine when a browsing context is a “secure context.” The HTML spec states that “an environment is a secure context if the following algorithm returns true…”, and one of the key steps in that algorithm is: “If the result of Is URL potentially trustworthy? given environment’s top-level creation URL is ‘Potentially Trustworthy’, then return true.”. This defers to the Secure Contexts spec’s “Is origin potentially trustworthy?” check for the top-level page’s origin to decide if a context is secure.

  • WHATWG Fetch Standard: The Fetch spec similarly uses “potentially trustworthy” in defining network request behaviors. For example, the algorithms for handling Mixed Content use this terminology: “Upgrade a mixed content request to a potentially trustworthy URL, if appropriate.” (Fetch Standard - WhatWG).

Summarized: Secure Contexts, HTML, and Fetch (along with related specs like Mixed Content) all treat “potentially trustworthy origin” as the canonical definition of a secure environment. This covers HTTPS and WSS, but also explicitly includes other cases like localhost, and excludes others (plain http to remote hosts) – thus providing a precise security model. The term “secure connection” by itself has no formal definition in these specs and could be interpreted in narrower ways (e.g. “only TLS”).

Browser Implementations

Browser behavior has evolved to align with the “potentially trustworthy origin” model as it’s more accurate and future-proof than a simplistic “secure connection” check. Both Chromium and Firefox have made conscious changes to use the broader trustworthy origin concept for cookies marked Secure, rather than just checking for HTTPS:

  • Firefox: Firefox historically allowed Secure cookies on https:// but not on http:// origins. However, as developers increasingly run local servers on http://localhost and expect to use Secure cookies (or the __Host-/__Secure- name prefixes which require secure contexts), Mozilla updated Firefox’s implementation to treat http://localhost as secure for cookies. The Firefox bug report states the plan: “By RFC6265, the definition of ’secure’ [protocol] is defined by the user-agent. I propose to unify the implementations and use nsMixedContentBlocker::IsPotentiallyTrustworthyOrigin() instead of a simple ’https’ scheme check.” (1618113 - Allow ’secure’ cookies when set by localhost). The code for this can be found here.

  • Chromium: Historically, Chrome did allow Secure cookies on http://localhost (treating localhost as secure), but it initially did not allow the stricter __Host- and __Secure- cookie name prefixes on localhost. The Chromium team has broadened the definition of “secure” for cookies to include potentially trustworthy origins as well. A Chromium issue discussion notes that they consider support for __Host-/__Secure- on localhost “in scope”. The cookie handling code defines an enumeration of cookie source schemes with categories for cryptographic vs. non-cryptographic but trustworthy. Chrome’s CookieAccessScheme includes: kCryptographic (for HTTPS/WSS, implicitly trustworthy) and kTrustworthy (for non-HTTPS schemes that are deemed potentially trustworthy). A comment in the source links directly to the W3C Secure Contexts definition of potentially trustworthy origin, showing that Chrome’s engineers explicitly built the Secure=“potentially trustworthy” logic into their implementation...

It’s clear that “secure connection” as a term is too simplistic to capture the real conditions under which cookies (and other features) should be considered secure. A strict reading might equate secure connection only with TLS however both Firefox and Chrome purposefully include “localhost” (no TLS) as secure based on the reasoning that it’s a trustworthy origin. The term potentially trustworthy origin precisely describes the broader notion of security – it includes both cryptographic transport and other trustworthy circumstances (like loopback interfaces, local files, or extension contexts).

Terminology and Maintainability

The phrase “secure connection” is ambiguous without an explicit definition. The RFC6265bis text itself had to add a clarifying note to hint at meaning. If our implementation simply says “if secure connection, then …” we’d likely need a similar comment explaining what “secure” entails. By using the term “potentially trustworthy origin” (or referring to a secure context), we immediately invoke a well-defined concept. It’s unambiguous to a web platform engineer that this means the origin satisfies the standard Secure Contexts criteria. I believe future contributors are more likely to be familiar with the Secure Contexts definition (since it’s used across many specs and APIs) than with the cookie spec’s isolated wording.

Most modern web platform code (both in specs and in browser source) has standardized on talking about “trustworthy” or “secure contexts.” Our cookie implementation logic should speak the same language, aligning itself conceptually with web platforms, and making the code self-documenting in a clearer way.

It’s important to note that using “potentially trustworthy origin” in our implementation detail is not actually contradicting RFC6265bis-19 – it’s clarifying it. The RFC’s intent was clearly that user agents decide what “secure” means. Today’s consensus is that secure = potentially trustworthy origin. Issue discussions in the IETF HTTP Working Group suggest exactly this alignment: to “consider reusing the notion of ‘Is origin potentially trustworthy?’ from the Secure Contexts spec, instead of [a] user-agent-dependent ‘secure protocol’ check”. So while our implementation detail might use a different phrasing than the raw RFC text, it actually reflects the RFC’s prescribed approach (deferring to UA judgment) more faithfully by naming the precise criterion we’ve chosen to follow.

Sticking slavishly to the term “secure connection” for the sake of mirroring the RFC introduces ambiguity and requires redundant explanatory comments.

Using “potentially trustworthy” in place of “secure connection” is a technically sound decision. It is a precisely defined algorithm, and is the best interpretation of the RFC6265bis-19 secure mandate.

I can make appropriate comments linking the two terms thus maintaining fidelity to RFC6265bis-19.

@Chriss4123
Copy link
Author

@colincasey I've completed all the requested changes and also added a comment linking a potentially trustworthy origin and the notion of a "secure" connection in draft-ietf-httpbis-rfc6265bis-19.

I'm ready for you to re-review the PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Secure; Flag should be ignored when sending to 'localhost'
3 participants