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

Issue 800603 link

Starred by 1 user

Issue metadata

Status: Assigned
Owner:
Buried. Ping if important.
Components:
EstimatedDays: ----
NextAction: ----
OS: Mac
Pri: 3
Type: Bug



Sign in to add a comment

strict-dynamic CSP source extensions do not apply to link elements with rel="import"

Project Member Reported by wyatta@google.com, Jan 10 2018

Issue description

Chrome Version: 63.0.3239.132 (Official Build) (64-bit)
OS: macOS High Sierra 10.13.2 (17C88)

What steps will reproduce the problem?
(1) Serve an HTML document with a CSP that uses a script-src nonce and strict-dynamic.
(2) Load a script using a script tag with the correct nonce.
(3) Allow the script to append a script element and an HTML import link element. Do not set the nonce on either.

What is the expected result?
  Both the inner script and inner HTML import load without violating the CSP.

What happens instead?
  Only the script loads and the HTML import is rejected.

Below is a golang program that illustrates the bug with a simple web-server. If you run the program and visit /a in Chrome, you will see that it loads /b on account of the script tag with correct nonce. However, /b tries to load both /c and /d (an HTML import and script respectively), but fails to load /c for CSP reasons.

Interestingly, if the link element being programmatically added is given a nonce attribute, it loads just fine, so it seems that the nonce extension of the CSP still applies, but not the strict-dynamic extension.

==================================
package main
import (
  "io"
  "net/http"
)

const ResponseA = `
<html><body>
  <script src='/b' nonce="foo"></script>
</body></html>`
const ResponseB = `
var link = document.createElement('link');
link.href = '/c';
link.rel = 'import';
document.body.appendChild(link);

var script = document.createElement('script');
script.src = '/d';
document.body.appendChild(script);`
const ResponseC = `I never get loaded.`
const ResponseD = `console.log('I get executed');`

func handleA(w http.ResponseWriter, r *http.Request) {
  w.Header().Set("content-type", "text/html; charset=utf-8")
  w.Header().Set("content-security-policy", "script-src 'nonce-foo' 'strict-dynamic';")
  io.WriteString(w, ResponseA)
}

func handleB(w http.ResponseWriter, r *http.Request) {
  w.Header().Set("content-type", "text/javascript; charset=utf-8")
  io.WriteString(w, ResponseB)
}

func handleC(w http.ResponseWriter, r *http.Request) {
  w.Header().Set("content-type", "text/html; charset=utf-8")
  io.WriteString(w, ResponseC)
}

func handleD(w http.ResponseWriter, r *http.Request) {
  w.Header().Set("content-type", "text/javascript")
  io.WriteString(w, ResponseD)
}

func main() {
  http.HandleFunc("/a", handleA)
  http.HandleFunc("/b", handleB)
  http.HandleFunc("/c", handleC)
  http.HandleFunc("/d", handleD)
  http.ListenAndServe(":8000", nil)
}

 

Comment 1 by mkwst@chromium.org, Jan 11 2018

Owner: mkwst@chromium.org
Status: Assigned (was: Untriaged)

Sign in to add a comment