Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Hide nonce content attribute values. (#2369) #2373

Merged
merged 12 commits into from Nov 22, 2017
108 changes: 73 additions & 35 deletions source
Expand Up @@ -6839,6 +6839,56 @@ a.setAttribute('href', 'https://example.com/'); // change the content attribute
</ol>
</div>

<h4>Nonce attributes</h4>

<p>A <dfn data-export="">nonce content attribute</dfn> represents a cryptographic nonce ("number
used once") which can be used by <cite>Content Security Policy</cite> to determine whether or not
a given fetch will be allowed to proceed. The value is text. <ref spec="CSP"></p>

<p>Elements that have a <span>nonce content attribute</span> ensure that the crytographic nonce is
only exposed to script (and not to side-channels like CSS attribute selectors) by extracting the
value from the content attribute, moving it into an internal slot name <dfn data-export=""
data-dfn-for="NoncedHTMLElement" data-dfn-type="attribute">[[CryptographicNonce]]</dfn>, and
exposing it to script via the <code>NoncedHTMLElement</code> interface defined below:</p>

<pre class="idl">[NoInterfaceObject]
interface <dfn>NoncedHTMLElement</dfn> {
[<span>CEReactions</span>] attribute DOMString nonce;
};</pre>

<dl class="domintro">
<dt><var>element</var> . <code data-x="">nonce</code></dt>
<dd>
<p>Returns the value of the element's <code>[[CryptographicNonce]]</code> internal slot.</p>
<p>Can be set, to update that slot's value.</p>
</dd>
</dl>

<p>The <dfn><code data-x="dom-NoncedHTMLElement-nonce">nonce</code></dfn> IDL attribute must, on
getting, return the value of the element's <code>[[CryptographicNonce]]</code>; and on setting,
set the element's <code>[[CryptographicNonce]]</code> to the specified new value.</p>

<p>When such an element that implements <code>NoncedHTMLElement</code> <span>becomes
connected</span>, the user agent must <span>immediately</span> execute the following steps on the
<var>element</var>:
Copy link
Member

Choose a reason for hiding this comment

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

Do you have mutation observer tests for this behavior? This is a little different from the parser modifying the value directly.

Copy link
Member

Choose a reason for hiding this comment

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

Also, is the nonce likely shared across elements? If so, we should maybe hide it for all elements including those that do not yet implement NoncedHTMLElement. Otherwise each time we add it to a new element you might end up having it accidentally exposed in user agents that do not implement that new nonce feature yet.

Copy link
Member

Choose a reason for hiding this comment

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

In which case it should maybe be part of DOM's "superglobal" (not named as such) attributes.

Copy link
Member Author

Choose a reason for hiding this comment

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

Do you have mutation observer tests for this behavior?

Nope, but I can add some to web-platform-tests/wpt#5423.

Also, is the nonce likely shared across elements? If so, we should maybe hide it for all elements including those that do not yet implement NoncedHTMLElement. Otherwise each time we add it to a new element you might end up having it accidentally exposed in user agents that do not implement that new nonce feature yet.

I'm happy to do it that way, it's probably more future-proof. Also, I'm told that Firefox actually implements nonces for more resource types than we've actually specced, so doing it at a higher level is probably helpful for them in the short term.

In that case, would you suggest dropping NoncedHTMLElement, and putting the behavior on HTMLElement directly?

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, though if you also want to add it to SVG (for <svg:script> and friends) we should maybe just put it on Element.

(I didn't realize the observable mutation of the nonce content attribute was intentional. I guess it doesn't matter much as we also expose it through the nonce IDL attribute.)

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, though if you also want to add it to SVG (for svg:script and friends) we should maybe just put it on Element.

I do indeed want to apply the same change to SVGScriptElement. Element sounds like the simplest way to do that.

(I didn't realize the observable mutation of the nonce content attribute was intentional. I guess it doesn't matter much as we also expose it through the nonce IDL attribute.)

It's "intentional" only insofar as it seems a necessary side-effect of tying the adjustment to insertion. I suppose we could avoid it by hooking more deeply into the mechanics of setting the attribute's value via something like setAttribute, but it doesn't seem like a good idea to introduce that kind of complexity.

Since we're really only concerned about hiding the value from non-script sources, that seems like a reasonable tradeoff.


<ol>
<li>
<p>If <var>element</var> has a <span>nonce content attribute</span> <var>attr</var> whose value
is not the empty string, then:</p>

<ol>
<li>Let <var>nonce</var> be <var>attr</var>'s value.</li>
<li>Set <var>attr</var>'s value to the empty string.</li>
Copy link
Contributor

Choose a reason for hiding this comment

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

What is the initial value in this slot, if any?

Copy link
Member Author

Choose a reason for hiding this comment

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

Let's say the empty string.

<li>Set <var>element</var>.<code>[[CryptographicNonce]]</code> to <var>nonce</var>.</li>
</ol>
</li>
</ol>

Copy link
Contributor

Choose a reason for hiding this comment

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

Why does this need [CEReactions]? But see below.

Copy link
Member Author

Choose a reason for hiding this comment

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

I think you're right; this attribute is unnecessary in the model we're working with here.

<p>The <span data-x="concept-node-clone-ext">cloning steps</span> for elements that implement
<code>NoncedHTMLElement</code> must set the <code>[[CryptographicNonce]]</code> slot on the copy
to the value of the slot on the element being cloned.</p>


<h3>Common DOM interfaces</h3>
Copy link
Contributor

Choose a reason for hiding this comment

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

So if you do this on an element with a "nonce" attribute before it's been inserted into the DOM, it won't return anything useful, right? Is that the behavior we want?

Similarly, if you set this on an element before it's inserted into the DOM, what happens?

Copy link
Member Author

Choose a reason for hiding this comment

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

So if you do this on an element with a "nonce" attribute before it's been inserted into the DOM, it won't return anything useful, right? Is that the behavior we want?

That's the way things are written right now, yes.

I think what you're implying is that we might want to change the mechanism to trigger when attributes are appended, changed, or replaced? We can explore that, but it's not really clear to me what the mechanism would look like. Here, we have an attribute on the element, and we reset it's value upon insertion, which has pretty clearly defined behaviors. If we alter the above algorithms, what visible behavior would you expect from x.setAttribute('nonce', 'nonceynonce');? Would you expect a mutation event with a newValue of 'nonceynonce'? Or ''? Or two events?

Similarly, if you set this on an element before it's inserted into the DOM, what happens?

Assuming something like:

var x = document.createElement('script');
x.src = 'whatever.js';
x.nonce = 'nonceynonce';
document.head.appendChild(x);

I'd expect the inserted element to have its [[CryptographicNonce]] slot populated with nonceynonce. If that matched the page's policy, then the script would execute.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think what you're implying is that we might want to change the mechanism to trigger when attributes are appended, changed, or replaced?

No, I'm implying that the nonce getter should perhaps return the attribute value if set, the internal slot otherwise. Maybe. It's really not terribly clear what's best here in cases when we both have a value in the slot and an attribute value.

One think that's not clear to me: in current UAs if you have a <link> that is in the DOM, and you change its nonce attribute, does that trigger an attempt to refetch the stylesheet? If not, it sounds like we have undesirable path-dependency where the sheet we get depends on the order of setting "nonce" and insertion. We have this for <script> by virtue of script execution being irreversible, but stylesheet loads don't really require a path dependency, and we probably shouldn't introduce one if we can avoid it.

Assuming something like:

What about the same case but that sets both .nonce and the nonce content attribute, to different values? What is the expected behavior, and does this model produce it? Does the order of the sets matter?

Copy link
Member Author

Choose a reason for hiding this comment

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

No, I'm implying that the nonce getter should perhaps return the attribute value if set, the internal slot otherwise. Maybe. It's really not terribly clear what's best here in cases when we both have a value in the slot and an attribute value.

That seems more confusing than just treating them as separate things. I think I'd prefer the currently described behavior.

One think that's not clear to me: in current UAs if you have a that is in the DOM, and you change its nonce attribute, does that trigger an attempt to refetch the stylesheet?

You mean something like the following?

var l = document.createElement('link');
l.rel = "stylesheet";
l.href = "/whatever.css";
document.head.appendChild(l);
l.nonce = "yay";

If so, no, it doesn't. Skimming https://cs.chromium.org/chromium/src/third_party/WebKit/Source/core/html/HTMLLinkElement.cpp?rcl=ded5e27012a9b0d50de4b80a16e024fe5106b160&l=65, I think Blink will potentially trigger a load upon parsing rel, href, type, as, media, or scope. That doesn't match up with the spec's description of when to "obtain" the resource: https://html.spec.whatwg.org/multipage/semantics.html#link-type-stylesheet:concept-link-obtain.

If not, it sounds like we have undesirable path-dependency where the sheet we get depends on the order of setting "nonce" and insertion. We have this for <script> by virtue of script execution being irreversible, but stylesheet loads don't really require a path dependency, and we probably shouldn't introduce one if we can avoid it.

I don't have strong feelings about this. If you'd like <link> to trigger reloads when we touch the nonce attribute, I'm fine with that.

Copy link
Member Author

Choose a reason for hiding this comment

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

What about the same case but that sets both .nonce and the nonce content attribute, to different values? What is the expected behavior, and does this model produce it? Does the order of the sets matter?

var x = document.createElement('script');
x.src = 'whatever.js';
x.nonce = 'nonceynonce';
x.setAttribute('nonce', 'invalid');
document.head.appendChild(x);

Would not execute, as the internal slot would be overwritten with the incorrect value at insertion time. The order of the assignement to x.nonce vs the call to setAttribute('nonce', ...) shouldn't matter, as the insertion-time behavior doesn't care about the way the element got into a particular state, it just processes the attributes as they are.


Expand Down Expand Up @@ -12839,7 +12889,6 @@ interface <dfn>HTMLLinkElement</dfn> : <span>HTMLElement</span> {
[<span>CEReactions</span>] attribute <span>RequestDestination</span> <span data-x="dom-link-as">as</span>; // (default "")
[SameObject, PutForwards=<span data-x="dom-DOMTokenList-value">value</span>] readonly attribute <span>DOMTokenList</span> <span data-x="dom-link-relList">relList</span>;
[<span>CEReactions</span>] attribute DOMString <span data-x="dom-link-media">media</span>;
[<span>CEReactions</span>] attribute DOMString <span data-x="dom-link-nonce">nonce</span>;
[<span>CEReactions</span>] attribute DOMString <span data-x="dom-link-integrity">integrity</span>;
[<span>CEReactions</span>] attribute DOMString <span data-x="dom-link-hreflang">hreflang</span>;
[<span>CEReactions</span>] attribute DOMString <span data-x="dom-link-type">type</span>;
Expand All @@ -12849,7 +12898,9 @@ interface <dfn>HTMLLinkElement</dfn> : <span>HTMLElement</span> {
[<span>CEReactions</span>] attribute <span>WorkerType</span> <span data-x="dom-link-workertype">workerType</span>;
[<span>CEReactions</span>] attribute boolean <span data-x="dom-link-useCache">useCache</span>;
};
<span>HTMLLinkElement</span> implements <span>LinkStyle</span>;</pre>
<span>HTMLLinkElement</span> implements <span>LinkStyle</span>;
<span>HTMLLinkElement</span> implements <span>NoncedHTMLElement</span>;
</pre>
</dd>
</dl><!--TOPIC:HTML-->

Expand All @@ -12865,6 +12916,10 @@ interface <dfn>HTMLLinkElement</dfn> : <span>HTMLElement</span> {
<span>CORS settings attribute</span>. It is intended for use with <span data-x="external resource
link">external resource links</span>.</p>

<p>The <dfn><code data-x="attr-link-nonce">nonce</code></dfn> attribute is a <span>nonce content
attribute</span>. It is intended for use with <span data-x="external resource link">external
resource links</span>.</p>

<p>The types of link indicated (the relationships) are given by the value of the <dfn><code
data-x="attr-link-rel">rel</code></dfn> attribute, which, if present, must have a value that is a
<span>set of space-separated tokens</span>. The <a href="#linkTypes">allowed keywords and their
Expand Down Expand Up @@ -12956,11 +13011,6 @@ interface <dfn>HTMLLinkElement</dfn> : <span>HTMLElement</span> {
<p>The <dfn><code data-x="attr-link-media">media</code></dfn> attribute says which media the
resource applies to. The value must be a <span>valid media query list</span>.</p>

<p>The <dfn><code data-x="attr-link-nonce">nonce</code></dfn> attribute represents a cryptographic
nonce ("number used once") which can be used by <cite>Content Security Policy</cite> to determine
whether or not an <span data-x="external resource link">external resource specified by the
link</span> will be loaded and applied to the document. The value is text. <ref spec="CSP"></p>

<p>The <dfn data-export="" data-dfn-for="link" data-dfn-type="element-attr"><code
data-x="attr-link-integrity">integrity</code></dfn> attribute represents the <span
data-x="concept-request-integrity-metadata">integrity metadata</span> for requests which this
Expand Down Expand Up @@ -13078,7 +13128,6 @@ interface <dfn>HTMLLinkElement</dfn> : <span>HTMLElement</span> {
<dfn><code data-x="dom-link-hreflang">hreflang</code></dfn>,
<dfn><code data-x="dom-link-integrity">integrity</code></dfn>,
<dfn><code data-x="dom-link-media">media</code></dfn>,
<dfn><code data-x="dom-link-nonce">nonce</code></dfn>,
<dfn><code data-x="dom-link-rel">rel</code></dfn>,
<dfn><code data-x="dom-link-scope">scope</code></dfn>,
<dfn><code data-x="dom-link-sizes">sizes</code></dfn>, and
Expand Down Expand Up @@ -13220,8 +13269,8 @@ interface <dfn>HTMLLinkElement</dfn> : <span>HTMLElement</span> {
<span>environment settings object</span>.

<li><p>Set <var>request</var>'s <span data-x="concept-request-nonce-metadata">cryptographic
nonce metadata</span> to the current value of the <code>link</code> element's <code
data-x="attr-link-nonce">nonce</code> content attribute.</p></li>
nonce metadata</span> to the current value of the <code>link</code> element's
<code>[[CryptographicNonce]]</code> internal slot.</p></li>

<li><p>Set <var>request</var>'s <span data-x="concept-request-integrity-metadata">integrity
metadata</span> to the current value of the <code>link</code> element's <code
Expand Down Expand Up @@ -14559,10 +14608,10 @@ people expect to have work and what is necessary.
<pre class="idl">[<span>HTMLConstructor</span>]
interface <dfn>HTMLStyleElement</dfn> : <span>HTMLElement</span> {
[<span>CEReactions</span>] attribute DOMString <span data-x="dom-style-media">media</span>;
[<span>CEReactions</span>] attribute DOMString <span data-x="dom-style-nonce">nonce</span>;
[<span>CEReactions</span>] attribute DOMString <span data-x="dom-style-type">type</span>;
};
<span>HTMLStyleElement</span> implements <span>LinkStyle</span>;</pre>
<span>HTMLStyleElement</span> implements <span>LinkStyle</span>;
<span>HTMLStyleElement</span> implements <span>NoncedHTMLElement</span>;</pre>
</dd>
</dl><!--TOPIC:HTML-->

Expand Down Expand Up @@ -14605,10 +14654,8 @@ interface <dfn>HTMLStyleElement</dfn> : <span>HTMLElement</span> {
attribute is omitted, is "<code data-x="">all</code>", meaning that by default styles apply to all
media.</p>

<p>The <dfn><code data-x="attr-style-nonce">nonce</code></dfn> attribute represents a
cryptographic nonce ("number used once") which can be used by <cite>Content Security Policy</cite>
to determine whether or not the style specified by an element will be applied to the document. The
value is text. <ref spec="CSP"></p>
<p>The <dfn><code data-x="attr-style-nonce">nonce</code></dfn> attribute is a <span>nonce content
attribute</span>.</p>

<p id="title-on-style">The <dfn><code data-x="attr-style-title">title</code></dfn> attribute on
<code>style</code> elements defines <span data-x="CSS style sheet set">CSS style sheet
Expand Down Expand Up @@ -14783,8 +14830,7 @@ c-end = "-->"</pre>

<div w-nodev>

<p>The <dfn><code data-x="dom-style-media">media</code></dfn>, <dfn><code
data-x="dom-style-nonce">nonce</code></dfn>, and <dfn><code
<p>The <dfn><code data-x="dom-style-media">media</code></dfn>, and <dfn><code
data-x="dom-style-type">type</code></dfn> IDL attributes must <span>reflect</span> the respective
content attributes of the same name.</p>

Expand Down Expand Up @@ -57540,10 +57586,10 @@ interface <dfn>HTMLScriptElement</dfn> : <span>HTMLElement</span> {
[<span>CEReactions</span>] attribute boolean <span data-x="dom-script-defer">defer</span>;
[<span>CEReactions</span>] attribute DOMString? <span data-x="dom-script-crossOrigin">crossOrigin</span>;
[<span>CEReactions</span>] attribute DOMString <span data-x="dom-script-text">text</span>;
[<span>CEReactions</span>] attribute DOMString <span data-x="dom-script-nonce">nonce</span>;
[<span>CEReactions</span>] attribute DOMString <span data-x="dom-script-integrity">integrity</span>;

};</pre>
};
<span>HTMLScriptElement</span> implements <span>NoncedHTMLElement</span>;</pre>
</dd>
</dl><!--TOPIC:HTML-->

Expand Down Expand Up @@ -57690,9 +57736,8 @@ interface <dfn>HTMLScriptElement</dfn> : <span>HTMLElement</span> {
data-x="CORS protocol">CORS protocol</span> for cross-origin fetching.</p>

<p>The <dfn data-export="" data-dfn-for="script" data-dfn-type="element-attr"><code
data-x="attr-script-nonce">nonce</code></dfn> attribute represents a cryptographic nonce ("number
used once") which can be used by <cite>Content Security Policy</cite> to determine whether or not
the script specified by an element will be executed. The value is text. <ref spec="CSP"></p>
data-x="attr-script-nonce">nonce</code></dfn> attribute is a <span>nonce content
attribute</span>.</p>

<p>The <dfn data-export="" data-dfn-for="script" data-dfn-type="element-attr"><code
data-x="attr-script-integrity">integrity</code></dfn> attribute represents the <span
Expand All @@ -57717,10 +57762,9 @@ interface <dfn>HTMLScriptElement</dfn> : <span>HTMLElement</span> {
<p>The IDL attributes <dfn><code data-x="dom-script-src">src</code></dfn>, <dfn><code
data-x="dom-script-type">type</code></dfn>, <dfn><code
data-x="dom-script-charset">charset</code></dfn>, <dfn><code
data-x="dom-script-defer">defer</code></dfn>, <dfn><code
data-x="dom-script-integrity">integrity</code></dfn>, and <dfn><code
data-x="dom-script-nonce">nonce</code></dfn>, must each <span>reflect</span> the respective
content attributes of the same name.</p>
data-x="dom-script-defer">defer</code></dfn>, and <dfn><code
data-x="dom-script-integrity">integrity</code></dfn>, must each <span>reflect</span> the
respective content attributes of the same name.</p>

<p>The <dfn><code data-x="dom-script-crossOrigin">crossOrigin</code></dfn> IDL attribute must
<span>reflect</span> the <code data-x="attr-script-crossorigin">crossorigin</code> content attribute.</p>
Expand Down Expand Up @@ -58219,14 +58263,8 @@ o............A....e
</dl>
</li>

<li>

<p>If the <code>script</code> element has a <code data-x="attr-script-nonce">nonce</code>
attribute, then let <var>cryptographic nonce</var> be that attribute's value.</p>

<p>Otherwise, let <var>cryptographic nonce</var> be the empty string.</p>

</li>
<li><p>Let <var>cryptographic nonce</var> be the element's <code>[[CryptographicNonce]]</code>
internal slot's value.</p></li>

<li>

Expand Down