From e039b824f93212b87262fc8e7c5cab6bbeaa3ddb Mon Sep 17 00:00:00 2001 From: Adrien Plazas Date: Tue, 29 Mar 2016 13:30:27 +0200 Subject: Add SoupHstsEnforcer This is a WIP. Add the SoupSessionFeature SoupHstsEnforcer to allow a session to support HSTS (RFC 6797). This also adds SoupHstsPolicy and an early version of SoupHstsEnforcerDb. --- docs/specs/README | 1 + docs/specs/rfc6797.txt | 2579 ++++++++++++++++++++++++++++++++++ libsoup/Makefile.am | 7 + libsoup/soup-hsts-enforcer-db.c | 314 +++++ libsoup/soup-hsts-enforcer-db.h | 45 + libsoup/soup-hsts-enforcer-private.h | 14 + libsoup/soup-hsts-enforcer.c | 602 ++++++++ libsoup/soup-hsts-enforcer.h | 53 + libsoup/soup-hsts-policy.c | 479 +++++++ libsoup/soup-hsts-policy.h | 59 + libsoup/soup-types.h | 2 + libsoup/soup.h | 3 + 12 files changed, 4158 insertions(+) create mode 100644 docs/specs/rfc6797.txt create mode 100644 libsoup/soup-hsts-enforcer-db.c create mode 100644 libsoup/soup-hsts-enforcer-db.h create mode 100644 libsoup/soup-hsts-enforcer-private.h create mode 100644 libsoup/soup-hsts-enforcer.c create mode 100644 libsoup/soup-hsts-enforcer.h create mode 100644 libsoup/soup-hsts-policy.c create mode 100644 libsoup/soup-hsts-policy.h diff --git a/docs/specs/README b/docs/specs/README index 0dee62d0..e498a22e 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 +rfc6797 - HTTP Strict Transport Security (HSTS) diff --git a/docs/specs/rfc6797.txt b/docs/specs/rfc6797.txt new file mode 100644 index 00000000..c4e689fb --- /dev/null +++ b/docs/specs/rfc6797.txt @@ -0,0 +1,2579 @@ + + + + + + +Internet Engineering Task Force (IETF) J. Hodges +Request for Comments: 6797 PayPal +Category: Standards Track C. Jackson +ISSN: 2070-1721 Carnegie Mellon University + A. Barth + Google, Inc. + November 2012 + + + HTTP Strict Transport Security (HSTS) + +Abstract + + This specification defines a mechanism enabling web sites to declare + themselves accessible only via secure connections and/or for users to + be able to direct their user agent(s) to interact with given sites + only over secure connections. This overall policy is referred to as + HTTP Strict Transport Security (HSTS). The policy is declared by web + sites via the Strict-Transport-Security HTTP response header field + and/or by other means, such as user agent configuration, for example. + +Status of This Memo + + This is an Internet Standards Track document. + + This document is a product of the Internet Engineering Task Force + (IETF). It represents the consensus of the IETF community. It has + received public review and has been approved for publication by the + Internet Engineering Steering Group (IESG). Further information on + Internet Standards is available in Section 2 of RFC 5741. + + Information about the current status of this document, any errata, + and how to provide feedback on it may be obtained at + http://www.rfc-editor.org/info/rfc6797. + + + + + + + + + + + + + + + + + +Hodges, et al. Standards Track [Page 1] + +RFC 6797 HTTP Strict Transport Security (HSTS) November 2012 + + +Copyright Notice + + Copyright (c) 2012 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 ....................................................4 + 1.1. Organization of This Specification .........................6 + 1.2. Document Conventions .......................................6 + 2. Overview ........................................................6 + 2.1. Use Cases ..................................................6 + 2.2. HTTP Strict Transport Security Policy Effects ..............6 + 2.3. Threat Model ...............................................6 + 2.3.1. Threats Addressed ...................................7 + 2.3.1.1. Passive Network Attackers ..................7 + 2.3.1.2. Active Network Attackers ...................7 + 2.3.1.3. Web Site Development and Deployment Bugs ...8 + 2.3.2. Threats Not Addressed ...............................8 + 2.3.2.1. Phishing ...................................8 + 2.3.2.2. Malware and Browser Vulnerabilities ........8 + 2.4. Requirements ...............................................9 + 2.4.1. Overall Requirement .................................9 + 2.4.1.1. Detailed Core Requirements .................9 + 2.4.1.2. Detailed Ancillary Requirements ...........10 + 3. Conformance Criteria ...........................................10 + 4. Terminology ....................................................11 + 5. HSTS Mechanism Overview ........................................13 + 5.1. HSTS Host Declaration .....................................13 + 5.2. HSTS Policy ...............................................13 + 5.3. HSTS Policy Storage and Maintenance by User Agents ........14 + 5.4. User Agent HSTS Policy Enforcement ........................14 + 6. Syntax .........................................................14 + 6.1. Strict-Transport-Security HTTP Response Header Field ......15 + 6.1.1. The max-age Directive ..............................16 + 6.1.2. The includeSubDomains Directive ....................16 + 6.2. Examples ..................................................16 + + + + +Hodges, et al. Standards Track [Page 2] + +RFC 6797 HTTP Strict Transport Security (HSTS) November 2012 + + + 7. Server Processing Model ........................................17 + 7.1. HTTP-over-Secure-Transport Request Type ...................17 + 7.2. HTTP Request Type .........................................18 + 8. User Agent Processing Model ....................................18 + 8.1. Strict-Transport-Security Response Header Field + Processing ................................................19 + 8.1.1. Noting an HSTS Host - Storage Model ................20 + 8.2. Known HSTS Host Domain Name Matching ......................20 + 8.3. URI Loading and Port Mapping ..............................21 + 8.4. Errors in Secure Transport Establishment ..................22 + 8.5. HTTP-Equiv Element Attribute .......................22 + 8.6. Missing Strict-Transport-Security Response Header Field ...23 + 9. Constructing an Effective Request URI ..........................23 + 9.1. ERU Fundamental Definitions ...............................23 + 9.2. Determining the Effective Request URI .....................24 + 9.2.1. Effective Request URI Examples .....................24 + 10. Domain Name IDNA-Canonicalization .............................25 + 11. Server Implementation and Deployment Advice ...................26 + 11.1. Non-Conformant User Agent Considerations .................26 + 11.2. HSTS Policy Expiration Time Considerations ...............26 + 11.3. Using HSTS in Conjunction with Self-Signed Public-Key + Certificates .............................................27 + 11.4. Implications of includeSubDomains ........................28 + 11.4.1. Considerations for Offering Unsecured HTTP + Services at Alternate Ports or Subdomains of an + HSTS Host ........................................28 + 11.4.2. Considerations for Offering Web Applications at + Subdomains of an HSTS Host .......................29 + 12. User Agent Implementation Advice ..............................30 + 12.1. No User Recourse .........................................30 + 12.2. User-Declared HSTS Policy ................................30 + 12.3. HSTS Pre-Loaded List .....................................31 + 12.4. Disallow Mixed Security Context Loads ....................31 + 12.5. HSTS Policy Deletion .....................................31 + 13. Internationalized Domain Names for Applications (IDNA): + Dependency and Migration ......................................32 + + + + + + + + + + + + + + + +Hodges, et al. Standards Track [Page 3] + +RFC 6797 HTTP Strict Transport Security (HSTS) November 2012 + + + 14. Security Considerations .......................................32 + 14.1. Underlying Secure Transport Considerations ...............32 + 14.2. Non-Conformant User Agent Implications ...................33 + 14.3. Ramifications of HSTS Policy Establishment Only over + Error-Free Secure Transport ..............................33 + 14.4. The Need for includeSubDomains ...........................34 + 14.5. Denial of Service ........................................35 + 14.6. Bootstrap MITM Vulnerability .............................36 + 14.7. Network Time Attacks .....................................37 + 14.8. Bogus Root CA Certificate Phish plus DNS Cache + Poisoning Attack .........................................37 + 14.9. Creative Manipulation of HSTS Policy Store ...............37 + 14.10. Internationalized Domain Names ..........................38 + 15. IANA Considerations ...........................................39 + 16. References ....................................................39 + 16.1. Normative References .....................................39 + 16.2. Informative References ...................................40 + Appendix A. Design Decision Notes .................................44 + Appendix B. Differences between HSTS Policy and Same-Origin + Policy ................................................45 + Appendix C. Acknowledgments .......................................46 + +1. Introduction + + HTTP [RFC2616] may be used over various transports, typically the + Transmission Control Protocol (TCP). However, TCP does not provide + channel integrity protection, confidentiality, or secure host + identification. Thus, the Secure Sockets Layer (SSL) protocol + [RFC6101] and its successor, Transport Layer Security (TLS) [RFC5246] + were developed in order to provide channel-oriented security and are + typically layered between application protocols and TCP. [RFC2818] + specifies how HTTP is layered onto TLS and defines the Uniform + Resource Identifier (URI) scheme of "https" (in practice, however, + HTTP user agents (UAs) typically use either TLS or SSL3, depending + upon a combination of negotiation with the server and user + preferences). + + UAs employ various local security policies with respect to the + characteristics of their interactions with web resources, depending + on (in part) whether they are communicating with a given web + resource's host using HTTP or HTTP-over-Secure-Transport. For + example, cookies ([RFC6265]) may be flagged as Secure. UAs are to + send such Secure cookies to their addressed host only over a secure + transport. This is in contrast to non-Secure cookies, which are + returned to the host regardless of transport (although subject to + other rules). + + + + + +Hodges, et al. Standards Track [Page 4] + +RFC 6797 HTTP Strict Transport Security (HSTS) November 2012 + + + UAs typically announce to their users any issues with secure + connection establishment, such as being unable to validate a TLS + server certificate trust chain, or if a TLS server certificate is + expired, or if a TLS host's domain name appears incorrectly in the + TLS server certificate (see Section 3.1 of [RFC2818]). Often, UAs + enable users to elect to continue to interact with a web resource's + host in the face of such issues. This behavior is sometimes referred + to as "click(ing) through" security [GoodDhamijaEtAl05] + [SunshineEgelmanEtAl09]; thus, it can be described as "click-through + insecurity". + + A key vulnerability enabled by click-through insecurity is the + leaking of any cookies the web resource may be using to manage a + user's session. The threat here is that an attacker could obtain the + cookies and then interact with the legitimate web resource while + impersonating the user. + + Jackson and Barth proposed an approach, in [ForceHTTPS], to enable + web resources to declare that any interactions by UAs with the web + resource must be conducted securely and that any issues with + establishing a secure transport session are to be treated as fatal + and without direct user recourse. The aim is to prevent click- + through insecurity and address other potential threats. + + This specification embodies and refines the approach proposed in + [ForceHTTPS]. For example, rather than using a cookie to convey + policy from a web resource's host to a UA, it defines an HTTP + response header field for this purpose. Additionally, a web + resource's host may declare its policy to apply to the entire domain + name subtree rooted at its host name. This enables HTTP Strict + Transport Security (HSTS) to protect so-called "domain cookies", + which are applied to all subdomains of a given web resource's host + name. + + This specification also incorporates notions from [JacksonBarth2008] + in that policy is applied on an "entire-host" basis: it applies to + HTTP (only) over any TCP port of the issuing host. + + Note that the policy defined by this specification is distinctly + different than the "same-origin policy" defined in "The Web Origin + Concept" [RFC6454]. These differences are summarized in Appendix B. + + + + + + + + + + +Hodges, et al. Standards Track [Page 5] + +RFC 6797 HTTP Strict Transport Security (HSTS) November 2012 + + +1.1. Organization of This Specification + + This specification begins with an overview of the use cases, policy + effects, threat models, and requirements for HSTS (in Section 2). + Then, Section 3 defines conformance requirements. Section 4 defines + terminology relevant to this document. The HSTS mechanism itself is + formally specified in Sections 5 through 15. + +1.2. Document Conventions + + NOTE: This is a note to the reader. These are points that should be + expressly kept in mind and/or considered. + +2. Overview + + This section discusses the use cases, summarizes the HSTS Policy, and + continues with a discussion of the threat model, non-addressed + threats, and derived requirements. + +2.1. Use Cases + + The high-level use case is a combination of: + + o Web browser user wishes to interact with various web sites (some + arbitrary, some known) in a secure fashion. + + o Web site deployer wishes to offer their site in an explicitly + secure fashion for their own, as well as their users', benefit. + +2.2. HTTP Strict Transport Security Policy Effects + + The effects of the HSTS Policy, as applied by a conformant UA in + interactions with a web resource host wielding such policy (known as + an HSTS Host), are summarized as follows: + + 1. UAs transform insecure URI references to an HSTS Host into secure + URI references before dereferencing them. + + 2. The UA terminates any secure transport connection attempts upon + any and all secure transport errors or warnings. + +2.3. Threat Model + + HSTS is concerned with three threat classes: passive network + attackers, active network attackers, and imperfect web developers. + However, it is explicitly not a remedy for two other classes of + threats: phishing and malware. Threats that are addressed, as well + as threats that are not addressed, are briefly discussed below. + + + +Hodges, et al. Standards Track [Page 6] + +RFC 6797 HTTP Strict Transport Security (HSTS) November 2012 + + + Readers may wish to refer to Section 2 of [ForceHTTPS] for details as + well as relevant citations. + +2.3.1. Threats Addressed + +2.3.1.1. Passive Network Attackers + + When a user browses the web on a local wireless network (e.g., an + 802.11-based wireless local area network) a nearby attacker can + possibly eavesdrop on the user's unencrypted Internet Protocol-based + connections, such as HTTP, regardless of whether or not the local + wireless network itself is secured [BeckTews09]. Freely available + wireless sniffing toolkits (e.g., [Aircrack-ng]) enable such passive + eavesdropping attacks, even if the local wireless network is + operating in a secure fashion. A passive network attacker using such + tools can steal session identifiers/cookies and hijack the user's web + session(s) by obtaining cookies containing authentication credentials + [ForceHTTPS]. For example, there exist widely available tools, such + as Firesheep (a web browser extension) [Firesheep], that enable their + wielder to obtain other local users' session cookies for various web + applications. + + To mitigate such threats, some web sites support, but usually do not + force, access using end-to-end secure transport -- e.g., signaled + through URIs constructed with the "https" scheme [RFC2818]. This can + lead users to believe that accessing such services using secure + transport protects them from passive network attackers. + Unfortunately, this is often not the case in real-world deployments, + as session identifiers are often stored in non-Secure cookies to + permit interoperability with versions of the service offered over + insecure transport ("Secure cookies" are those cookies containing the + "Secure" attribute [RFC6265]). For example, if the session + identifier for a web site (an email service, say) is stored in a + non-Secure cookie, it permits an attacker to hijack the user's + session if the user's UA makes a single insecure HTTP request to the + site. + +2.3.1.2. Active Network Attackers + + A determined attacker can mount an active attack, either by + impersonating a user's DNS server or, in a wireless network, by + spoofing network frames or offering a similarly named evil twin + access point. If the user is behind a wireless home router, an + attacker can attempt to reconfigure the router using default + passwords and other vulnerabilities. Some sites, such as banks, rely + on end-to-end secure transport to protect themselves and their users + from such active attackers. Unfortunately, browsers allow their + users to easily opt out of these protections in order to be usable + + + +Hodges, et al. Standards Track [Page 7] + +RFC 6797 HTTP Strict Transport Security (HSTS) November 2012 + + + for sites that incorrectly deploy secure transport, for example by + generating and self-signing their own certificates (without also + distributing their certification authority (CA) certificate to their + users' browsers). + +2.3.1.3. Web Site Development and Deployment Bugs + + The security of an otherwise uniformly secure site (i.e., all of its + content is materialized via "https" URIs) can be compromised + completely by an active attacker exploiting a simple mistake, such as + the loading of a cascading style sheet or a SWF (Shockwave Flash) + movie over an insecure connection (both cascading style sheets and + SWF movies can script the embedding page, to the surprise of many web + developers, plus some browsers do not issue so-called "mixed content + warnings" when SWF files are embedded via insecure connections). + Even if the site's developers carefully scrutinize their login page + for "mixed content", a single insecure embedding anywhere on the + overall site compromises the security of their login page because an + attacker can script (i.e., control) the login page by injecting code + (e.g., a script) into another, insecurely loaded, site page. + + NOTE: "Mixed content" as used above (see also Section 5.3 in + [W3C.REC-wsc-ui-20100812]) refers to the notion termed "mixed + security context" in this specification and should not be + confused with the same "mixed content" term used in the + context of markup languages such as XML and HTML. + +2.3.2. Threats Not Addressed + +2.3.2.1. Phishing + + Phishing attacks occur when an attacker solicits authentication + credentials from the user by hosting a fake site located on a + different domain than the real site, perhaps driving traffic to the + fake site by sending a link in an email message. Phishing attacks + can be very effective because users find it difficult to distinguish + the real site from a fake site. HSTS is not a defense against + phishing per se; rather, it complements many existing phishing + defenses by instructing the browser to protect session integrity and + long-lived authentication tokens [ForceHTTPS]. + +2.3.2.2. Malware and Browser Vulnerabilities + + Because HSTS is implemented as a browser security mechanism, it + relies on the trustworthiness of the user's system to protect the + session. Malicious code executing on the user's system can + compromise a browser session, regardless of whether HSTS is used. + + + + +Hodges, et al. Standards Track [Page 8] + +RFC 6797 HTTP Strict Transport Security (HSTS) November 2012 + + +2.4. Requirements + + This section identifies and enumerates various requirements derived + from the use cases and the threats discussed above and also lists the + detailed core requirements that HTTP Strict Transport Security + addresses, as well as ancillary requirements that are not directly + addressed. + +2.4.1. Overall Requirement + + o Minimize, for web browser users and web site deployers, the risks + that are derived from passive and active network attackers, web + site development and deployment bugs, and insecure user actions. + +2.4.1.1. Detailed Core Requirements + + These core requirements are derived from the overall requirement and + are addressed by this specification. + + 1. Web sites need to be able to declare to UAs that they should be + accessed using a strict security policy. + + 2. Web sites need to be able to instruct UAs that contact them + insecurely to do so securely. + + 3. UAs need to retain persistent data about web sites that signal + strict security policy enablement, for time spans declared by the + web sites. Additionally, UAs need to cache the "freshest" strict + security policy information, in order to allow web sites to + update the information. + + 4. UAs need to rewrite all insecure UA "http" URI loads to use the + "https" secure scheme for those web sites for which secure policy + is enabled. + + 5. Web site administrators need to be able to signal strict security + policy application to subdomains of higher-level domains for + which strict security policy is enabled, and UAs need to enforce + such policy. + + For example, both example.com and foo.example.com could set + policy for bar.foo.example.com. + + + + + + + + + +Hodges, et al. Standards Track [Page 9] + +RFC 6797 HTTP Strict Transport Security (HSTS) November 2012 + + + 6. UAs need to disallow security policy application to peer domains, + and/or higher-level domains, by domains for which strict security + policy is enabled. + + For example, neither bar.foo.example.com nor foo.example.com can + set policy for example.com, nor can bar.foo.example.com set + policy for foo.example.com. Also, foo.example.com cannot set + policy for sibling.example.com. + + 7. UAs need to prevent users from "clicking through" security + warnings. Halting connection attempts in the face of secure + transport exceptions is acceptable. See also Section 12.1 ("No + User Recourse"). + + NOTE: A means for uniformly securely meeting the first core + requirement above is not specifically addressed by this + specification (see Section 14.6 ("Bootstrap MITM + Vulnerability")). It may be addressed by a future revision of + this specification or some other specification. Note also + that there are means by which UA implementations may more + fully meet the first core requirement; see Section 12 ("User + Agent Implementation Advice"). + +2.4.1.2. Detailed Ancillary Requirements + + These ancillary requirements are also derived from the overall + requirement. They are not normatively addressed in this + specification but could be met by UA implementations at their + implementor's discretion, although meeting these requirements may be + complex. + + 1. Disallow "mixed security context" loads (see Section 2.3.1.3). + + 2. Facilitate user declaration of web sites for which strict + security policy is enabled, regardless of whether the sites + signal HSTS Policy. + +3. Conformance Criteria + + This specification is written for hosts and user agents. + + A conformant host is one that implements all the requirements listed + in this specification that are applicable to hosts. + + A conformant user agent is one that implements all the requirements + listed in this specification that are applicable to user agents. + + + + + +Hodges, et al. Standards Track [Page 10] + +RFC 6797 HTTP Strict Transport Security (HSTS) November 2012 + + + 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]. + +4. Terminology + + Terminology is defined in this section. + + ASCII case-insensitive comparison: + + means comparing two strings exactly, codepoint for codepoint, + except that the characters in the range U+0041 .. U+005A (i.e., + LATIN CAPITAL LETTER A to LATIN CAPITAL LETTER Z) and the + corresponding characters in the range U+0061 .. U+007A (i.e., + LATIN SMALL LETTER A to LATIN SMALL LETTER Z) are considered to + also match. See [Unicode] for details. + + codepoint: + + is a colloquial contraction of Code Point, which is any value in + the Unicode codespace; that is, the range of integers from 0 to + 10FFFF(hex) [Unicode]. + + domain name: + + is also referred to as "DNS name" and is defined in [RFC1035] to + be represented outside of the DNS protocol itself (and + implementations thereof) as a series of labels separated by dots, + e.g., "example.com" or "yet.another.example.org". In the context + of this specification, domain names appear in that portion of a + URI satisfying the reg-name production in "Appendix A. Collected + ABNF for URI" in [RFC3986], and the host component from the Host + HTTP header field production in Section 14.23 of [RFC2616]. + + NOTE: The domain names appearing in actual URI instances and + matching the aforementioned production components may or + may not be a fully qualified domain name. + + domain name label: + + is that portion of a domain name appearing "between the dots", + i.e., consider "foo.example.com": "foo", "example", and "com" are + all domain name labels. + + + + + + + + +Hodges, et al. Standards Track [Page 11] + +RFC 6797 HTTP Strict Transport Security (HSTS) November 2012 + + + Effective Request URI: + + is a URI, identifying the target resource, that can be inferred by + an HTTP host for any given HTTP request it receives. Such + inference is necessary because HTTP requests often do not contain + a complete "absolute" URI identifying the target resource. See + Section 9 ("Constructing an Effective Request URI"). + + HTTP Strict Transport Security: + + is the overall name for the combined UA- and server-side security + policy defined by this specification. + + HTTP Strict Transport Security Host: + + is a conformant host implementing the HTTP server aspects of the + HSTS Policy. This means that an HSTS Host returns the + "Strict-Transport-Security" HTTP response header field in its HTTP + response messages sent over secure transport. + + HTTP Strict Transport Security Policy: + + is the name of the combined overall UA- and server-side facets of + the behavior defined in this specification. + + HSTS: + + See HTTP Strict Transport Security. + + HSTS Host: + + See HTTP Strict Transport Security Host. + + HSTS Policy: + + See HTTP Strict Transport Security Policy. + + Known HSTS Host: + + is an HSTS Host for which the UA has an HSTS Policy in effect; + i.e., the UA has noted this host as a Known HSTS Host. See + Section 8.1.1 ("Noting an HSTS Host - Storage Model") for + particulars. + + Local policy: + + comprises policy rules that deployers specify and that are often + manifested as configuration settings. + + + +Hodges, et al. Standards Track [Page 12] + +RFC 6797 HTTP Strict Transport Security (HSTS) November 2012 + + + MITM: + + is an acronym for "man in the middle". See "man-in-the-middle + attack" in [RFC4949]. + + Request URI: + + is the URI used to cause a UA to issue an HTTP request message. + See also "Effective Request URI". + + UA: + + is an acronym for "user agent". For the purposes of this + specification, a UA is an HTTP client application typically + actively manipulated by a user [RFC2616]. + + unknown HSTS Host: + + is an HSTS Host that the user agent has not noted. + +5. HSTS Mechanism Overview + + This section provides an overview of the mechanism by which an HSTS + Host conveys its HSTS Policy to UAs and how UAs process the HSTS + Policies received from HSTS Hosts. The mechanism details are + specified in Sections 6 through 15. + +5.1. HSTS Host Declaration + + An HTTP host declares itself an HSTS Host by issuing to UAs an HSTS + Policy, which is represented by and conveyed via the + Strict-Transport-Security HTTP response header field over secure + transport (e.g., TLS). Upon error-free receipt and processing of + this header by a conformant UA, the UA regards the host as a Known + HSTS Host. + +5.2. HSTS Policy + + An HSTS Policy directs UAs to communicate with a Known HSTS Host only + over secure transport and specifies policy retention time duration. + + HSTS Policy explicitly overrides the UA processing of URI references, + user input (e.g., via the "location bar"), or other information that, + in the absence of HSTS Policy, might otherwise cause UAs to + communicate insecurely with the Known HSTS Host. + + + + + + +Hodges, et al. Standards Track [Page 13] + +RFC 6797 HTTP Strict Transport Security (HSTS) November 2012 + + + An HSTS Policy may contain an optional directive -- includeSubDomains + -- specifying that this HSTS Policy also applies to any hosts whose + domain names are subdomains of the Known HSTS Host's domain name. + +5.3. HSTS Policy Storage and Maintenance by User Agents + + UAs store and index HSTS Policies based strictly upon the domain + names of the issuing HSTS Hosts. + + This means that UAs will maintain the HSTS Policy of any given HSTS + Host separately from any HSTS Policies issued by any other HSTS Hosts + whose domain names are superdomains or subdomains of the given HSTS + Host's domain name. Only the given HSTS Host can update or can cause + deletion of its issued HSTS Policy. It accomplishes this by sending + Strict-Transport-Security HTTP response header fields to UAs with new + values for policy time duration and subdomain applicability. Thus, + UAs cache the "freshest" HSTS Policy information on behalf of an HSTS + Host. Specifying a zero time duration signals the UA to delete the + HSTS Policy (including any asserted includeSubDomains directive) for + that HSTS Host. See Section 8.1 ("Strict-Transport-Security Response + Header Field Processing") for details. Additionally, Section 6.2 + presents examples of Strict-Transport-Security HTTP response header + fields. + +5.4. User Agent HSTS Policy Enforcement + + When establishing an HTTP connection to a given host, however + instigated, the UA examines its cache of Known HSTS Hosts to see if + there are any with domain names that are superdomains of the given + host's domain name. If any are found, and of those if any have the + includeSubDomains directive asserted, then HSTS Policy applies to the + given host. Otherwise, HSTS Policy applies to the given host only if + the given host is itself known to the UA as an HSTS Host. See + Section 8.3 ("URI Loading and Port Mapping") for details. + +6. Syntax + + This section defines the syntax of the Strict-Transport-Security HTTP + response header field and its directives, and presents some examples. + + Section 7 ("Server Processing Model") then details how hosts employ + this header field to declare their HSTS Policy, and Section 8 ("User + Agent Processing Model") details how user agents process the header + field and apply the HSTS Policy. + + + + + + + +Hodges, et al. Standards Track [Page 14] + +RFC 6797 HTTP Strict Transport Security (HSTS) November 2012 + + +6.1. Strict-Transport-Security HTTP Response Header Field + + The Strict-Transport-Security HTTP response header field (STS header + field) indicates to a UA that it MUST enforce the HSTS Policy in + regards to the host emitting the response message containing this + header field. + + The ABNF (Augmented Backus-Naur Form) syntax for the STS header field + is given below. It is based on the Generic Grammar defined in + Section 2 of [RFC2616] (which includes a notion of "implied linear + whitespace", also known as "implied *LWS"). + + Strict-Transport-Security = "Strict-Transport-Security" ":" + [ directive ] *( ";" [ directive ] ) + + directive = directive-name [ "=" directive-value ] + directive-name = token + directive-value = token | quoted-string + + where: + + token = + quoted-string = + + The two directives defined in this specification are described below. + The overall requirements for directives are: + + 1. The order of appearance of directives is not significant. + + 2. All directives MUST appear only once in an STS header field. + Directives are either optional or required, as stipulated in + their definitions. + + 3. Directive names are case-insensitive. + + 4. UAs MUST ignore any STS header field containing directives, or + other header field value data, that does not conform to the + syntax defined in this specification. + + 5. If an STS header field contains directive(s) not recognized by + the UA, the UA MUST ignore the unrecognized directives, and if + the STS header field otherwise satisfies the above requirements + (1 through 4), the UA MUST process the recognized directives. + + Additional directives extending the semantic functionality of the STS + header field can be defined in other specifications, with a registry + (having an IANA policy definition of IETF Review [RFC5226]) defined + for them at such time. + + + +Hodges, et al. Standards Track [Page 15] + +RFC 6797 HTTP Strict Transport Security (HSTS) November 2012 + + + NOTE: Such future directives will be ignored by UAs implementing + only this specification, as well as by generally + non-conforming UAs. See Section 14.2 ("Non-Conformant User + Agent Implications") for further discussion. + +6.1.1. The max-age Directive + + The REQUIRED "max-age" directive specifies the number of seconds, + after the reception of the STS header field, during which the UA + regards the host (from whom the message was received) as a Known HSTS + Host. See also Section 8.1.1 ("Noting an HSTS Host - Storage + Model"). The delta-seconds production is specified in [RFC2616]. + + The syntax of the max-age directive's REQUIRED value (after + quoted-string unescaping, if necessary) is defined as: + + max-age-value = delta-seconds + + delta-seconds = <1*DIGIT, defined in [RFC2616], Section 3.3.2> + + NOTE: A max-age value of zero (i.e., "max-age=0") signals the UA to + cease regarding the host as a Known HSTS Host, including the + includeSubDomains directive (if asserted for that HSTS Host). + See also Section 8.1 ("Strict-Transport-Security Response + Header Field Processing"). + +6.1.2. The includeSubDomains Directive + + The OPTIONAL "includeSubDomains" directive is a valueless directive + which, if present (i.e., it is "asserted"), signals the UA that the + HSTS Policy applies to this HSTS Host as well as any subdomains of + the host's domain name. + +6.2. Examples + + The HSTS header field below stipulates that the HSTS Policy is to + remain in effect for one year (there are approximately 31536000 + seconds in a year), and the policy applies only to the domain of the + HSTS Host issuing it: + + Strict-Transport-Security: max-age=31536000 + + The HSTS header field below stipulates that the HSTS Policy is to + remain in effect for approximately six months and that the policy + applies to the domain of the issuing HSTS Host and all of its + subdomains: + + Strict-Transport-Security: max-age=15768000 ; includeSubDomains + + + +Hodges, et al. Standards Track [Page 16] + +RFC 6797 HTTP Strict Transport Security (HSTS) November 2012 + + + The max-age directive value can optionally be quoted: + + Strict-Transport-Security: max-age="31536000" + + The HSTS header field below indicates that the UA must delete the + entire HSTS Policy associated with the HSTS Host that sent the header + field: + + Strict-Transport-Security: max-age=0 + + The HSTS header field below has exactly the same effect as the one + immediately above because the includeSubDomains directive's presence + in the HSTS header field is ignored when max-age is zero: + + Strict-Transport-Security: max-age=0; includeSubDomains + +7. Server Processing Model + + This section describes the processing model that HSTS Hosts + implement. The model comprises two facets: the first being the + processing rules for HTTP request messages received over a secure + transport (TLS [RFC5246] or SSL [RFC6101]; see also Section 14.1 + ("Underlying Secure Transport Considerations")), and the second being + the processing rules for HTTP request messages received over + non-secure transports, such as TCP. + +7.1. HTTP-over-Secure-Transport Request Type + + When replying to an HTTP request that was conveyed over a secure + transport, an HSTS Host SHOULD include in its response message an STS + header field that MUST satisfy the grammar specified above in + Section 6.1 ("Strict-Transport-Security HTTP Response Header Field"). + If an STS header field is included, the HSTS Host MUST include only + one such header field. + + Establishing a given host as a Known HSTS Host, in the context of a + given UA, MAY be accomplished over HTTP, which is in turn running + over secure transport, by correctly returning (per this + specification) at least one valid STS header field to the UA. Other + mechanisms, such as a client-side pre-loaded Known HSTS Host list, + MAY also be used; e.g., see Section 12 ("User Agent Implementation + Advice"). + + NOTE: Including the STS header field is stipulated as a "SHOULD" in + order to accommodate various server- and network-side caches + and load-balancing configurations where it may be difficult to + uniformly emit STS header fields on behalf of a given HSTS + Host. + + + +Hodges, et al. Standards Track [Page 17] + +RFC 6797 HTTP Strict Transport Security (HSTS) November 2012 + + +7.2. HTTP Request Type + + If an HSTS Host receives an HTTP request message over a non-secure + transport, it SHOULD send an HTTP response message containing a + status code indicating a permanent redirect, such as status code 301 + (Section 10.3.2 of [RFC2616]), and a Location header field value + containing either the HTTP request's original Effective Request URI + (see Section 9 ("Constructing an Effective Request URI")) altered as + necessary to have a URI scheme of "https", or a URI generated + according to local policy with a URI scheme of "https". + + NOTE: The above behavior is a "SHOULD" rather than a "MUST" due to: + + * Risks in server-side non-secure-to-secure redirects + [OWASP-TLSGuide]. + + * Site deployment characteristics. For example, a site that + incorporates third-party components may not behave correctly + when doing server-side non-secure-to-secure redirects in the + case of being accessed over non-secure transport but does + behave correctly when accessed uniformly over secure transport. + The latter is the case given an HSTS-capable UA that has + already noted the site as a Known HSTS Host (by whatever means, + e.g., prior interaction or UA configuration). + + An HSTS Host MUST NOT include the STS header field in HTTP responses + conveyed over non-secure transport. + +8. User Agent Processing Model + + This section describes the HTTP Strict Transport Security processing + model for UAs. There are several facets to the model, enumerated by + the following subsections. + + This processing model assumes that the UA implements IDNA2008 + [RFC5890], or possibly IDNA2003 [RFC3490], as noted in Section 13 + ("Internationalized Domain Names for Applications (IDNA): Dependency + and Migration"). It also assumes that all domain names manipulated + in this specification's context are already IDNA-canonicalized as + outlined in Section 10 ("Domain Name IDNA-Canonicalization") prior to + the processing specified in this section. + + NOTE: [RFC3490] is referenced due to its ongoing relevance to + actual deployments for the foreseeable future. + + The above assumptions mean that this processing model also + specifically assumes that appropriate IDNA and Unicode validations + and character list testing have occurred on the domain names, in + + + +Hodges, et al. Standards Track [Page 18] + +RFC 6797 HTTP Strict Transport Security (HSTS) November 2012 + + + conjunction with their IDNA-canonicalization, prior to the processing + specified in this section. See the IDNA-specific security + considerations in Section 14.10 ("Internationalized Domain Names") + for rationale and further details. + +8.1. Strict-Transport-Security Response Header Field Processing + + If an HTTP response, received over a secure transport, includes an + STS header field, conforming to the grammar specified in Section 6.1 + ("Strict-Transport-Security HTTP Response Header Field"), and there + are no underlying secure transport errors or warnings (see + Section 8.4), the UA MUST either: + + o Note the host as a Known HSTS Host if it is not already so noted + (see Section 8.1.1 ("Noting an HSTS Host - Storage Model")), + + or + + o Update the UA's cached information for the Known HSTS Host if + either or both of the max-age and includeSubDomains header field + value tokens are conveying information different than that already + maintained by the UA. + + The max-age value is essentially a "time to live" value relative + to the reception time of the STS header field. + + If the max-age header field value token has a value of zero, the + UA MUST remove its cached HSTS Policy information (including the + includeSubDomains directive, if asserted) if the HSTS Host is + known, or the UA MUST NOT note this HSTS Host if it is not yet + known. + + If a UA receives more than one STS header field in an HTTP + response message over secure transport, then the UA MUST process + only the first such header field. + + Otherwise: + + o If an HTTP response is received over insecure transport, the UA + MUST ignore any present STS header field(s). + + o The UA MUST ignore any STS header fields not conforming to the + grammar specified in Section 6.1 ("Strict-Transport-Security HTTP + Response Header Field"). + + + + + + + +Hodges, et al. Standards Track [Page 19] + +RFC 6797 HTTP Strict Transport Security (HSTS) November 2012 + + +8.1.1. Noting an HSTS Host - Storage Model + + If the substring matching the host production from the Request-URI + (of the message to which the host responded) syntactically matches + the IP-literal or IPv4address productions from Section 3.2.2 of + [RFC3986], then the UA MUST NOT note this host as a Known HSTS Host. + + Otherwise, if the substring does not congruently match a Known HSTS + Host's domain name, per the matching procedure specified in + Section 8.2 ("Known HSTS Host Domain Name Matching"), then the UA + MUST note this host as a Known HSTS Host, caching the HSTS Host's + domain name and noting along with it the expiry time of this + information, as effectively stipulated per the given max-age value, + as well as whether the includeSubDomains directive is asserted or + not. See also Section 11.2 ("HSTS Policy Expiration Time + Considerations"). + + The UA MUST NOT modify the expiry time or the includeSubDomains + directive of any superdomain matched Known HSTS Host. + + A Known HSTS Host is "expired" if its cache entry has an expiry date + in the past. The UA MUST evict all expired Known HSTS Hosts from its + cache if, at any time, an expired Known HSTS Host exists in the + cache. + +8.2. Known HSTS Host Domain Name Matching + + A given domain name may match a Known HSTS Host's domain name in one + or both of two fashions: a congruent match, or a superdomain match. + Alternatively, there may be no match. + + The steps below determine whether there are any matches, and if so, + of which fashion: + + Compare the given domain name with the domain name of each of the + UA's unexpired Known HSTS Hosts. For each Known HSTS Host's + domain name, the comparison is done with the given domain name + label-by-label (comparing only labels) using an ASCII case- + insensitive comparison beginning with the rightmost label, and + continuing right-to-left. See also Section 2.3.2.4 of [RFC5890]. + + * Superdomain Match + + If a label-for-label match between an entire Known HSTS Host's + domain name and a right-hand portion of the given domain name + is found, then this Known HSTS Host's domain name is a + superdomain match for the given domain name. There could be + multiple superdomain matches for a given domain name. + + + +Hodges, et al. Standards Track [Page 20] + +RFC 6797 HTTP Strict Transport Security (HSTS) November 2012 + + + For example: + + Given domain name (DN): qaz.bar.foo.example.com + + Superdomain matched + Known HSTS Host DN: bar.foo.example.com + + Superdomain matched + Known HSTS Host DN: foo.example.com + + + * Congruent Match + + If a label-for-label match between a Known HSTS Host's domain + name and the given domain name is found -- i.e., there are no + further labels to compare -- then the given domain name + congruently matches this Known HSTS Host. + + For example: + + Given domain name: foo.example.com + + Congruently matched + Known HSTS Host DN: foo.example.com + + + * Otherwise, if no matches are found, the given domain name does + not represent a Known HSTS Host. + +8.3. URI Loading and Port Mapping + + Whenever the UA prepares to "load" (also known as "dereference") any + "http" URI [RFC3986] (including when following HTTP redirects + [RFC2616]), the UA MUST first determine whether a domain name is + given in the URI and whether it matches a Known HSTS Host, using + these steps: + + 1. Extract from the URI any substring described by the host + component of the authority component of the URI. + + 2. If the substring is null, then there is no match with any Known + HSTS Host. + + 3. Else, if the substring is non-null and syntactically matches the + IP-literal or IPv4address productions from Section 3.2.2 of + [RFC3986], then there is no match with any Known HSTS Host. + + + + + +Hodges, et al. Standards Track [Page 21] + +RFC 6797 HTTP Strict Transport Security (HSTS) November 2012 + + + 4. Otherwise, the substring is a given domain name, which MUST be + matched against the UA's Known HSTS Hosts using the procedure in + Section 8.2 ("Known HSTS Host Domain Name Matching"). + + 5. If, when performing domain name matching any superdomain match + with an asserted includeSubDomains directive is found, or, if no + superdomain matches with asserted includeSubDomains directives + are found and a congruent match is found (with or without an + asserted includeSubDomains directive), then before proceeding + with the load: + + The UA MUST replace the URI scheme with "https" [RFC2818], and + + if the URI contains an explicit port component of "80", then + the UA MUST convert the port component to be "443", or + + if the URI contains an explicit port component that is not + equal to "80", the port component value MUST be preserved; + otherwise, + + if the URI does not contain an explicit port component, the UA + MUST NOT add one. + + NOTE: These steps ensure that the HSTS Policy applies to HTTP + over any TCP port of an HSTS Host. + + NOTE: In the case where an explicit port is provided (and to a + lesser extent with subdomains), it is reasonably likely that + there is actually an HTTP (i.e., non-secure) server running on + the specified port and that an HTTPS request will thus fail + (see item 6 in Appendix A ("Design Decision Notes")). + +8.4. Errors in Secure Transport Establishment + + When connecting to a Known HSTS Host, the UA MUST terminate the + connection (see also Section 12 ("User Agent Implementation Advice")) + if there are any errors, whether "warning" or "fatal" or any other + error level, with the underlying secure transport. For example, this + includes any errors found in certificate validity checking that UAs + employ, such as via Certificate Revocation Lists (CRLs) [RFC5280], or + via the Online Certificate Status Protocol (OCSP) [RFC2560], as well + as via TLS server identity checking [RFC6125]. + +8.5. HTTP-Equiv Element Attribute + + UAs MUST NOT heed http-equiv="Strict-Transport-Security" attribute + settings on elements [W3C.REC-html401-19991224] in received + content. + + + +Hodges, et al. Standards Track [Page 22] + +RFC 6797 HTTP Strict Transport Security (HSTS) November 2012 + + +8.6. Missing Strict-Transport-Security Response Header Field + + If a UA receives HTTP responses from a Known HSTS Host over a secure + channel but the responses are missing the STS header field, the UA + MUST continue to treat the host as a Known HSTS Host until the + max-age value for the knowledge of that Known HSTS Host is reached. + Note that the max-age value could be effectively infinite for a given + Known HSTS Host. For example, this would be the case if the Known + HSTS Host is part of a pre-configured list that is implemented such + that the list entries never "age out". + +9. Constructing an Effective Request URI + + This section specifies how an HSTS Host must construct the Effective + Request URI for a received HTTP request. + + HTTP requests often do not carry an absoluteURI for the target + resource; instead, the URI needs to be inferred from the Request-URI, + Host header field, and connection context ([RFC2616], Sections 3.2.1, + 5.1.2, and 5.2). The result of this process is called the "effective + request URI (ERU)". The "target resource" is the resource identified + by the effective request URI. + +9.1. ERU Fundamental Definitions + + The first line of an HTTP request message, Request-Line, is specified + by the following ABNF from [RFC2616], Section 5.1: + + Request-Line = Method SP Request-URI SP HTTP-Version CRLF + + The Request-URI, within the Request-Line, is specified by the + following ABNF from [RFC2616], Section 5.1.2: + + Request-URI = "*" | absoluteURI | abs_path | authority + + The Host request header field is specified by the following ABNF from + [RFC2616], Section 14.23: + + Host = "Host" ":" host [ ":" port ] + + + + + + + + + + + + +Hodges, et al. Standards Track [Page 23] + +RFC 6797 HTTP Strict Transport Security (HSTS) November 2012 + + +9.2. Determining the Effective Request URI + + If the Request-URI is an absoluteURI, then the effective request URI + is the Request-URI. + + If the Request-URI uses the abs_path form or the asterisk form, and + the Host header field is present, then the effective request URI is + constructed by concatenating: + + o the scheme name: "http" if the request was received over an + insecure TCP connection, or "https" when received over a TLS/ + SSL-secured TCP connection, and + + o the octet sequence "://", and + + o the host, and the port (if present), from the Host header field, + and + + o the Request-URI obtained from the Request-Line, unless the + Request-URI is just the asterisk "*". + + If the Request-URI uses the abs_path form or the asterisk form, and + the Host header field is not present, then the effective request URI + is undefined. + + Otherwise, when Request-URI uses the authority form, the effective + request URI is undefined. + + Effective request URIs are compared using the rules described in + [RFC2616] Section 3.2.3, except that empty path components MUST NOT + be treated as equivalent to an absolute path of "/". + +9.2.1. Effective Request URI Examples + + Example 1: the effective request URI for the message + + GET /pub/WWW/TheProject.html HTTP/1.1 + Host: www.example.org:8080 + + (received over an insecure TCP connection) is "http", plus "://", + plus the authority component "www.example.org:8080", plus the + request-target "/pub/WWW/TheProject.html". Thus, it is + "http://www.example.org:8080/pub/WWW/TheProject.html". + + + + + + + + +Hodges, et al. Standards Track [Page 24] + +RFC 6797 HTTP Strict Transport Security (HSTS) November 2012 + + + Example 2: the effective request URI for the message + + OPTIONS * HTTP/1.1 + Host: www.example.org + + (received over an SSL/TLS secured TCP connection) is "https", plus + "://", plus the authority component "www.example.org". Thus, it is + "https://www.example.org". + +10. Domain Name IDNA-Canonicalization + + An IDNA-canonicalized domain name is the output string generated by + the following steps. The input is a putative domain name string + ostensibly composed of any combination of "A-labels", "U-labels", and + "NR-LDH labels" (see Section 2 of [RFC5890]) concatenated using some + separator character (typically "."). + + 1. Convert the input putative domain name string to an order- + preserving sequence of individual label strings. + + 2. When implementing IDNA2008, convert, validate, and test each + A-label and U-label found among the sequence of individual label + strings, using the procedures defined in Sections 5.3 through 5.5 + of [RFC5891]. + + Otherwise, when implementing IDNA2003, convert each label using + the "ToASCII" conversion in Section 4 of [RFC3490] (see also the + definition of "equivalence of labels" in Section 2 of [RFC3490]). + + 3. If no errors occurred during the foregoing step, concatenate all + the labels in the sequence, in order, into a string, separating + each label from the next with a %x2E (".") character. The + resulting string, known as an IDNA-canonicalized domain name, is + appropriate for use in the context of Section 8 ("User Agent + Processing Model"). + + Otherwise, errors occurred. The input putative domain name + string was not successfully IDNA-canonicalized. Invokers of this + procedure should attempt appropriate error recovery. + + See also Sections 13 ("Internationalized Domain Names for + Applications (IDNA): Dependency and Migration") and 14.10 + ("Internationalized Domain Names") of this specification for further + details and considerations. + + + + + + + +Hodges, et al. Standards Track [Page 25] + +RFC 6797 HTTP Strict Transport Security (HSTS) November 2012 + + +11. Server Implementation and Deployment Advice + + This section is non-normative. + +11.1. Non-Conformant User Agent Considerations + + Non-conformant UAs ignore the Strict-Transport-Security header field; + thus, non-conformant user agents do not address the threats described + in Section 2.3.1 ("Threats Addressed"). Please refer to Section 14.2 + ("Non-Conformant User Agent Implications") for further discussion. + +11.2. HSTS Policy Expiration Time Considerations + + Server implementations and deploying web sites need to consider + whether they are setting an expiry time that is a constant value into + the future, or whether they are setting an expiry time that is a + fixed point in time. + + The "constant value into the future" approach can be accomplished by + constantly sending the same max-age value to UAs. + + For example, a max-age value of 7776000 seconds is 90 days: + + Strict-Transport-Security: max-age=7776000 + + Note that each receipt of this header by a UA will require the UA to + update its notion of when it must delete its knowledge of this Known + HSTS Host. + + The "fixed point in time" approach can be accomplished by sending + max-age values that represent the remaining time until the desired + expiry time. This would require the HSTS Host to send a newly + calculated max-age value in each HTTP response. + + A consideration here is whether a deployer wishes to have the + signaled HSTS Policy expiry time match that for the web site's domain + certificate. + + Additionally, server implementers should consider employing a default + max-age value of zero in their deployment configuration systems. + This will require deployers to willfully set max-age in order to have + UAs enforce the HSTS Policy for their host and will protect them from + inadvertently enabling HSTS with some arbitrary non-zero duration. + + + + + + + + +Hodges, et al. Standards Track [Page 26] + +RFC 6797 HTTP Strict Transport Security (HSTS) November 2012 + + +11.3. Using HSTS in Conjunction with Self-Signed Public-Key + Certificates + + If all four of the following conditions are true... + + o a web site/organization/enterprise is generating its own secure + transport public-key certificates for web sites, and + + o that organization's root certification authority (CA) certificate + is not typically embedded by default in browser and/or operating + system CA certificate stores, and + + o HSTS Policy is enabled on a host identifying itself using a + certificate signed by the organization's CA (i.e., a "self-signed + certificate"), and + + o this certificate does not match a usable TLS certificate + association (as defined by Section 4 of the TLSA protocol + specification [RFC6698]), + + ...then secure connections to that site will fail, per the HSTS + design. This is to protect against various active attacks, as + discussed above. + + However, if said organization wishes to employ its own CA, and self- + signed certificates, in concert with HSTS, it can do so by deploying + its root CA certificate to its users' browsers or operating system CA + root certificate stores. It can also, in addition or instead, + distribute to its users' browsers the end-entity certificate(s) for + specific hosts. There are various ways in which this can be + accomplished (details are out of scope for this specification). Once + its root CA certificate is installed in the browsers, it may employ + HSTS Policy on its site(s). + + Alternatively, that organization can deploy the TLSA protocol; all + browsers that also use TLSA will then be able to trust the + certificates identified by usable TLS certificate associations as + denoted via TLSA. + + NOTE: Interactively distributing root CA certificates to users, + e.g., via email, and having the users install them, is + arguably training the users to be susceptible to a possible + form of phishing attack. See Section 14.8 ("Bogus Root CA + Certificate Phish plus DNS Cache Poisoning Attack"). Thus, + care should be taken in the manner in which such certificates + are distributed and installed on users' systems and browsers. + + + + + +Hodges, et al. Standards Track [Page 27] + +RFC 6797 HTTP Strict Transport Security (HSTS) November 2012 + + +11.4. Implications of includeSubDomains + + The includeSubDomains directive has practical implications meriting + careful consideration; two example scenarios are: + + o An HSTS Host offers unsecured HTTP-based services on alternate + ports or at various subdomains of its HSTS Host domain name. + + o Distinct web applications are offered at distinct subdomains of an + HSTS Host, such that UAs often interact directly with these + subdomain web applications without having necessarily interacted + with a web application offered at the HSTS Host's domain name (if + any). + + The sections below discuss each of these scenarios in turn. + +11.4.1. Considerations for Offering Unsecured HTTP Services at + Alternate Ports or Subdomains of an HSTS Host + + For example, certification authorities often offer their CRL + distribution and OCSP services [RFC2560] over plain HTTP, and + sometimes at a subdomain of a publicly available web application that + may be secured by TLS/SSL. For example, is + a publicly available web application for "Example CA", a + certification authority. Customers use this web application to + register their public keys and obtain certificates. "Example CA" + generates certificates for customers containing + as the value for the "CRL + Distribution Points" and "Authority Information Access:OCSP" + certificate fields. + + If ca.example.com were to issue an HSTS Policy with the + includeSubDomains directive, then HTTP-based user agents implementing + HSTS that have interacted with the ca.example.com web application + would fail to retrieve CRLs and fail to check OCSP for certificates, + because these services are offered over plain HTTP. + + In this case, Example CA can either: + + o not use the includeSubDomains directive, or + + o ensure that HTTP-based services offered at subdomains of + ca.example.com are also uniformly offered over TLS/SSL, or + + + + + + + + +Hodges, et al. Standards Track [Page 28] + +RFC 6797 HTTP Strict Transport Security (HSTS) November 2012 + + + o offer plain HTTP-based services at a different domain name, e.g., + crl-and-ocsp.ca.example.NET, or + + o utilize an alternative approach to distributing certificate status + information, obviating the need to offer CRL distribution and OCSP + services over plain HTTP (e.g., the "Certificate Status Request" + TLS extension [RFC6066], often colloquially referred to as "OCSP + Stapling"). + + NOTE: The above points are expressly only an example and do not + purport to address all the involved complexities. For + instance, there are many considerations -- on the part of CAs, + certificate deployers, and applications (e.g., browsers) -- + involved in deploying an approach such as "OCSP Stapling". + Such issues are out of scope for this specification. + +11.4.2. Considerations for Offering Web Applications at Subdomains of + an HSTS Host + + In this scenario, an HSTS Host declares an HSTS Policy with an + includeSubDomains directive, and there also exist distinct web + applications offered at distinct subdomains of the HSTS Host such + that UAs often interact directly with these subdomain web + applications without having necessarily interacted with the HSTS + Host. In such a case, the UAs will not receive or enforce the HSTS + Policy. + + For example, the HSTS Host is "example.com", and it is configured to + emit the STS header field with the includeSubDomains directive. + However, example.com's actual web application is addressed at + "www.example.com", and example.com simply redirects user agents to + "https://www.example.com/". + + If the STS header field is only emitted by "example.com" but UAs + typically bookmark -- and links (from anywhere on the web) are + typically established to -- "www.example.com", and "example.com" is + not contacted directly by all user agents in some non-zero percentage + of interactions, then some number of UAs will not note "example.com" + as an HSTS Host, and some number of users of "www.example.com" will + be unprotected by HSTS Policy. + + To address this, HSTS Hosts should be configured such that the STS + header field is emitted directly at each HSTS Host domain or + subdomain name that constitutes a well-known "entry point" to one's + web application(s), whether or not the includeSubDomains directive is + employed. + + + + + +Hodges, et al. Standards Track [Page 29] + +RFC 6797 HTTP Strict Transport Security (HSTS) November 2012 + + + Thus, in our example, if the STS header field is emitted from both + "example.com" and "www.example.com", this issue will be addressed. + Also, if there are any other well-known entry points to web + applications offered by "example.com", such as "foo.example.com", + they should also be configured to emit the STS header field. + +12. User Agent Implementation Advice + + This section is non-normative. + + In order to provide users and web sites more effective protection, as + well as controls for managing their UA's caching of HSTS Policy, UA + implementers should consider including features such as the + following: + +12.1. No User Recourse + + Failing secure connection establishment on any warnings or errors + (per Section 8.4 ("Errors in Secure Transport Establishment")) should + be done with "no user recourse". This means that the user should not + be presented with a dialog giving her the option to proceed. Rather, + it should be treated similarly to a server error where there is + nothing further the user can do with respect to interacting with the + target web application, other than wait and retry. + + Essentially, "any warnings or errors" means anything that would cause + the UA implementation to announce to the user that something is not + entirely correct with the connection establishment. + + Not doing this, i.e., allowing user recourse such as "clicking + through warning/error dialogs", is a recipe for a man-in-the-middle + attack. If a web application issues an HSTS Policy, then it is + implicitly opting into the "no user recourse" approach, whereby all + certificate errors or warnings cause a connection termination, with + no chance to "fool" users into making the wrong decision and + compromising themselves. + +12.2. User-Declared HSTS Policy + + A user-declared HSTS Policy is the ability for users to explicitly + declare a given domain name as representing an HSTS Host, thus + seeding it as a Known HSTS Host before any actual interaction with + it. This would help protect against the bootstrap MITM vulnerability + as discussed in Section 14.6 ("Bootstrap MITM Vulnerability"). + + + + + + + +Hodges, et al. Standards Track [Page 30] + +RFC 6797 HTTP Strict Transport Security (HSTS) November 2012 + + + NOTE: Such a feature is difficult to get right on a per-site basis. + See the discussion of "rewrite rules" in Section 5.5 of + [ForceHTTPS]. For example, arbitrary web sites may not + materialize all their URIs using the "https" scheme and thus + could "break" if a UA were to attempt to access the site + exclusively using such URIs. Also note that this feature + would complement, but is independent of, an "HSTS pre-loaded + list" feature (see Section 12.3). + +12.3. HSTS Pre-Loaded List + + An HSTS pre-loaded list is a facility whereby web site administrators + can have UAs pre-configured with HSTS Policy for their site(s) by the + UA vendor(s) -- a so-called "pre-loaded list" -- in a manner similar + to how root CA certificates are embedded in browsers "at the + factory". This would help protect against the bootstrap MITM + vulnerability (Section 14.6). + + NOTE: Such a facility would complement a "user-declared HSTS Policy" + feature (Section 12.2). + +12.4. Disallow Mixed Security Context Loads + + "Mixed security context" loads happen when a web application + resource, fetched by the UA over a secure transport, subsequently + causes the fetching of one or more other resources without using + secure transport. This is also generally referred to as "mixed + content" loads (see Section 5.3 ("Mixed Content") in + [W3C.REC-wsc-ui-20100812]) but should not be confused with the same + "mixed content" term that is also used in the context of markup + languages such as XML and HTML. + + NOTE: In order to provide behavioral uniformity across UA + implementations, the notion of mixed security context will + require further standardization work, e.g., to define the + term(s) more clearly and to define specific behaviors with + respect to it. + +12.5. HSTS Policy Deletion + + HSTS Policy deletion is the ability to delete a UA's cached HSTS + Policy on a per-HSTS Host basis. + + NOTE: Adding such a feature should be done very carefully in both + the user interface and security senses. Deleting a cache + entry for a Known HSTS Host should be a very deliberate and + well-considered act -- it shouldn't be something that users + get used to doing as a matter of course: e.g., just "clicking + + + +Hodges, et al. Standards Track [Page 31] + +RFC 6797 HTTP Strict Transport Security (HSTS) November 2012 + + + through" in order to get work done. Also, implementations + need to guard against allowing an attacker to inject code, + e.g., ECMAscript, into the UA that silently and + programmatically removes entries from the UA's cache of Known + HSTS Hosts. + +13. Internationalized Domain Names for Applications (IDNA): Dependency + and Migration + + Textual domain names on the modern Internet may contain one or more + "internationalized" domain name labels. Such domain names are + referred to as "internationalized domain names" (IDNs). The + specification suites defining IDNs and the protocols for their use + are named "Internationalized Domain Names for Applications (IDNA)". + At this time, there are two such specification suites: IDNA2008 + [RFC5890] and its predecessor IDNA2003 [RFC3490]. + + IDNA2008 obsoletes IDNA2003, but there are differences between the + two specifications, and thus there can be differences in processing + (e.g., converting) domain name labels that have been registered under + one from those registered under the other. There will be a + transition period of some time during which IDNA2003-based domain + name labels will exist in the wild. In order to facilitate their + IDNA transition, user agents SHOULD implement IDNA2008 [RFC5890] and + MAY implement [RFC5895] (see also Section 7 of [RFC5894]) or [UTS46]. + If a user agent does not implement IDNA2008, the user agent MUST + implement IDNA2003. + +14. Security Considerations + + This specification concerns the expression, conveyance, and + enforcement of the HSTS Policy. The overall HSTS Policy threat + model, including addressed and unaddressed threats, is given in + Section 2.3 ("Threat Model"). + + Additionally, the sections below discuss operational ramifications of + the HSTS Policy, provide feature rationale, discuss potential HSTS + Policy misuse, and highlight some known vulnerabilities in the HSTS + Policy regime. + +14.1. Underlying Secure Transport Considerations + + This specification is fashioned to be independent of the secure + transport underlying HTTP. However, the threat analysis and + requirements in Section 2 ("Overview") in fact presume TLS or SSL as + the underlying secure transport. Thus, employment of HSTS in the + context of HTTP running over some other secure transport protocol + would require assessment of that secure transport protocol's security + + + +Hodges, et al. Standards Track [Page 32] + +RFC 6797 HTTP Strict Transport Security (HSTS) November 2012 + + + model in conjunction with the specifics of how HTTP is layered over + it in order to assess HSTS's subsequent security properties in that + context. + +14.2. Non-Conformant User Agent Implications + + Non-conformant user agents ignore the Strict-Transport-Security + header field; thus, non-conformant user agents do not address the + threats described in Section 2.3.1 ("Threats Addressed"). + + This means that the web application and its users wielding + non-conformant UAs will be vulnerable to both of the following: + + o Passive network attacks due to web site development and deployment + bugs: + + For example, if the web application contains any insecure + references (e.g., "http") to the web application server, and if + not all of its cookies are flagged as "Secure", then its + cookies will be vulnerable to passive network sniffing and, + potentially, subsequent misuse of user credentials. + + o Active network attacks: + + For example, if an attacker is able to place a "man in the + middle", secure transport connection attempts will likely yield + warnings to the user, but without HSTS Policy being enforced, + the present common practice is to allow the user to "click + through" and proceed. This renders the user and possibly the + web application open to abuse by such an attacker. + + This is essentially the status quo for all web applications and their + users in the absence of HSTS Policy. Since web application providers + typically do not control the type or version of UAs their web + applications interact with, the implication is that HSTS Host + deployers must generally exercise the same level of care to avoid web + site development and deployment bugs (see Section 2.3.1.3) as they + would if they were not asserting HSTS Policy. + +14.3. Ramifications of HSTS Policy Establishment Only over Error-Free + Secure Transport + + The user agent processing model defined in Section 8 ("User Agent + Processing Model") stipulates that a host is initially noted as a + Known HSTS Host, or that updates are made to a Known HSTS Host's + cached information, only if the UA receives the STS header field over + a secure transport connection having no underlying secure transport + errors or warnings. + + + +Hodges, et al. Standards Track [Page 33] + +RFC 6797 HTTP Strict Transport Security (HSTS) November 2012 + + + The rationale behind this is that if there is a "man in the middle" + (MITM) -- whether a legitimately deployed proxy or an illegitimate + entity -- it could cause various mischief (see also Appendix A + ("Design Decision Notes") item 3, as well as Section 14.6 ("Bootstrap + MITM Vulnerability")); for example: + + o Unauthorized notation of the host as a Known HSTS Host, + potentially leading to a denial-of-service situation if the host + does not uniformly offer its services over secure transport (see + also Section 14.5 ("Denial of Service")). + + o Resetting the time to live for the host's designation as a Known + HSTS Host by manipulating the max-age header field parameter value + that is returned to the UA. If max-age is returned as zero, this + will cause the host to cease being regarded as a Known HSTS Host + by the UA, leading to either insecure connections to the host or + possibly denial of service if the host delivers its services only + over secure transport. + + However, this means that if a UA is "behind" a MITM non-transparent + TLS proxy -- within a corporate intranet, for example -- and + interacts with an unknown HSTS Host beyond the proxy, the user could + possibly be presented with the legacy secure connection error + dialogs. Even if the risk is accepted and the user "clicks through", + the host will not be noted as an HSTS Host. Thus, as long as the UA + is behind such a proxy, the user will be vulnerable and will possibly + be presented with the legacy secure connection error dialogs for + as-yet unknown HSTS Hosts. + + Once the UA successfully connects to an unknown HSTS Host over error- + free secure transport, the host will be noted as a Known HSTS Host. + This will result in the failure of subsequent connection attempts + from behind interfering proxies. + + The above discussion relates to the recommendation in Section 12 + ("User Agent Implementation Advice") that the secure connection be + terminated with "no user recourse" whenever there are warnings and + errors and the host is a Known HSTS Host. Such a posture protects + users from "clicking through" security warnings and putting + themselves at risk. + +14.4. The Need for includeSubDomains + + Without the includeSubDomains directive, a web application would not + be able to adequately protect so-called "domain cookies" (even if + these cookies have their "Secure" flag set and thus are conveyed only + on secure channels). These are cookies the web application expects + UAs to return to any and all subdomains of the web application. + + + +Hodges, et al. Standards Track [Page 34] + +RFC 6797 HTTP Strict Transport Security (HSTS) November 2012 + + + For example, suppose example.com represents the top-level DNS name + for a web application. Further suppose that this cookie is set for + the entire example.com domain, i.e., it is a "domain cookie", and it + has its Secure flag set. Suppose example.com is a Known HSTS Host + for this UA, but the includeSubDomains directive is not set. + + Now, if an attacker causes the UA to request a subdomain name that is + unlikely to already exist in the web application, such as + "https://uxdhbpahpdsf.example.com/", but that the attacker has + managed to register in the DNS and point at an HTTP server under the + attacker's control, then: + + 1. The UA is unlikely to already have an HSTS Policy established for + "uxdhbpahpdsf.example.com". + + 2. The HTTP request sent to uxdhbpahpdsf.example.com will include + the Secure-flagged domain cookie. + + 3. If "uxdhbpahpdsf.example.com" returns a certificate during TLS + establishment, and the user "clicks through" any warning that + might be presented (it is possible, but not certain, that one may + obtain a requisite certificate for such a domain name such that a + warning may or may not appear), then the attacker can obtain the + Secure-flagged domain cookie that's ostensibly being protected. + + Without the "includeSubDomains" directive, HSTS is unable to protect + such Secure-flagged domain cookies. + +14.5. Denial of Service + + HSTS could be used to mount certain forms of Denial-of-Service (DoS) + attacks against web sites. A DoS attack is an attack in which one or + more network entities target a victim entity and attempt to prevent + the victim from doing useful work. This section discusses such + scenarios in terms of HSTS, though this list is not exhaustive. See + also [RFC4732] for a discussion of overall Internet DoS + considerations. + + o Web applications available over HTTP + + There is an opportunity for perpetrating DoS attacks with web + applications (or critical portions of them) that are available + only over HTTP without secure transport, if attackers can cause + UAs to set HSTS Policy for such web applications' host(s). + + This is because once the HSTS Policy is set for a web + application's host in a UA, the UA will only use secure transport + to communicate with the host. If the host is not using secure + + + +Hodges, et al. Standards Track [Page 35] + +RFC 6797 HTTP Strict Transport Security (HSTS) November 2012 + + + transport or is not using it for critical portions of its web + application, then the web application will be rendered unusable + for the UA's user. + + NOTE: This is a use case for UAs to offer an "HSTS Policy + deletion" feature as noted in Section 12.5 ("HSTS Policy + Deletion"). + + An HSTS Policy can be set for a victim host in various ways: + + * If the web application has an HTTP response splitting + vulnerability [CWE-113] (which can be abused in order to + facilitate "HTTP header injection"). + + * If an attacker can spoof a redirect from an insecure victim + site, e.g., to , + where the latter is attacker-controlled and has an apparently + valid certificate. In this situation, the attacker can then + set an HSTS Policy for example.com and also for all subdomains + of example.com. + + * If an attacker can convince users to manually configure HSTS + Policy for a victim host. This assumes that their UAs offer + such a capability (see Section 12 ("User Agent Implementation + Advice")). Alternatively, if such UA configuration is + scriptable, then an attacker can cause UAs to execute his + script and set HSTS Policies for whichever desired domains. + + o Inadvertent use of includeSubDomains + + The includeSubDomains directive instructs UAs to automatically + regard all subdomains of the given HSTS Host as Known HSTS Hosts. + If any such subdomains do not support properly configured secure + transport, then they will be rendered unreachable from such UAs. + +14.6. Bootstrap MITM Vulnerability + + Bootstrap MITM (man-in-the-middle) vulnerability is a vulnerability + that users and HSTS Hosts encounter in the situation where the user + manually enters, or follows a link, to an unknown HSTS Host using an + "http" URI rather than an "https" URI. Because the UA uses an + insecure channel in the initial attempt to interact with the + specified server, such an initial interaction is vulnerable to + various attacks (see Section 5.3 of [ForceHTTPS]). + + NOTE: There are various features/facilities that UA implementations + may employ in order to mitigate this vulnerability. Please + see Section 12 ("User Agent Implementation Advice"). + + + +Hodges, et al. Standards Track [Page 36] + +RFC 6797 HTTP Strict Transport Security (HSTS) November 2012 + + +14.7. Network Time Attacks + + Active network attacks can subvert network time protocols (such as + the Network Time Protocol (NTP) [RFC5905]) -- making HSTS less + effective against clients that trust NTP or lack a real time clock. + Network time attacks are beyond the scope of this specification. + Note that modern operating systems use NTP by default. See also + Section 2.10 of [RFC4732]. + +14.8. Bogus Root CA Certificate Phish plus DNS Cache Poisoning Attack + + An attacker could conceivably obtain users' login credentials + belonging to a victim HSTS-protected web application via a bogus root + CA certificate phish plus DNS cache poisoning attack. + + For example, the attacker could first convince users of a victim web + application (which is protected by HSTS Policy) to install the + attacker's version of a root CA certificate purporting (falsely) to + represent the CA of the victim web application. This might be + accomplished by sending the users a phishing email message with a + link to such a certificate, which their browsers may offer to install + if clicked on. + + Then, if the attacker can perform an attack on the users' DNS + servers, (e.g., via cache poisoning) and turn on HSTS Policy for + their fake web application, the affected users' browsers would access + the attacker's web application rather than the legitimate web + application. + + This type of attack leverages vectors that are outside of the scope + of HSTS. However, the feasibility of such threats can be mitigated + by including in a web application's overall deployment approach + appropriate employment, in addition to HSTS, of security facilities + such as DNS Security Extensions [RFC4033], plus techniques to block + email phishing and fake certificate injection. + +14.9. Creative Manipulation of HSTS Policy Store + + Since an HSTS Host may select its own host name and subdomains + thereof, and this information is cached in the HSTS Policy store of + conforming UAs, it is possible for those who control one or more HSTS + Hosts to encode information into domain names they control and cause + such UAs to cache this information as a matter of course in the + process of noting the HSTS Host. This information can be retrieved + by other hosts through cleverly constructed and loaded web resources, + causing the UA to send queries to (variations of) the encoded domain + names. Such queries can reveal whether the UA had previously visited + the original HSTS Host (and subdomains). + + + +Hodges, et al. Standards Track [Page 37] + +RFC 6797 HTTP Strict Transport Security (HSTS) November 2012 + + + Such a technique could potentially be abused as yet another form of + "web tracking" [WebTracking]. + +14.10. Internationalized Domain Names + + Internet security relies in part on the DNS and the domain names it + hosts. Domain names are used by users to identify and connect to + Internet hosts and other network resources. For example, Internet + security is compromised if a user entering an internationalized + domain name (IDN) is connected to different hosts based on different + interpretations of the IDN. + + The processing models specified in this specification assume that the + domain names they manipulate are IDNA-canonicalized, and that the + canonicalization process correctly performed all appropriate IDNA and + Unicode validations and character list testing per the requisite + specifications (e.g., as noted in Section 10 ("Domain Name IDNA- + Canonicalization")). These steps are necessary in order to avoid + various potentially compromising situations. + + In brief, examples of issues that could stem from lack of careful and + consistent Unicode and IDNA validations include unexpected processing + exceptions, truncation errors, and buffer overflows, as well as + false-positive and/or false-negative domain name matching results. + Any of the foregoing issues could possibly be leveraged by attackers + in various ways. + + Additionally, IDNA2008 [RFC5890] differs from IDNA2003 [RFC3490] in + terms of disallowed characters and character mapping conventions. + This situation can also lead to false-positive and/or false-negative + domain name matching results, resulting in, for example, users + possibly communicating with unintended hosts or not being able to + reach intended hosts. + + For details, refer to the Security Considerations sections of + [RFC5890], [RFC5891], and [RFC3490], as well as the specifications + they normatively reference. Additionally, [RFC5894] provides + detailed background and rationale for IDNA2008 in particular, as well + as IDNA and its issues in general, and should be consulted in + conjunction with the former specifications. + + + + + + + + + + + +Hodges, et al. Standards Track [Page 38] + +RFC 6797 HTTP Strict Transport Security (HSTS) November 2012 + + +15. IANA Considerations + + Below is the Internet Assigned Numbers Authority (IANA) Permanent + Message Header Field registration information per [RFC3864]. + + Header field name: Strict-Transport-Security + Applicable protocol: http + Status: standard + Author/Change controller: IETF + Specification document(s): this one + +16. References + +16.1. Normative References + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC2616] Fielding, R., Gettys, J., Mogul, J., Frystyk, H., + Masinter, L., Leach, P., and T. Berners-Lee, "Hypertext + Transfer Protocol -- HTTP/1.1", RFC 2616, June 1999. + + [RFC2818] Rescorla, E., "HTTP Over TLS", RFC 2818, May 2000. + + [RFC3490] Faltstrom, P., Hoffman, P., and A. Costello, + "Internationalizing Domain Names in Applications (IDNA)", + RFC 3490, March 2003. + + [RFC3864] Klyne, G., Nottingham, M., and J. Mogul, "Registration + Procedures for Message Header Fields", BCP 90, RFC 3864, + September 2004. + + [RFC3986] Berners-Lee, T., Fielding, R., and L. Masinter, "Uniform + Resource Identifier (URI): Generic Syntax", STD 66, + RFC 3986, January 2005. + + [RFC5246] Dierks, T. and E. Rescorla, "The Transport Layer Security + (TLS) Protocol Version 1.2", RFC 5246, August 2008. + + [RFC5890] Klensin, J., "Internationalized Domain Names for + Applications (IDNA): Definitions and Document Framework", + RFC 5890, August 2010. + + [RFC5891] Klensin, J., "Internationalized Domain Names in + Applications (IDNA): Protocol", RFC 5891, August 2010. + + + + + + +Hodges, et al. Standards Track [Page 39] + +RFC 6797 HTTP Strict Transport Security (HSTS) November 2012 + + + [RFC5895] Resnick, P. and P. Hoffman, "Mapping Characters for + Internationalized Domain Names in Applications + (IDNA) 2008", RFC 5895, September 2010. + + [RFC6698] Hoffman, P. and J. Schlyter, "The DNS-Based Authentication + of Named Entities (DANE) Transport Layer Security (TLS) + Protocol: TLSA", RFC 6698, August 2012. + + [UTS46] Davis, M. and M. Suignard, "Unicode IDNA Compatibility + Processing", Unicode Technical Standard #46, + . + + [Unicode] The Unicode Consortium, "The Unicode Standard", + . + + [W3C.REC-html401-19991224] + Raggett, D., Le Hors, A., and I. Jacobs, "HTML 4.01 + Specification", World Wide Web Consortium Recommendation + REC-html401-19991224, December 1999, + . + +16.2. Informative References + + [Aircrack-ng] + d'Otreppe, T., "Aircrack-ng", Accessed: 11-Jul-2010, + . + + [BeckTews09] + Beck, M. and E. Tews, "Practical Attacks Against WEP and + WPA", Second ACM Conference on Wireless Network + Security Zurich, Switzerland, 2009, + . + + [CWE-113] "CWE-113: Improper Neutralization of CRLF Sequences in + HTTP Headers ('HTTP Response Splitting')", Common Weakness + Enumeration , The Mitre + Corporation , + . + + [Firesheep] + Various, "Firesheep", Wikipedia Online, ongoing, . + + + + + + + + +Hodges, et al. Standards Track [Page 40] + +RFC 6797 HTTP Strict Transport Security (HSTS) November 2012 + + + [ForceHTTPS] + Jackson, C. and A. Barth, "ForceHTTPS: Protecting High- + Security Web Sites from Network Attacks", In Proceedings + of the 17th International World Wide Web Conference + (WWW2008) , 2008, + . + + [GoodDhamijaEtAl05] + Good, N., Dhamija, R., Grossklags, J., Thaw, D., + Aronowitz, S., Mulligan, D., and J. Konstan, "Stopping + Spyware at the Gate: A User Study of Privacy, Notice and + Spyware", In Proceedings of Symposium On Usable Privacy + and Security (SOUPS) Pittsburgh, PA, USA, July 2005, + . + + [HTTP1_1-UPD] + Fielding, R., Ed., and J. Reschke, Ed., "Hypertext + Transfer Protocol (HTTP/1.1): Message Syntax and Routing", + Work in Progress, October 2012. + + [JacksonBarth2008] + Jackson, C. and A. Barth, "Beware of Finer-Grained + Origins", Web 2.0 Security and Privacy Workshop, Oakland, + CA, USA, 2008, + . + + [OWASP-TLSGuide] + Coates, M., Wichers, D., Boberski, M., and T. Reguly, + "Transport Layer Protection Cheat Sheet", + Accessed: 11-Jul-2010, . + + [RFC1035] Mockapetris, P., "Domain names - implementation and + specification", STD 13, RFC 1035, November 1987. + + [RFC2560] Myers, M., Ankney, R., Malpani, A., Galperin, S., and C. + Adams, "X.509 Internet Public Key Infrastructure Online + Certificate Status Protocol - OCSP", RFC 2560, June 1999. + + [RFC4033] Arends, R., Austein, R., Larson, M., Massey, D., and S. + Rose, "DNS Security Introduction and Requirements", + RFC 4033, March 2005. + + [RFC4732] Handley, M., Rescorla, E., and IAB, "Internet Denial-of- + Service Considerations", RFC 4732, December 2006. + + + + + +Hodges, et al. Standards Track [Page 41] + +RFC 6797 HTTP Strict Transport Security (HSTS) November 2012 + + + [RFC4949] Shirey, R., "Internet Security Glossary, Version 2", + RFC 4949, August 2007. + + [RFC5226] Narten, T. and H. Alvestrand, "Guidelines for Writing an + IANA Considerations Section in RFCs", BCP 26, RFC 5226, + May 2008. + + [RFC5280] Cooper, D., Santesson, S., Farrell, S., Boeyen, S., + Housley, R., and W. Polk, "Internet X.509 Public Key + Infrastructure Certificate and Certificate Revocation List + (CRL) Profile", RFC 5280, May 2008. + + [RFC5894] Klensin, J., "Internationalized Domain Names for + Applications (IDNA): Background, Explanation, and + Rationale", RFC 5894, August 2010. + + [RFC5905] Mills, D., Martin, J., Burbank, J., and W. Kasch, "Network + Time Protocol Version 4: Protocol and Algorithms + Specification", RFC 5905, June 2010. + + [RFC6066] Eastlake, D., "Transport Layer Security (TLS) Extensions: + Extension Definitions", RFC 6066, January 2011. + + [RFC6101] Freier, A., Karlton, P., and P. Kocher, "The Secure + Sockets Layer (SSL) Protocol Version 3.0", RFC 6101, + August 2011. + + [RFC6125] Saint-Andre, P. and J. Hodges, "Representation and + Verification of Domain-Based Application Service Identity + within Internet Public Key Infrastructure Using X.509 + (PKIX) Certificates in the Context of Transport Layer + Security (TLS)", RFC 6125, March 2011. + + [RFC6265] Barth, A., "HTTP State Management Mechanism", RFC 6265, + April 2011. + + [RFC6454] Barth, A., "The Web Origin Concept", RFC 6454, + December 2011. + + [SunshineEgelmanEtAl09] + Sunshine, J., Egelman, S., Almuhimedi, H., Atri, N., and + L. Cranor, "Crying Wolf: An Empirical Study of SSL Warning + Effectiveness", In Proceedings of 18th USENIX Security + Symposium Montreal, Canada, August 2009, . + + + + + +Hodges, et al. Standards Track [Page 42] + +RFC 6797 HTTP Strict Transport Security (HSTS) November 2012 + + + [W3C.REC-wsc-ui-20100812] + Roessler, T. and A. Saldhana, "Web Security Context: User + Interface Guidelines", World Wide Web Consortium + Recommendation REC-wsc-ui-20100812, August 2010, + . + + [WebTracking] + Schmucker, N., "Web Tracking", SNET2 Seminar Paper + - Summer Term, 2011, . + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Hodges, et al. Standards Track [Page 43] + +RFC 6797 HTTP Strict Transport Security (HSTS) November 2012 + + +Appendix A. Design Decision Notes + + This appendix documents various design decisions. + + 1. Cookies aren't appropriate for HSTS Policy expression, as they + are potentially mutable (while stored in the UA); therefore, an + HTTP header field is employed. + + 2. We chose to not attempt to specify how "mixed security context + loads" (also known as "mixed content loads") are handled, due to + UA implementation considerations as well as classification + difficulties. + + 3. An HSTS Host may update UA notions of HSTS Policy via new HSTS + header field parameter values. We chose to have UAs honor the + "freshest" information received from a server because there is + the chance of a web site sending out an erroneous HSTS Policy, + such as a multi-year max-age value, and/or an incorrect + includeSubDomains directive. If the HSTS Host couldn't correct + such errors over protocol, it would require some form of + annunciation to users and manual intervention on the users' part, + which could be a non-trivial problem for both web application + providers and their users. + + 4. HSTS Hosts are identified only via domain names -- explicit IP + address identification of all forms is excluded. This is for + simplification and also is in recognition of various issues with + using direct IP address identification in concert with PKI-based + security. + + 5. The max-age approach of having the HSTS Host provide a simple + integer number of seconds for a cached HSTS Policy time-to-live + value, as opposed to an approach of stating an expiration time in + the future, was chosen for various reasons. Amongst the reasons + are no need for clock synchronization, no need to define date and + time value syntaxes (specification simplicity), and + implementation simplicity. + + 6. In determining whether port mapping was to be employed, the + option of merely refusing to dereference any URL with an explicit + port was considered. It was felt, though, that the possibility + that the URI to be dereferenced is incorrect (and there is indeed + a valid HTTPS server at that port) is worth the small cost of + possibly wasted HTTPS fetches to HTTP servers. + + + + + + + +Hodges, et al. Standards Track [Page 44] + +RFC 6797 HTTP Strict Transport Security (HSTS) November 2012 + + +Appendix B. Differences between HSTS Policy and Same-Origin Policy + + HSTS Policy has the following primary characteristics: + + HSTS Policy stipulates requirements for the security + characteristics of UA-to-host connection establishment, on a + per-host basis. + + Hosts explicitly declare HSTS Policy to UAs. Conformant UAs are + obliged to implement hosts' declared HSTS Policies. + + HSTS Policy is conveyed over protocol from the host to the UA. + + The UA maintains a cache of Known HSTS Hosts. + + UAs apply HSTS Policy whenever making an HTTP connection to a + Known HSTS Host, regardless of host port number; i.e., it applies + to all ports on a Known HSTS Host. Hosts are unable to affect + this aspect of HSTS Policy. + + Hosts may optionally declare that their HSTS Policy applies to all + subdomains of their host domain name. + + In contrast, the Same-Origin Policy (SOP) [RFC6454] has the following + primary characteristics: + + An origin is the scheme, host, and port of a URI identifying a + resource. + + A UA may dereference a URI, thus loading a representation of the + resource the URI identifies. UAs label resource representations + with their origins, which are derived from their URIs. + + The SOP refers to a collection of principles, implemented within + UAs, governing the isolation of and communication between resource + representations within the UA, as well as resource + representations' access to network resources. + + In summary, although both HSTS Policy and SOP are enforced by UAs, + HSTS Policy is optionally declared by hosts and is not origin-based, + while the SOP applies to all resource representations loaded from all + hosts by conformant UAs. + + + + + + + + + +Hodges, et al. Standards Track [Page 45] + +RFC 6797 HTTP Strict Transport Security (HSTS) November 2012 + + +Appendix C. Acknowledgments + + The authors thank Devdatta Akhawe, Michael Barrett, Ben Campbell, + Tobias Gondrom, Paul Hoffman, Murray Kucherawy, Barry Leiba, James + Manger, Alexey Melnikov, Haevard Molland, Yoav Nir, Yngve N. + Pettersen, Laksh Raghavan, Marsh Ray, Julian Reschke, Eric Rescorla, + Tom Ritter, Peter Saint-Andre, Brian Smith, Robert Sparks, Maciej + Stachowiak, Sid Stamm, Andy Steingrubl, Brandon Sterne, Martin + Thomson, Daniel Veditz, and Jan Wrobel, as well as all the websec + working group participants and others for their various reviews and + helpful contributions. + + Thanks to Julian Reschke for his elegant rewriting of the effective + request URI text, which he did when incorporating the ERU notion into + the updates to HTTP/1.1 [HTTP1_1-UPD]. Subsequently, the ERU text in + this spec was lifted from Julian's work in the updated HTTP/1.1 + (part 1) specification and adapted to the [RFC2616] ABNF. + +Authors' Addresses + + Jeff Hodges + PayPal + 2211 North First Street + San Jose, California 95131 + US + + EMail: Jeff.Hodges@PayPal.com + + + Collin Jackson + Carnegie Mellon University + + EMail: collin.jackson@sv.cmu.edu + + + Adam Barth + Google, Inc. + + EMail: ietf@adambarth.com + URI: http://www.adambarth.com/ + + + + + + + + + + + +Hodges, et al. Standards Track [Page 46] + diff --git a/libsoup/Makefile.am b/libsoup/Makefile.am index 4a0db8e2..cead4d18 100644 --- a/libsoup/Makefile.am +++ b/libsoup/Makefile.am @@ -47,6 +47,9 @@ soup_headers = \ soup-date.h \ soup-form.h \ soup-headers.h \ + soup-hsts-enforcer.h \ + soup-hsts-enforcer-db.h \ + soup-hsts-policy.h \ soup-logger.h \ soup-message.h \ soup-message-body.h \ @@ -158,6 +161,10 @@ libsoup_2_4_la_SOURCES = \ soup-filter-input-stream.h \ soup-form.c \ soup-headers.c \ + soup-hsts-enforcer.c \ + soup-hsts-enforcer-db.c \ + soup-hsts-enforcer-private.h \ + soup-hsts-policy.c \ soup-init.c \ soup-io-stream.h \ soup-io-stream.c \ diff --git a/libsoup/soup-hsts-enforcer-db.c b/libsoup/soup-hsts-enforcer-db.c new file mode 100644 index 00000000..319f118d --- /dev/null +++ b/libsoup/soup-hsts-enforcer-db.c @@ -0,0 +1,314 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * soup-hsts-enforcer-db.c: database-based HSTS policy storage + * + * Using soup-cookie-jar-db as template + * Copyright (C) 2016 Igalia S.L. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include + +#include "soup-hsts-enforcer-db.h" +#include "soup-hsts-enforcer-private.h" +#include "soup.h" + +/** + * SECTION:soup-hsts-enforcer-db + * @short_description: Database-based HSTS Enforcer + * + * #SoupHstsEnforcerDB is a #SoupHstsEnforcer that reads HSTS policies from + * and writes them to a sqlite database. + **/ + +enum { + PROP_0, + + PROP_FILENAME, + + LAST_PROP +}; + +typedef struct { + char *filename; + sqlite3 *db; +} SoupHstsEnforcerDBPrivate; + +#define SOUP_HSTS_ENFORCER_DB_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_HSTS_ENFORCER_DB, SoupHstsEnforcerDBPrivate)) + +G_DEFINE_TYPE (SoupHstsEnforcerDB, soup_hsts_enforcer_db, SOUP_TYPE_HSTS_ENFORCER) + +static void load (SoupHstsEnforcer *hsts_enforcer); + +static void +soup_hsts_enforcer_db_init (SoupHstsEnforcerDB *db) +{ +} + +static void +soup_hsts_enforcer_db_finalize (GObject *object) +{ + SoupHstsEnforcerDBPrivate *priv = + SOUP_HSTS_ENFORCER_DB_GET_PRIVATE (object); + + g_free (priv->filename); + g_clear_pointer (&priv->db, sqlite3_close); + + G_OBJECT_CLASS (soup_hsts_enforcer_db_parent_class)->finalize (object); +} + +static void +soup_hsts_enforcer_db_set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + SoupHstsEnforcerDBPrivate *priv = + SOUP_HSTS_ENFORCER_DB_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_FILENAME: + priv->filename = g_value_dup_string (value); + load (SOUP_HSTS_ENFORCER (object)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +soup_hsts_enforcer_db_get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + SoupHstsEnforcerDBPrivate *priv = + SOUP_HSTS_ENFORCER_DB_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_FILENAME: + g_value_set_string (value, priv->filename); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +/** + * soup_hsts_enforcer_db_new: + * @filename: the filename to read to/write from, or %NULL + * + * Creates a #SoupHstsEnforcerDB. + * + * @filename will be read in at startup to create an initial set of HSTS + * policies. Changes to the policies will be written to @filename when the + * 'changed' signal is emitted from the HSTS enforcer. + * + * Return value: the new #SoupHstsEnforcer + * + * Since: 2.54 + **/ +SoupHstsEnforcer * +soup_hsts_enforcer_db_new (const char *filename) +{ + g_return_val_if_fail (filename != NULL, NULL); + + return g_object_new (SOUP_TYPE_HSTS_ENFORCER_DB, + SOUP_HSTS_ENFORCER_DB_FILENAME, filename, + NULL); +} + +#define QUERY_ALL "SELECT id, host, expiry, includeSubDomains FROM soup_hsts_policies;" +#define CREATE_TABLE "CREATE TABLE soup_hsts_policies (id INTEGER PRIMARY KEY, host TEXT UNIQUE, expiry INTEGER, includeSubDomains INTEGER)" +#define QUERY_INSERT "INSERT OR REPLACE INTO soup_hsts_policies VALUES((SELECT id FROM soup_hsts_policies WHERE host=%Q), %Q, %d, %d);" +#define QUERY_DELETE "DELETE FROM soup_hsts_policies WHERE host=%Q;" + +enum { + COL_ID, + COL_HOST, + COL_EXPIRY, + COL_SUB_DOMAINS, + N_COL, +}; + +static int +callback (void *data, int argc, char **argv, char **colname) +{ + SoupHstsPolicy *policy = NULL; + SoupHstsEnforcer *hsts_enforcer = SOUP_HSTS_ENFORCER (data); + + char *host; + gulong expire_time; + time_t now; + SoupDate *expires; + gboolean include_sub_domains = FALSE; + + now = time (NULL); + + host = argv[COL_HOST]; + expire_time = strtoul (argv[COL_EXPIRY], NULL, 10); + + if (now >= expire_time) + return 0; + + expires = soup_date_new_from_time_t (expire_time); + include_sub_domains = (g_strcmp0 (argv[COL_SUB_DOMAINS], "1") == 0); + + policy = soup_hsts_policy_new (host, expires, include_sub_domains); + + if (policy) + soup_hsts_enforcer_set_policy (hsts_enforcer, policy); + else + soup_date_free (expires); + + return 0; +} + +static void +try_create_table (sqlite3 *db) +{ + char *error = NULL; + + if (sqlite3_exec (db, CREATE_TABLE, NULL, NULL, &error)) { + g_warning ("Failed to execute query: %s", error); + sqlite3_free (error); + } +} + +static void +exec_query_with_try_create_table (sqlite3 *db, + const char *sql, + int (*callback)(void*,int,char**,char**), + void *argument) +{ + char *error = NULL; + gboolean try_create = TRUE; + +try_exec: + if (sqlite3_exec (db, sql, callback, argument, &error)) { + if (try_create) { + try_create = FALSE; + try_create_table (db); + sqlite3_free (error); + error = NULL; + goto try_exec; + } else { + g_warning ("Failed to execute query: %s", error); + sqlite3_free (error); + } + } +} + +/* Follows sqlite3 convention; returns TRUE on error */ +static gboolean +open_db (SoupHstsEnforcer *hsts_enforcer) +{ + SoupHstsEnforcerDBPrivate *priv = + SOUP_HSTS_ENFORCER_DB_GET_PRIVATE (hsts_enforcer); + + char *error = NULL; + + if (sqlite3_open (priv->filename, &priv->db)) { + sqlite3_close (priv->db); + priv->db = NULL; + g_warning ("Can't open %s", priv->filename); + return TRUE; + } + + if (sqlite3_exec (priv->db, "PRAGMA synchronous = OFF; PRAGMA secure_delete = 1;", NULL, NULL, &error)) { + g_warning ("Failed to execute query: %s", error); + sqlite3_free (error); + } + + return FALSE; +} + +static void +load (SoupHstsEnforcer *hsts_enforcer) +{ + SoupHstsEnforcerDBPrivate *priv = + SOUP_HSTS_ENFORCER_DB_GET_PRIVATE (hsts_enforcer); + + if (priv->db == NULL) { + if (open_db (hsts_enforcer)) + return; + } + + exec_query_with_try_create_table (priv->db, QUERY_ALL, callback, hsts_enforcer); +} + +static void +soup_hsts_enforcer_db_changed (SoupHstsEnforcer *hsts_enforcer, + SoupHstsPolicy *old_policy, + SoupHstsPolicy *new_policy) +{ + SoupHstsEnforcerDBPrivate *priv = + SOUP_HSTS_ENFORCER_DB_GET_PRIVATE (hsts_enforcer); + char *query; + + if (priv->db == NULL) { + if (open_db (hsts_enforcer)) + return; + } + + if (old_policy && !new_policy) { + query = sqlite3_mprintf (QUERY_DELETE, + old_policy->domain); + exec_query_with_try_create_table (priv->db, query, NULL, NULL); + sqlite3_free (query); + } + + /* Insert the new policy or update the existing one. */ + if (new_policy && new_policy->expires) { + gulong expires; + + expires = (gulong)soup_date_to_time_t (new_policy->expires); + query = sqlite3_mprintf (QUERY_INSERT, + new_policy->domain, + new_policy->domain, + expires, + new_policy->include_sub_domains); + exec_query_with_try_create_table (priv->db, query, NULL, NULL); + sqlite3_free (query); + } +} + +static gboolean +soup_hsts_enforcer_db_is_persistent (SoupHstsEnforcer *hsts_enforcer) +{ + return TRUE; +} + +static void +soup_hsts_enforcer_db_class_init (SoupHstsEnforcerDBClass *db_class) +{ + SoupHstsEnforcerClass *hsts_enforcer_class = + SOUP_HSTS_ENFORCER_CLASS (db_class); + GObjectClass *object_class = G_OBJECT_CLASS (db_class); + + g_type_class_add_private (db_class, sizeof (SoupHstsEnforcerDBPrivate)); + + hsts_enforcer_class->is_persistent = soup_hsts_enforcer_db_is_persistent; + hsts_enforcer_class->changed = soup_hsts_enforcer_db_changed; + + object_class->finalize = soup_hsts_enforcer_db_finalize; + object_class->set_property = soup_hsts_enforcer_db_set_property; + object_class->get_property = soup_hsts_enforcer_db_get_property; + + /** + * SOUP_HSTS_ENFORCER_DB_FILENAME: + * + * Alias for the #SoupHstsEnforcerDB:filename property. (The + * HSTS policy storage filename.) + **/ + g_object_class_install_property ( + object_class, PROP_FILENAME, + g_param_spec_string (SOUP_HSTS_ENFORCER_DB_FILENAME, + "Filename", + "HSTS policy storage filename", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); +} diff --git a/libsoup/soup-hsts-enforcer-db.h b/libsoup/soup-hsts-enforcer-db.h new file mode 100644 index 00000000..38f56e76 --- /dev/null +++ b/libsoup/soup-hsts-enforcer-db.h @@ -0,0 +1,45 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2016 Igalia S.L. + */ + +#ifndef SOUP_HSTS_ENFORCER_DB_H +#define SOUP_HSTS_ENFORCER_DB_H 1 + +#include + +G_BEGIN_DECLS + +#define SOUP_TYPE_HSTS_ENFORCER_DB (soup_hsts_enforcer_db_get_type ()) +#define SOUP_HSTS_ENFORCER_DB(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_HSTS_ENFORCER_DB, SoupHstsEnforcerDB)) +#define SOUP_HSTS_ENFORCER_DB_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_HSTS_ENFORCER_DB, SoupHstsEnforcerDBClass)) +#define SOUP_IS_HSTS_ENFORCER_DB(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_HSTS_ENFORCER_DB)) +#define SOUP_IS_HSTS_ENFORCER_DB_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_HSTS_ENFORCER_DB)) +#define SOUP_HSTS_ENFORCER_DB_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_HSTS_ENFORCER_DB, SoupHstsEnforcerDBClass)) + +typedef struct { + SoupHstsEnforcer parent; + +} SoupHstsEnforcerDB; + +typedef struct { + SoupHstsEnforcerClass parent_class; + + /* Padding for future expansion */ + void (*_libsoup_reserved1) (void); + void (*_libsoup_reserved2) (void); + void (*_libsoup_reserved3) (void); + void (*_libsoup_reserved4) (void); +} SoupHstsEnforcerDBClass; + +#define SOUP_HSTS_ENFORCER_DB_FILENAME "filename" + +SOUP_AVAILABLE_IN_2_42 +GType soup_hsts_enforcer_db_get_type (void); + +SOUP_AVAILABLE_IN_2_42 +SoupHstsEnforcer *soup_hsts_enforcer_db_new (const char *filename); + +G_END_DECLS + +#endif /* SOUP_HSTS_ENFORCER_DB_H */ diff --git a/libsoup/soup-hsts-enforcer-private.h b/libsoup/soup-hsts-enforcer-private.h new file mode 100644 index 00000000..274d0560 --- /dev/null +++ b/libsoup/soup-hsts-enforcer-private.h @@ -0,0 +1,14 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2016 Igalia S.L. + */ + +#ifndef SOUP_HSTS_ENFORCER_PRIVATE_H +#define SOUP_HSTS_ENFORCER_PRIVATE_H 1 + +#include + +void soup_hsts_enforcer_set_policy (SoupHstsEnforcer *hsts_enforcer, + SoupHstsPolicy *policy); + +#endif /* SOUP_HSTS_ENFORCER_PRIVATE_H */ diff --git a/libsoup/soup-hsts-enforcer.c b/libsoup/soup-hsts-enforcer.c new file mode 100644 index 00000000..8f3cf2b7 --- /dev/null +++ b/libsoup/soup-hsts-enforcer.c @@ -0,0 +1,602 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * soup-hsts-enforcer.c: HTTP Strict Transport Security implementation + * + * Copyright (C) 2016 Igalia S.L. + */ + +/* TODO Use only internationalized domain names */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include "soup-hsts-enforcer.h" +#include "soup-hsts-enforcer-private.h" +#include "soup.h" + +/** + * SECTION:soup-hsts-enforcer + * @short_description: Automatic HSTS enforcing for SoupSession + * + * A #SoupHstsEnforcer stores HSTS policies and enforce them when + * required. + * #SoupHstsEnforcer implements #SoupSessionFeature, so you can add a + * HSTS enforcer to a session with soup_session_add_feature() or + * soup_session_add_feature_by_type(). + * + * When the #SoupSession the #SoupHstsEnforcer is attached to sends a + * message, the #SoupHstsEnforcer will ask for a redirection to HTTPS if + * the destination is a known HSTS host and is contacted over an insecure + * transport protocol (HTTP). + * + * Note that the base #SoupHstsEnforcer class does not support any form + * of long-term HSTS policy persistence. + **/ + +static void soup_hsts_enforcer_session_feature_init (SoupSessionFeatureInterface *feature_interface, gpointer interface_data); + +G_DEFINE_TYPE_WITH_CODE (SoupHstsEnforcer, soup_hsts_enforcer, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (SOUP_TYPE_SESSION_FEATURE, + soup_hsts_enforcer_session_feature_init)) + +enum { + CHANGED, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +typedef struct { + GHashTable *host_policies; + GHashTable *session_policies; +} SoupHstsEnforcerPrivate; +#define SOUP_HSTS_ENFORCER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_HSTS_ENFORCER, SoupHstsEnforcerPrivate)) + +static void +soup_hsts_enforcer_init (SoupHstsEnforcer *hsts_enforcer) +{ + SoupHstsEnforcerPrivate *priv = SOUP_HSTS_ENFORCER_GET_PRIVATE (hsts_enforcer); + + priv->host_policies = g_hash_table_new_full (soup_str_case_hash, + soup_str_case_equal, + g_free, NULL); + + priv->session_policies = g_hash_table_new_full (soup_str_case_hash, + soup_str_case_equal, + g_free, NULL); +} + +static void +soup_hsts_enforcer_finalize (GObject *object) +{ + SoupHstsEnforcerPrivate *priv = SOUP_HSTS_ENFORCER_GET_PRIVATE (object); + GHashTableIter iter; + gpointer key, value; + + g_hash_table_iter_init (&iter, priv->host_policies); + while (g_hash_table_iter_next (&iter, &key, &value)) + soup_hsts_policy_free (value); + g_hash_table_destroy (priv->host_policies); + + g_hash_table_iter_init (&iter, priv->session_policies); + while (g_hash_table_iter_next (&iter, &key, &value)) + soup_hsts_policy_free (value); + g_hash_table_destroy (priv->session_policies); + + G_OBJECT_CLASS (soup_hsts_enforcer_parent_class)->finalize (object); +} + +static gboolean +soup_hsts_enforcer_real_is_persistent (SoupHstsEnforcer *hsts_enforcer) +{ + return FALSE; +} + +static void +soup_hsts_enforcer_class_init (SoupHstsEnforcerClass *hsts_enforcer_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (hsts_enforcer_class); + + g_type_class_add_private (hsts_enforcer_class, sizeof (SoupHstsEnforcerPrivate)); + + object_class->finalize = soup_hsts_enforcer_finalize; + + hsts_enforcer_class->is_persistent = soup_hsts_enforcer_real_is_persistent; + + /** + * SoupHstsEnforcer::changed: + * @hsts_enforcer: the #SoupHstsEnforcer + * @old_policy: the old #SoupHstsPolicy value + * @new_policy: the new #SoupHstsPolicy value + * + * Emitted when @hsts_enforcer changes. If a policy has been added, + * @new_policy will contain the newly-added policy and + * @old_policy will be %NULL. If a policy has been deleted, + * @old_policy will contain the to-be-deleted policy and + * @new_policy will be %NULL. If a policy has been changed, + * @old_policy will contain its old value, and @new_policy its + * new value. + **/ + signals[CHANGED] = + g_signal_new ("changed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (SoupHstsEnforcerClass, changed), + NULL, NULL, + NULL, + G_TYPE_NONE, 2, + SOUP_TYPE_HSTS_POLICY | G_SIGNAL_TYPE_STATIC_SCOPE, + SOUP_TYPE_HSTS_POLICY | G_SIGNAL_TYPE_STATIC_SCOPE); +} + +/** + * soup_hsts_enforcer_new: + * + * Creates a new #SoupHstsEnforcer. The base #SoupHstsEnforcer class does + * not support persistent storage of HSTS policies; use a subclass for + * that. + * + * Returns: a new #SoupHstsEnforcer + * + * Since: 2.54 + **/ +SoupHstsEnforcer * +soup_hsts_enforcer_new (void) +{ + return g_object_new (SOUP_TYPE_HSTS_ENFORCER, NULL); +} + +static void +soup_hsts_enforcer_changed (SoupHstsEnforcer *hsts_enforcer, + SoupHstsPolicy *old, SoupHstsPolicy *new) +{ + g_return_if_fail (SOUP_IS_HSTS_ENFORCER (hsts_enforcer)); + + g_assert_true (old || new); + + g_signal_emit (hsts_enforcer, signals[CHANGED], 0, old, new); +} + +static void +soup_hsts_enforcer_remove_expired_host_policies (SoupHstsEnforcer *hsts_enforcer) +{ + SoupHstsEnforcerPrivate *priv; + SoupHstsPolicy *policy; + GList *domains, *p; + const char *domain; + + g_return_if_fail (SOUP_IS_HSTS_ENFORCER (hsts_enforcer)); + + priv = SOUP_HSTS_ENFORCER_GET_PRIVATE (hsts_enforcer); + + /* Remove all the expired policies as soon as one is encountered as required by the RFC. */ + domains = g_hash_table_get_keys (priv->host_policies); + for (p = domains; p; p = p->next ) { + domain = (const char *) p->data; + policy = g_hash_table_lookup (priv->host_policies, domain); + if (policy && soup_hsts_policy_is_expired (policy)) { + g_hash_table_remove (priv->host_policies, domain); + soup_hsts_enforcer_changed (hsts_enforcer, policy, NULL); + soup_hsts_policy_free (policy); + } + } + g_list_free (domains); +} + +static void +soup_hsts_enforcer_remove_host_policy (SoupHstsEnforcer *hsts_enforcer, + const gchar *domain) +{ + SoupHstsEnforcerPrivate *priv; + SoupHstsPolicy *policy; + + g_return_if_fail (SOUP_IS_HSTS_ENFORCER (hsts_enforcer)); + g_return_if_fail (domain != NULL); + + priv = SOUP_HSTS_ENFORCER_GET_PRIVATE (hsts_enforcer); + + policy = g_hash_table_lookup (priv->host_policies, domain); + + g_assert_nonnull (policy); + + g_hash_table_remove (priv->host_policies, domain); + soup_hsts_enforcer_changed (hsts_enforcer, policy, NULL); + soup_hsts_policy_free (policy); + + soup_hsts_enforcer_remove_expired_host_policies (hsts_enforcer); +} + +static void +soup_hsts_enforcer_replace_policy (SoupHstsEnforcer *hsts_enforcer, + SoupHstsPolicy *new_policy) +{ + SoupHstsEnforcerPrivate *priv; + GHashTable *policies; + SoupHstsPolicy *old_policy; + const gchar *domain; + gboolean is_permanent; + + g_return_if_fail (SOUP_IS_HSTS_ENFORCER (hsts_enforcer)); + g_return_if_fail (new_policy != NULL); + + g_assert_false (soup_hsts_policy_is_expired (new_policy)); + + domain = soup_hsts_policy_get_domain (new_policy); + is_permanent = soup_hsts_policy_is_permanent (new_policy); + + g_return_if_fail (domain != NULL); + + priv = SOUP_HSTS_ENFORCER_GET_PRIVATE (hsts_enforcer); + policies = is_permanent ? priv->session_policies : + priv->host_policies; + + old_policy = g_hash_table_lookup (policies, domain); + + g_assert_nonnull (old_policy); + + g_hash_table_remove (policies, domain); + g_hash_table_insert (policies, g_strdup (domain), new_policy); + if (!is_permanent && !soup_hsts_policy_equal (old_policy, new_policy)) + soup_hsts_enforcer_changed (hsts_enforcer, old_policy, new_policy); + soup_hsts_policy_free (old_policy); + + soup_hsts_enforcer_remove_expired_host_policies (hsts_enforcer); +} + +static void +soup_hsts_enforcer_insert_policy (SoupHstsEnforcer *hsts_enforcer, + SoupHstsPolicy *policy) +{ + SoupHstsEnforcerPrivate *priv; + GHashTable *policies; + const gchar *domain; + gboolean is_permanent; + + g_return_if_fail (SOUP_IS_HSTS_ENFORCER (hsts_enforcer)); + g_return_if_fail (policy != NULL); + + g_assert_false (soup_hsts_policy_is_expired (policy)); + + domain = soup_hsts_policy_get_domain (policy); + is_permanent = soup_hsts_policy_is_permanent (policy); + + g_return_if_fail (domain != NULL); + + priv = SOUP_HSTS_ENFORCER_GET_PRIVATE (hsts_enforcer); + policies = is_permanent ? priv->session_policies : + priv->host_policies; + + g_assert_false (g_hash_table_contains (policies, domain)); + + g_hash_table_insert (policies, g_strdup (domain), policy); + if (!is_permanent) + soup_hsts_enforcer_changed (hsts_enforcer, NULL, policy); +} + +/** + * soup_hsts_enforcer_set_policy: + * @hsts_enforcer: a #SoupHstsEnforcer + * @policy: (transfer full): the policy of the HSTS host + * + * Sets @domain's HSTS policy to @policy. If @policy is expired, any + * existing HSTS policy for this host will be removed instead. If a policy + * exited for this host, it will be replaced. Otherwise, the new policy + * will be inserted. + * + * This steals @policy. + * + * Since: 2.54 + **/ +void +soup_hsts_enforcer_set_policy (SoupHstsEnforcer *hsts_enforcer, + SoupHstsPolicy *policy) +{ + SoupHstsEnforcerPrivate *priv; + GHashTable *policies; + const gchar *domain; + gboolean is_permanent; + + g_return_if_fail (SOUP_IS_HSTS_ENFORCER (hsts_enforcer)); + g_return_if_fail (policy != NULL); + + domain = soup_hsts_policy_get_domain (policy); + is_permanent = soup_hsts_policy_is_permanent (policy); + + g_return_if_fail (domain != NULL); + + priv = SOUP_HSTS_ENFORCER_GET_PRIVATE (hsts_enforcer); + policies = is_permanent ? priv->session_policies : + priv->host_policies; + + if (!is_permanent && soup_hsts_policy_is_expired (policy)) { + soup_hsts_enforcer_remove_host_policy (hsts_enforcer, domain); + soup_hsts_policy_free (policy); + return; + } + + if (g_hash_table_contains (policies, domain)) + soup_hsts_enforcer_replace_policy (hsts_enforcer, policy); + else + soup_hsts_enforcer_insert_policy (hsts_enforcer, policy); +} + +static SoupHstsPolicy * +soup_hsts_enforcer_get_host_policy (SoupHstsEnforcer *hsts_enforcer, + const gchar *domain) +{ + SoupHstsEnforcerPrivate *priv; + + g_return_val_if_fail (SOUP_IS_HSTS_ENFORCER (hsts_enforcer), NULL); + g_return_val_if_fail (domain != NULL, NULL); + + priv = SOUP_HSTS_ENFORCER_GET_PRIVATE (hsts_enforcer); + + return g_hash_table_lookup (priv->host_policies, domain); +} + +static SoupHstsPolicy * +soup_hsts_enforcer_get_session_policy (SoupHstsEnforcer *hsts_enforcer, + const gchar *domain) +{ + SoupHstsEnforcerPrivate *priv; + + g_return_val_if_fail (SOUP_IS_HSTS_ENFORCER (hsts_enforcer), NULL); + g_return_val_if_fail (domain != NULL, NULL); + + priv = SOUP_HSTS_ENFORCER_GET_PRIVATE (hsts_enforcer); + + return g_hash_table_lookup (priv->session_policies, domain); +} + +/** + * soup_hsts_enforcer_set_session_policy: + * @hsts_enforcer: a #SoupHstsEnforcer + * @domain: policy domain or hostname + * @include_sub_domains: %TRUE if the policy applies on sub domains + * + * Sets a session policy@domain's HSTS policy to @policy. If @policy is expired, any + * existing HSTS policy for this host will be removed instead. If a policy + * exited for this host, it will be replaced. Otherwise, the new policy + * will be inserted. + * + * Since: 2.54 + **/ +void +soup_hsts_enforcer_set_session_policy (SoupHstsEnforcer *hsts_enforcer, + const char *domain, + gboolean include_sub_domains) +{ + SoupHstsPolicy *policy; + + g_return_if_fail (SOUP_IS_HSTS_ENFORCER (hsts_enforcer)); + g_return_if_fail (domain != NULL); + + policy = soup_hsts_policy_new_permanent (domain, include_sub_domains); + soup_hsts_enforcer_set_policy (hsts_enforcer, policy); +} + +static gboolean +soup_hsts_enforcer_is_valid_host (SoupHstsEnforcer *hsts_enforcer, + const gchar *domain) +{ + SoupHstsPolicy *policy; + + g_return_val_if_fail (SOUP_IS_HSTS_ENFORCER (hsts_enforcer), FALSE); + g_return_val_if_fail (domain != NULL, FALSE); + + if (soup_hsts_enforcer_get_session_policy (hsts_enforcer, domain)) + return TRUE; + + policy = soup_hsts_enforcer_get_host_policy (hsts_enforcer, domain); + if (policy) + return !soup_hsts_policy_is_expired (policy); + + return FALSE; +} + +static gboolean +soup_hsts_enforcer_host_includes_sub_domains (SoupHstsEnforcer *hsts_enforcer, + const gchar *domain) +{ + SoupHstsPolicy *policy; + gboolean include_sub_domains = FALSE; + + g_return_val_if_fail (SOUP_IS_HSTS_ENFORCER (hsts_enforcer), FALSE); + g_return_val_if_fail (domain != NULL, FALSE); + + policy = soup_hsts_enforcer_get_session_policy (hsts_enforcer, domain); + if (policy) + include_sub_domains |= soup_hsts_policy_includes_sub_domains (policy); + + policy = soup_hsts_enforcer_get_host_policy (hsts_enforcer, domain); + if (policy) + include_sub_domains |= soup_hsts_policy_includes_sub_domains (policy); + + return include_sub_domains; +} + +static inline const gchar* +super_domain_of (const gchar *domain) +{ + const gchar *iter = domain; + + g_return_val_if_fail (domain != NULL, NULL); + + for (; *iter != '\0' && *iter != '.' ; iter++); + for (; *iter == '.' ; iter++); + + if (*iter == '\0') + return NULL; + + return iter; +} + +static gboolean +soup_hsts_enforcer_must_enforce_secure_transport (SoupHstsEnforcer *hsts_enforcer, + const gchar *domain) +{ + const gchar *super_domain = domain; + + g_return_val_if_fail (SOUP_IS_HSTS_ENFORCER (hsts_enforcer), FALSE); + g_return_val_if_fail (domain != NULL, FALSE); + + if (soup_hsts_enforcer_is_valid_host (hsts_enforcer, domain)) + return TRUE; + + while ((super_domain = super_domain_of (super_domain)) != NULL) { + if (soup_hsts_enforcer_host_includes_sub_domains (hsts_enforcer, super_domain) && + soup_hsts_enforcer_is_valid_host (hsts_enforcer, super_domain)) + return TRUE; + } + + return FALSE; +} + +/* Processes the 'Strict-Transport-Security' field of a message's response header. */ +static void +soup_hsts_enforcer_process_sts_header (SoupHstsEnforcer *hsts_enforcer, + SoupMessage *msg) +{ + SoupHstsPolicy *policy; + SoupURI *uri; + + g_return_if_fail (hsts_enforcer != NULL); + g_return_if_fail (msg != NULL); + + /* TODO if connection error or warnings received, do nothing. */ + + /* TODO if header received on hazardous connection, do nothing. */ + + uri = soup_message_get_uri (msg); + + g_return_if_fail (uri != NULL); + + policy = soup_hsts_policy_new_from_response (msg); + + g_return_if_fail (policy != NULL); + + soup_hsts_enforcer_set_policy (hsts_enforcer, policy); +} + +/* Enforces HTTPS when demanded. */ +static gboolean +soup_hsts_enforcer_should_redirect_to_https (SoupHstsEnforcer *hsts_enforcer, + SoupMessage *msg) +{ + SoupURI *uri; + const gchar *domain; + + g_return_val_if_fail (hsts_enforcer != NULL, FALSE); + g_return_val_if_fail (msg != NULL, FALSE); + + uri = soup_message_get_uri (msg); + + g_return_val_if_fail (uri != NULL, FALSE); + + // HSTS secures only HTTP connections. + if (uri->scheme != SOUP_URI_SCHEME_HTTP) + return FALSE; + + domain = soup_uri_get_host (uri); + + g_return_val_if_fail (domain != NULL, FALSE); + + return soup_hsts_enforcer_must_enforce_secure_transport (hsts_enforcer, domain); +} + +static void +redirect_to_https (SoupMessage *msg) +{ + SoupURI *src_uri, *dst_uri; + char *dst; + + src_uri = soup_message_get_uri (msg); + + dst_uri = soup_uri_copy (src_uri); + soup_uri_set_scheme (dst_uri, SOUP_URI_SCHEME_HTTPS); + dst = soup_uri_to_string (dst_uri, FALSE); + soup_uri_free (dst_uri); + + soup_message_set_redirect (msg, 301, dst); + g_free (dst); +} + +static void +process_sts_header (SoupMessage *msg, gpointer user_data) +{ + SoupHstsEnforcer *hsts_enforcer = SOUP_HSTS_ENFORCER (user_data); + + g_return_if_fail (hsts_enforcer != NULL); + g_return_if_fail (msg != NULL); + + soup_hsts_enforcer_process_sts_header (hsts_enforcer, msg); +} + +static void +soup_hsts_enforcer_request_queued (SoupSessionFeature *feature, + SoupSession *session, + SoupMessage *msg) +{ + SoupHstsEnforcer *hsts_enforcer = SOUP_HSTS_ENFORCER (feature); + SoupURI *uri; + const char *scheme; + + g_return_if_fail (hsts_enforcer != NULL); + g_return_if_fail (msg != NULL); + + uri = soup_message_get_uri (msg); + + g_return_if_fail (uri != NULL); + + scheme = soup_uri_get_scheme (uri); + + if (scheme == SOUP_URI_SCHEME_HTTP) { + if (soup_hsts_enforcer_should_redirect_to_https (hsts_enforcer, msg)) + redirect_to_https (msg); + } + else if (scheme == SOUP_URI_SCHEME_HTTPS) { + soup_message_add_header_handler (msg, "got-headers", + "Strict-Transport-Security", + G_CALLBACK (process_sts_header), + hsts_enforcer); + } +} + +static void +soup_hsts_enforcer_request_unqueued (SoupSessionFeature *feature, + SoupSession *session, + SoupMessage *msg) +{ + g_signal_handlers_disconnect_by_func (msg, process_sts_header, feature); +} + +static void +soup_hsts_enforcer_session_feature_init (SoupSessionFeatureInterface *feature_interface, + gpointer interface_data) +{ + feature_interface->request_queued = soup_hsts_enforcer_request_queued; + feature_interface->request_unqueued = soup_hsts_enforcer_request_unqueued; +} + +/** + * soup_hsts_enforcer_is_persistent: + * @hsts_enforcer: a #SoupHstsEnforcer + * + * Gets whether @hsts_enforcer stores policies persistenly. + * + * Returns: %TRUE if @hsts_enforcer storage is persistent or %FALSE otherwise. + * + * Since: 2.54 + **/ +gboolean +soup_hsts_enforcer_is_persistent (SoupHstsEnforcer *hsts_enforcer) +{ + g_return_val_if_fail (SOUP_IS_HSTS_ENFORCER (hsts_enforcer), FALSE); + + return SOUP_HSTS_ENFORCER_GET_CLASS (hsts_enforcer)->is_persistent (hsts_enforcer); +} diff --git a/libsoup/soup-hsts-enforcer.h b/libsoup/soup-hsts-enforcer.h new file mode 100644 index 00000000..1253e234 --- /dev/null +++ b/libsoup/soup-hsts-enforcer.h @@ -0,0 +1,53 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2016 Igalia S.L. + */ + +#ifndef SOUP_HSTS_ENFORCER_H +#define SOUP_HSTS_ENFORCER_H 1 + +#include + +G_BEGIN_DECLS + +#define SOUP_TYPE_HSTS_ENFORCER (soup_hsts_enforcer_get_type ()) +#define SOUP_HSTS_ENFORCER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_HSTS_ENFORCER, SoupHstsEnforcer)) +#define SOUP_HSTS_ENFORCER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_HSTS_ENFORCER, SoupHstsEnforcerClass)) +#define SOUP_IS_HSTS_ENFORCER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_HSTS_ENFORCER)) +#define SOUP_IS_HSTS_ENFORCER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_HSTS_ENFORCER)) +#define SOUP_HSTS_ENFORCER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_HSTS_ENFORCER, SoupHstsEnforcerClass)) + +struct _SoupHstsEnforcer { + GObject parent; + +}; + +typedef struct { + GObjectClass parent_class; + + gboolean (*is_persistent) (SoupHstsEnforcer *hsts_enforcer); + + /* signals */ + void (*changed) (SoupHstsEnforcer *jar, + SoupHstsPolicy *old_policy, + SoupHstsPolicy *new_policy); + + /* Padding for future expansion */ + void (*_libsoup_reserved1) (void); + void (*_libsoup_reserved2) (void); +} SoupHstsEnforcerClass; + +SOUP_AVAILABLE_IN_2_54 +GType soup_hsts_enforcer_get_type (void); +SOUP_AVAILABLE_IN_2_54 +SoupHstsEnforcer *soup_hsts_enforcer_new (void); +SOUP_AVAILABLE_IN_2_54 +gboolean soup_hsts_enforcer_is_persistent (SoupHstsEnforcer *hsts_enforcer); + +SOUP_AVAILABLE_IN_2_54 +void soup_hsts_enforcer_set_session_policy (SoupHstsEnforcer *hsts_enforcer, + const char *domain, + gboolean include_sub_domains); +G_END_DECLS + +#endif /* SOUP_HSTS_ENFORCER_H */ diff --git a/libsoup/soup-hsts-policy.c b/libsoup/soup-hsts-policy.c new file mode 100644 index 00000000..e2989dbb --- /dev/null +++ b/libsoup/soup-hsts-policy.c @@ -0,0 +1,479 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * soup-hsts-policy.c + * + * Copyright (C) 2016 Igalia S.L. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "soup-hsts-policy.h" +#include "soup.h" + +/** + * SECTION:soup-hsts-policy + * @short_description: HTTP Strict Transport Security policies + * @see_also: #SoupHstsEnforcer + * + * #SoupHstsPolicy implements HTTP policies, as described by RFC 6797. + * + * To have a #SoupSession handle HSTS policies for your appliction + * automatically, use a #SoupHstsEnforcer. + **/ + +/** + * SoupHstsPolicy: + * @domain: the "domain" attribute, or else the hostname that the + * policy came from. + * @expires: the policy expiration time, or %NULL for a session policy + * @include_sub_domains: %TRUE if the policy applies on sub domains + * + * An HTTP Strict Transport Security policy. + * + * @domain give the host or domain that this policy belongs to and applies + * on. + * + * @expires will be non-%NULL if the policy has been set by the host and + * hence has an expiry time. If @expires is %NULL, it indicates that the + * policy is a session policy set by the user agent. + * + * If @include_sub_domains is set, the strict transport security policy + * must also be enforced on all subdomains of @domain. + * + * Since: 2.54 + **/ + +G_DEFINE_BOXED_TYPE (SoupHstsPolicy, soup_hsts_policy, soup_hsts_policy_copy, soup_hsts_policy_free) + +/** + * soup_hsts_policy_copy: + * @policy: a #SoupHstsPolicy + * + * Copies @policy. + * + * Return value: a copy of @policy + * + * Since: 2.54 + **/ +SoupHstsPolicy * +soup_hsts_policy_copy (SoupHstsPolicy *policy) +{ + SoupHstsPolicy *copy = g_slice_new0 (SoupHstsPolicy); + + copy->domain = g_strdup (policy->domain); + copy->expires = policy->expires ? soup_date_copy(policy->expires) + : NULL; + copy->include_sub_domains = policy->include_sub_domains; + + return copy; +} + +/** + * soup_hsts_policy_equal: + * @policy1: a #SoupCookie + * @policy2: a #SoupCookie + * + * Tests if @policy1 and @policy2 are equal. + * + * Note that currently, this does not check that the cookie domains + * match. This may change in the future. + * + * Return value: whether the cookies are equal. + * + * Since: 2.24 + */ +gboolean +soup_hsts_policy_equal (SoupHstsPolicy *policy1, SoupHstsPolicy *policy2) +{ + g_return_val_if_fail (policy1, FALSE); + g_return_val_if_fail (policy2, FALSE); + + if (strcmp (policy1->domain, policy2->domain)) + return FALSE; + + if (policy1->include_sub_domains != policy2->include_sub_domains) + return FALSE; + + if ((policy1->expires && !policy2->expires) || + (!policy1->expires && policy2->expires)) + return FALSE; + + if (policy1->expires && policy2->expires && + soup_date_to_time_t (policy1->expires) != + soup_date_to_time_t (policy2->expires)) + return FALSE; + + return TRUE; +} + +static inline const char * +skip_lws (const char *s) +{ + while (g_ascii_isspace (*s)) + s++; + return s; +} + +static inline const char * +unskip_lws (const char *s, const char *start) +{ + while (s > start && g_ascii_isspace (*(s - 1))) + s--; + return s; +} + +#define is_attr_ender(ch) ((ch) < ' ' || (ch) == ';' || (ch) == ',' || (ch) == '=') +#define is_value_ender(ch) ((ch) < ' ' || (ch) == ';') + +static char * +parse_value (const char **val_p, gboolean copy) +{ + const char *start, *end, *p; + char *value; + + p = *val_p; + if (*p == '=') + p++; + start = skip_lws (p); + for (p = start; !is_value_ender (*p); p++) + ; + end = unskip_lws (p, start); + + if (copy) + value = g_strndup (start, end - start); + else + value = NULL; + + *val_p = p; + return value; +} + +static SoupHstsPolicy * +parse_one_policy (const char *header, SoupURI *origin) +{ + const char *start, *end, *p; + gboolean has_value; + long max_age = -1; + gboolean include_sub_domains = FALSE; + + g_return_val_if_fail (origin == NULL || origin->host, NULL); + + p = start = skip_lws (header); + + /* Parse directives */ + do { + if (*p == ';') + p++; + + start = skip_lws (p); + for (p = start; !is_attr_ender (*p); p++) + ; + end = unskip_lws (p, start); + + has_value = (*p == '='); +#define MATCH_NAME(name) ((end - start == strlen (name)) && !g_ascii_strncasecmp (start, name, end - start)) + + if (MATCH_NAME ("max-age") && has_value) { + char *max_age_str, *max_age_end; + + /* Repeated directives make the policy invalid. */ + if (max_age >= 0) + goto fail; + + max_age_str = parse_value (&p, TRUE); + max_age = strtol (max_age_str, &max_age_end, 10); + g_free (max_age_str); + + if (*max_age_end == '\0') { + /* Invalid 'max-age' directive makes the policy invalid. */ + if (max_age < 0) + goto fail; + } + } else if (MATCH_NAME ("includeSubDomains")) { + /* Repeated directives make the policy invalid. */ + if (include_sub_domains) + goto fail; + + /* The 'includeSubDomains' directive can't have a value. */ + if (has_value) + goto fail; + + include_sub_domains = TRUE; + } else { + /* Unknown directives must be skipped. */ + if (has_value) + parse_value (&p, FALSE); + } + } while (*p == ';'); + + /* No 'max-age' directive makes the policy invalid. */ + if (max_age < 0) + goto fail; + + return soup_hsts_policy_new_with_max_age (origin->host, max_age, + include_sub_domains); + +fail: + return NULL; +} + +/** + * Return value: %TRUE if the hostname is suitable for an HSTS host, %FALSE + * otherwise. + **/ +static gboolean +is_hostname_valid (const char *hostname) +{ + if (!hostname) + return FALSE; + + /* Hostnames must have at least one '.' + */ + if (!strchr (hostname, '.')) + return FALSE; + + /* IP addresses are not valid hostnames, only domain names are. + */ + if (g_hostname_is_ip_address (hostname)) + return FALSE; + + /* The hostname should be a valid domain name. + */ + return TRUE; +} + +/** + * soup_hsts_policy_new: + * @domain: policy domain or hostname + * @expires: (transfer full): the expiry date of the policy + * @include_sub_domains: %TRUE if the policy applies on sub domains + * + * Creates a new #SoupHstsPolicy with the given attributes. + * + * @domain is a domain on which the strict transport security policy + * represented by this object must be enforced. + * + * @expires is the date and time when the policy should be considered + * expired. + * + * If @include_sub_domains is %TRUE, the strict transport security policy + * must also be enforced on all subdomains of @domain. + * + * Return value: a new #SoupHstsPolicy. + * + * Since: 2.54 + **/ +SoupHstsPolicy * +soup_hsts_policy_new (const char *domain, SoupDate *expires, + gboolean include_sub_domains) +{ + SoupHstsPolicy *policy; + + g_return_val_if_fail (is_hostname_valid (domain), NULL); + + policy = g_slice_new0 (SoupHstsPolicy); + policy->domain = g_strdup (domain); + policy->expires = expires; + policy->include_sub_domains = include_sub_domains; + + return policy; +} + +/** + * soup_hsts_policy_new_with_max_age: + * @domain: policy domain or hostname + * @max_age: max age of the policy + * @include_sub_domains: %TRUE if the policy applies on sub domains + * + * Creates a new #SoupHstsPolicy with the given attributes. + * + * @domain is a domain on which the strict transport security policy + * represented by this object must be enforced. + * + * @max_age is used to set the "expires" attribute on the policy; pass + * SOUP_HSTS_POLICY_MAX_AGE_PAST for an already-expired policy, or a + * lifetime in seconds. + * + * If @include_sub_domains is %TRUE, the strict transport security policy + * must also be enforced on all subdomains of @domain. + * + * Return value: a new #SoupHstsPolicy. + * + * Since: 2.54 + **/ +SoupHstsPolicy * +soup_hsts_policy_new_with_max_age (const char *domain, int max_age, + gboolean include_sub_domains) +{ + SoupDate *expires; + SoupHstsPolicy *policy; + + g_return_val_if_fail (is_hostname_valid (domain), NULL); + g_return_val_if_fail (max_age >= 0, NULL); + + if (max_age == SOUP_HSTS_POLICY_MAX_AGE_PAST) { + /* Use a date way in the past, to protect against + * clock skew. + */ + expires = soup_date_new (1970, 1, 1, 0, 0, 0); + } else + expires = soup_date_new_from_now (max_age); + + policy = soup_hsts_policy_new (domain, expires, include_sub_domains); + + if (!policy) + soup_date_free (expires); + + return policy; +} + +/** + * soup_hsts_policy_new_permanent: + * @domain: policy domain or hostname + * @include_sub_domains: %TRUE if the policy applies on sub domains + * + * Creates a new #SoupHstsPolicy with the given attributes. + * + * @domain is a domain on which the strict transport security policy + * represented by this object must be enforced. + * + * If @include_sub_domains is %TRUE, the strict transport security policy + * must also be enforced on all subdomains of @domain. + * + * Return value: a new #SoupHstsPolicy. + * + * Since: 2.54 + **/ +SoupHstsPolicy * +soup_hsts_policy_new_permanent (const char *domain, + gboolean include_sub_domains) +{ + return soup_hsts_policy_new (domain, NULL, include_sub_domains); +} + +/** + * soup_hsts_policy_new_from_response: + * @msg: a #SoupMessage containing a "Strict-Transport-Security" response + * header + * + * Parses @msg's first "Strict-Transport-Security" response header and + * returns a #SoupHstsPolicy, or %NULL if no valid + * "Strict-Transport-Security" response header was found. + * + * Return value: (nullable): a new #SoupHstsPolicy, or %NULL if no valid + * "Strict-Transport-Security" response header was found. + * + * Since: 2.54 + **/ +SoupHstsPolicy * +soup_hsts_policy_new_from_response (SoupMessage *msg) +{ + SoupURI *origin; + const char *name, *value; + SoupMessageHeadersIter iter; + + soup_message_headers_iter_init (&iter, msg->response_headers); + while (soup_message_headers_iter_next (&iter, &name, &value)) { + if (g_ascii_strcasecmp (name, "Strict-Transport-Security") != 0) + continue; + + origin = soup_message_get_uri (msg); + return parse_one_policy (value, origin); + } + + return NULL; +} + +/** + * soup_hsts_policy_get_domain: + * @policy: a #SoupHstsPolicy + * + * Gets @policy's domain. + * + * Return value: @policy's domain. + * + * Since: 2.54 + **/ +const char * +soup_hsts_policy_get_domain (SoupHstsPolicy *policy) +{ + return policy->domain; +} + +/** + * soup_hsts_policy_is_expired: + * @policy: a #SoupHstsPolicy + * + * Gets whether @policy is expired. + * + * Permanent policies never expire. + * + * Return value: whether @policy is expired. + * + * Since: 2.54 + **/ +gboolean +soup_hsts_policy_is_expired (SoupHstsPolicy *policy) +{ + return policy->expires && soup_date_is_past (policy->expires); +} + +/** + * soup_hsts_policy_includes_sub_domains: + * @policy: a #SoupHstsPolicy + * + * Gets whether @policy include its sub-domains. + * + * Return value: whether @policy include its sub-domains. + * + * Since: 2.54 + **/ +gboolean +soup_hsts_policy_includes_sub_domains (SoupHstsPolicy *policy) +{ + return policy->include_sub_domains; +} + +/** + * soup_hsts_policy_is_permanent: + * @policy: a #SoupHstsPolicy + * + * Gets whether @policy is permanent (not expirable). + * + * A permanent policy never expires and should not be saved by a persistent + * #SoupHstsEnforcer so the user agent can control them. + * + * Return value: whether @policy is permanent. + * + * Since: 2.54 + **/ +gboolean +soup_hsts_policy_is_permanent (SoupHstsPolicy *policy) +{ + return !policy->expires; +} + +/** + * soup_hsts_policy_free: + * @policy: a #SoupHstsPolicy + * + * Frees @policy. + * + * Since: 2.54 + **/ +void +soup_hsts_policy_free (SoupHstsPolicy *policy) +{ + g_return_if_fail (policy != NULL); + + g_free (policy->domain); + g_clear_pointer (&policy->expires, soup_date_free); + + g_slice_free (SoupHstsPolicy, policy); +} diff --git a/libsoup/soup-hsts-policy.h b/libsoup/soup-hsts-policy.h new file mode 100644 index 00000000..8492d4a9 --- /dev/null +++ b/libsoup/soup-hsts-policy.h @@ -0,0 +1,59 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2016 Igalia S.L. + */ + +#ifndef SOUP_HSTS_POLICY_H +#define SOUP_HSTS_POLICY_H 1 + +#include + +G_BEGIN_DECLS + +struct _SoupHstsPolicy { + char *domain; + SoupDate *expires; + gboolean include_sub_domains; +}; + +SOUP_AVAILABLE_IN_2_54 +GType soup_hsts_policy_get_type (void); +#define SOUP_TYPE_HSTS_POLICY (soup_hsts_policy_get_type()) + +#define SOUP_HSTS_POLICY_MAX_AGE_PAST (0) + +SOUP_AVAILABLE_IN_2_54 +SoupHstsPolicy *soup_hsts_policy_new (const char *domain, + SoupDate *expiry_date, + gboolean include_sub_domains); +SOUP_AVAILABLE_IN_2_54 +SoupHstsPolicy *soup_hsts_policy_new_with_max_age (const char *domain, + int max_age, + gboolean include_sub_domains); +SOUP_AVAILABLE_IN_2_54 +SoupHstsPolicy *soup_hsts_policy_new_permanent (const char *domain, + gboolean include_sub_domains); +SOUP_AVAILABLE_IN_2_54 +SoupHstsPolicy *soup_hsts_policy_new_from_response (SoupMessage *msg); + +SOUP_AVAILABLE_IN_2_54 +SoupHstsPolicy *soup_hsts_policy_copy (SoupHstsPolicy *policy); +SOUP_AVAILABLE_IN_2_54 +gboolean soup_hsts_policy_equal (SoupHstsPolicy *policy1, + SoupHstsPolicy *policy2); + +SOUP_AVAILABLE_IN_2_54 +const char *soup_hsts_policy_get_domain (SoupHstsPolicy *policy); +SOUP_AVAILABLE_IN_2_54 +gboolean soup_hsts_policy_is_expired (SoupHstsPolicy *policy); +SOUP_AVAILABLE_IN_2_54 +gboolean soup_hsts_policy_includes_sub_domains (SoupHstsPolicy *policy); +SOUP_AVAILABLE_IN_2_54 +gboolean soup_hsts_policy_is_permanent (SoupHstsPolicy *policy); + +SOUP_AVAILABLE_IN_2_54 +void soup_hsts_policy_free (SoupHstsPolicy *policy); + +G_END_DECLS + +#endif /* SOUP_HSTS_POLICY_H */ diff --git a/libsoup/soup-types.h b/libsoup/soup-types.h index e020de7d..5bd5b285 100644 --- a/libsoup/soup-types.h +++ b/libsoup/soup-types.h @@ -19,6 +19,8 @@ typedef struct _SoupAuthDomain SoupAuthDomain; typedef struct _SoupCookie SoupCookie; typedef struct _SoupCookieJar SoupCookieJar; typedef struct _SoupDate SoupDate; +typedef struct _SoupHstsEnforcer SoupHstsEnforcer; +typedef struct _SoupHstsPolicy SoupHstsPolicy; typedef struct _SoupMessage SoupMessage; typedef struct _SoupRequest SoupRequest; typedef struct _SoupRequestHTTP SoupRequestHTTP; diff --git a/libsoup/soup.h b/libsoup/soup.h index 6bedb624..711cccb4 100644 --- a/libsoup/soup.h +++ b/libsoup/soup.h @@ -29,6 +29,9 @@ extern "C" { #include #include #include +#include +#include +#include #include #include #include -- cgit v1.2.1