New issue
Advanced search Search tips
Note: Color blocks (like or ) mean that a user may not be available. Tooltip shows the reason.
Starred by 7 users

Issue metadata

Status: Fixed
Closed: Jan 2015
EstimatedDays: ----
NextAction: ----
OS: Windows
Pri: 2
Type: Bug

Sign in to add a comment

Issue 444511: HPKP enforces includeSubDomains even when the directive is not asserted

Reported by, Dec 22 2014

Issue description

Chrome Version       : Version 39.0.2171.95 m
OS Version: Windows 8.1 Pro x64
URLs (if applicable) :
Other browsers tested:
Add OK or FAIL after other browsers where you have tested this issue:
     Safari 5: No HPKP Support
  Firefox 4.x: No HPKP Support
     IE 7/8/9: No HPKP Support

What steps will reproduce the problem?
1. Issue a HPKP policy header: add_header Public-Key-Pins "pin-sha256='X3pGTSOuJeEVw989IJ/cEtXUEmy52zs1TZQrU06KUKg='; pin-sha256='MHJYVThihUrJcxW6wcqyOISTXIsInsdj3xK8QrZbHec='; pin-sha256='isi41AizREkLvvft0IRW4u3XMFR2Yg7bvrF7padyCJg='; max-age=10";. Note the lack of the 'includeSubdomains' directive.
2. Visit the site to confirm delivery of the header. 
3. Try to access a subdomain of that site. The HPKP policy is enforced on all subdomains when it should not not be. Subdomains cannot be accessed as a result as their certificates are not included in the HPKP policy. 
4. Remove the HPKP policy header. 
5. Wait the duration of 'max-age' (10 seconds) so that the policy expires. 
6. Try to access a subdomain again. 
7. Subdomain is still inaccessible. Policy does not expire. 

What is the expected result?
The HPKP policy being issued on should not have any impact on subdomains unless the 'includeSubdomains' directive is included in the policy. The policy should also expire after you have not loaded the site for 10 seconds (max-age). 

What happens instead of that?
The HPKP policy is enforced on subdomains of rendering them inaccessible due to their certificates not being pinned in the policy. The policy also doesn't expire after the time specified in max-age has elapsed. 

Please provide any additional information below. Attach a screenshot if

I have included a screenshot showing the policy being delivered from and a screenshot of one of my subdomains after I have manually cleared the HPKP policy from chrome://net-internals/#hsts so that I could access the subdomain. This shows that the policy is not being delivered when visiting the subdomain so it should not be applied there. 

UserAgentString: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36
HPKP subdomain browser.png
116 KB View Download
HPKP domain browser.png
103 KB View Download

Comment 1 by, Dec 24 2014

Labels: TE-NeedFurtherTriage

Comment 2 by, Dec 30 2014

Labels: -TE-NeedFurtherTriage TE-NeedsFurtherTriage

Comment 3 by, Jan 6 2015

Labels: Cr-Internals-Network-SSL

Comment 4 by, Jan 7 2015

Labels: Needs-Feedback
Can you please attach a net-internals log as detailed in

Comment 5 by, Jan 7 2015

I have captured a log of the fault happening and attached it.
973 KB Download

Comment 6 by, Jan 7 2015

Labels: Cr-Security
Summary: HPKP enforces includeSubDomains even when the directive is not asserted (was: HPKP not being enforced properly)

Comment 7 by, Jan 9 2015

I think I see the bug. We don't actually treat the HSTS and HPKP include_subdomains flags orthogonally. GetDynamicDomainState will accept either HSTS or HPKP include_subdomains and not check again in CheckPublicKeyPins (or ShouldUpgradeToSSL) since it never sees the domain. A quick fix would be to pass the hostname into both and check again, though that would have strange side effects like: if you set HSTS + includeSubdomains on, it would apply to unless sets a HPKP key pin. If then doesn't explicitly set HSTS, it won't have it. Likewise with HSTS and HPKP inverted.

We really should query the two independently and not tie their into the same mechanism. Either separate the structs completely or still return one jumbo struct but query separately within GetDynamicDomainState. The former is probably tidier. I don't think DomainState has any methods that cares about both.

Comment 8 by, Jan 9 2015

Would this also apply to the value of the max-age directive then? The max-age on my HPKP policy is set to 10, yet doesn't expire. I do have a max-age value of 31536000 on my HSTS policy.

Comment 9 by, Jan 9 2015

Yeah, I think the same mix-up is happening with max-age too. CheckPublicKeyPins doesn't re-check max-age.

Comment 10 by, Jan 9 2015

Status: Assigned
I've started fiddling with this, so I'll go take the bug.

Comment 11 by, Jan 13 2015

Status: Started
I did an initial pass at fixing this here:

Comment 12 by, Jan 13 2015

Linking since these seem related: In addition, I'm not sure this is related, but it looks like the set of pins that are cached is not cleared for each valid response (each valid Public-Key-Pinning header is supposed to clear out the old entries and set it to the values in the latest response):

Comment 13 by, Jan 13 2015

Hrm, yeah, I can see how it ends up appending the old entries. I think my patch actually ended up fixing that too. I'll update it to include an explicit test.

Comment 14 by, Jan 13 2015

 Issue 412866  has been merged into this issue.

Comment 15 by, Jan 15 2015

Project Member
The following revision refers to this bug:

commit ffd3a3bf5a45013052f6ae319983a7a249f4db38
Author: davidben <>
Date: Thu Jan 15 21:04:45 2015

Treat HSTS and HPKP state independently.

Although we have historically, and in static preloads, treated HSTS and HPKP as
part of the same underlying mechanism, the new headers consider them completely
orthogonal. Our current implementation has bugs where, particular where
includeSubdomains is involved, HPKP and HSTS entries get mixed together. This
CL does the following:

- Include separate domain strings for HPKP and HSTS in the output of
  GetDynamicDomainState. This allows net-internals to report on the two

- Switch tests to query TransportSecurityState's public API rather than
  manipulate DomainState directly, to reduce dependency on it.

- Make AddHSTSHeader, AddHSTS, etc., follow the same codepath. Notably the
  header variants called GetDynamicDomainState to get the template which means
  an includeSubdomains HPKP state on a parent domain would get copied over.

- AddHPKPHeader no longer appends the old pins to the new set.

- Make DeleteAllDynamicDataSince clear STS and PKP state independently.
  Notably, the old version would almost never drop DomainState entries because
  pkp.last_observed would be uninitialized and never pass the check.

- Make GetDynamicDomainState stitch together the appropriate STS and PKP
  results to form its output DomainState. This avoids includeSubdomains and
  expiration from one mechanism interacting with that of another.

- Add tests for all this.

We should remove DomainState altogether and leave PKPState and STSState as
separate entities (with some consideration for how they were historically
stored on disk), but this CL leaves that alone for now.

BUG= 444511 

Review URL:

Cr-Commit-Position: refs/heads/master@{#311734}


Comment 16 by, Jan 15 2015

Labels: M-42
Status: Fixed
And with that, I think it should be fixed. (Expect it to be in the next canary if you want to test it out.)

Sign in to add a comment