Inline Scripts
Most uses of inline scripts that would break when using CSP can be fixed by factoring the javascript out to an external .js file, and making the location of that file a CSP-approved source.
Fixing Inline Script Blocks
The code in the inline script block violation example can be fixed by simply moving the <script>
block into a .js file and adding the location of that file to the script-src
part of the CSP declaration.
If necessary, or during a transition period to fully refactored code, CSP 1.1 allows the use of a script nonce
attribute on script blocks that should be allowed.
The previous inline block has simply been moved to a .js file, with no other changes:
Alternately, the script element below uses a nonce value. If that value is matches one provided in the script-src of the CSP header, it will add the current time here: 12:14:22 PM
(The nonce here is hardcoded for demonstration purposes; in proper usage it should be cryptographically random, just as with a session cookie.)
<script nonce='nRfqpuKWNuYyUAFPTr6WVNZk9'> document.getElementById('example-inline-script-nonce').innerHTML=new Date().toLocaleTimeString(); </script>
Fixing Inline Event Handlers
Inline event handlers can be moved to external js files, and the equivalent code can be programmatically attached to the event handler by wrapping it in an anonymous function.
document.getElementById('example-inline-event-handler').onclick = function() {alert('Programmatically attached event handler ran')};
Fixing ‘javascript:’ URIs
These are simply not allowed with any effective CSP settings. If necessary, they can be emulated by setting an onClick event, as in Inline Event Handlers <a>
tags.
This link will pop an alert if inline sources are allowed: Try Me
The event handler was attached using an external .js file
document.getElementById('example-javascript-uri').onclick = function() {alert('Programmatically attached <a> onclick ran')};
Eval
Eval is pretty much nuked by default when using CSP; since it can do anything, it’s not possible to assert that it’s safe. You can allow it by adding 'unsafe-eval'
to your sources list, but you should probably consider that a transitional band-aid.
Fixing Eval
If eval is being used to execute arbitrary code, there really isn’t a fix; the entire approach needs to be rethought.
On the other hand, most limited uses of eval should be easy to fix by creating functions with parameters to handle the variations of code that were being passed to eval in normal, expected use.
No example here; just meditate on if eval is really needed (it isn’t).
In particular, using eval() to unpack JSON is a terrible idea. Use a proper JSON parser instead (jQuery provides access to the browser’s own parser via $.parseJSON()
).
And, just to top it off… non-eval based code is also going to run faster (possibly much faster if the eval is in a loop).
Fixing setTimeout() and setInterval()
Both versions of these that take existing functions are perfectly fine. Only invocations that take a string and eval it are affected, and just like eval, there’s no direct fix for these if they’re being used for arbitrary code.
More reasonable uses should also be able to be replaced by one or more functions defined in external js files.
The return values here should all be zero if they were prevented from running: [ , , , , ]If the code must access the global scope (such creating var foo in the example), simply explicitly stick it in the window
object.
document.getElementById('example-settimeout-value1').innerHTML = '' + setTimeout(function() {window['foo0'] = 'bar';}, 1000 * 3600);
Fixing document.write()
Use cases for document.write() are varied, but the most challenging use cases to replace tend to involve ad-hoc creation of lots of tags (DOM elements) via string concatenation. These can generally be replaced by programmatically constructing the DOM elements, using JSON data or HTML5 data-* attributes if the source data should be supplied by the HTML content (context).
var parentNode = document.getElementById('example-docwrite'); var contentNode = document.createElement('strong'); contentNode.innerHTML = parentNode.dataset.docwriteExampleText; parentNode.appendChild(contentNode);
Style
Fixing Inline Style Attributes
Replace inline CSS with either a stylesheet or styling with javascript in an allowed source.
document.getElementById('example-inline-style').style = 'color:blue;';
Internal Style Sheet
Internal style blocks can simply be extracted to an external CSS file.
If the style values are dynamic (such as user skins), the css classes can either be modified via javascript (based on whatever criteria are currently used), or factored out to a dynamic CSS file that is generated server-side.
<link href='/static/compliant_styles.css' rel='stylesheet'>
External Style Sheet
Without 'unsafe-inline'
inline use of the <style>
attribute to an external style sheet throws a violation instead of executing
<link rel='stylesheet' type='text/css' href='blue.css'><div class='get-blue-2'>This font should be blue</div>
Image
Fixing Image Sources
Make sure all your image sources are on the included in img-src
directives in the CSP policy. Here the img tag isn’t changed at all; https://*.google.com is just added to the img-src list.
<img src='https://www.google.com/images/srpr/logo4w.png'>
img-src https://*.google.com
Embedded Image
Embedded images can be allowed by adding data:
to the img-src
directive, or by factoring out the generator code into a server side script and pointing the image src there.
<img alt='' src='data:image/jpeg;base64,/9j/4AAQSkZJRgABAgAAZABkAAD/2wBDAAEBAQEBAQEBAQECAQEBAgICAQECAgMCAgICAgMEAwMDAwMDBAQEBAUEBAQGBgYGBgYICAgICAkJCQkJCQkJCQn/2wBDAQICAgMDAwUEBAUIBgUGCAkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQn/wAARCAAwADADAREAAhEBAxEB/8QAGQABAQEBAQEAAAAAAAAAAAAAAAgKCQUH/8QAKxAAAAQFAwMDBQEAAAAAAAAAAgMEBQABBgcICRESExQWFRciGSRak9YK/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/ANhEAgEAgEAgEBOV3sw8R8fKlQ0ZfvKW3NkKwc0JTo20pV9bs1NOShtONNILVlJXFUSaMkZpJgAmSDxmIApSnuGewfVbb3OtteSi2a5FobhMd1bd1H3Hj1e027JXxlX9meYlP7dciMNIN6R5Qyx8RT4jCIM9pynKA9yp6npqiaaqGs6zqFDSNH0ihVulV1W6KykDa2NqAoR6pWrVHiAUSSSUAQzDBikEIZTnOcpSgJytvnVhHeStGa29ocxrV3VuJUfcePUFTdwmF8el/ZkGKj+3Qolhp5vSIKGYPiGfEARCntKU5wFUwCAxZ/6NdJfUEzxzctbd7FGwHurbunLVslNvNQ+VU2x9F6Rvz4tOT9B4ckZ4uJCwkXMIJgny2kKc5ClIO/WifjfejEbTHxnx6yFoz2+vBb7zPy+kPUULt2nq1Vuzkl+6bT1KUzqJVJY/gYLbltPYUpykFU51W3rS8mEeY1obbs3kdxLq2ruFTdBU93BCPv3p8YViJCn66owogrqnmhDzMGEAd9xClKU5wGLPRP0T9TjEbU4xnyFyFxn9vrP2+8z8vq/zOlHbtPVqUdm1L9q2uylUZ1FSksHwLFty3nsGU5yDfrAIBAIBAIBAIDizqN6YmXGad7qWulYTVYuNgxR7BSqFgcrS0gU8jbXFySr1ysx3Nm3VG0FdY4pUWSLckQuJIdxzlsEIQF9AjUm/Invh+iqf7iAfQI1JvyJ74foqn+4gKpwn0hc3MXsnLZ30u9rO3Uywt3Q3rPkNgKkKfgsr/wCptKtvI7ia2qnIj7U9QBSDkmH8yw7cZ7CCHfqAQCAQCAQCA//Z' />
img-src data:
Frame
Frame restrictions are described in the child-src of the CSP spec:
The child-src directive governs the creation of nested browsing contexts as well as Worker execution contexts.
- Whenever the user agent fetches a URI (including when following redirects) in the course of one of the following activities, if the URI does not match the allowed frame sources, the user agent must act as if it had received an empty HTTP 400 response and report a violation.
- Report a violation when requesting data for display in a nested browsing context in the protected resource created by an
iframe
or aframe
element. - Navigating such as nested browsing context.
Iframe Example 1
The use of the <iframe>
tag with a 'src'
to a frame or iframe throws a violation instead of loading the page
<iframe src='https://example.iana.org'></iframe>
Iframe Example 2
The use of the <iframe>
tag with a 'src'
to a frame or iframe throws a violation instead of loading the page
<iframe SRC='javascript:document.write('Iframe');'></iframe>
Fonts
Remote Inline
Fonts can still be sourced from external domains, they just just need to be specified in the font-src
directive.
This example uses a font from the Google Font API, so it also uses a style-src
directive to allow the @font-face declaration to be loaded.
<link href='https://fonts.googleapis.com/css?family=Chela+One' rel='stylesheet' type='text/css'>
font-src https://themes.googleusercontent.com
Connect
XMLHttpRequest
XHR requests are controlled by connect-src
; simply include domains you want to allow connections to (* is supported). You’ll need an appropriate Access-Control-Allow-Origin header on the remote side of course.
The response code of an XHR request to Github is here:
var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function() { if (xhr.readyState == 4) { document.getElementById('example-connect').innerHTML = xhr.status; } } // Github is a cross-origin early adopter, and don't seem like they'd mind the traffic xhr.open('GET', 'https://api.github.com', true); xhr.send(null);
connect-src 'self' https://api.github.com ;
Object
Remote Object Sources
Valid object src locations should be specified in the object-src
part of the policy.
The box below is a demo Flash widget sourced from adobe.com.
<object type="application/x-shockwave-flash" data="https://helpx.adobe.com/content/help/en/flash-player/kb/find-version-flash-player/_jcr_content/main-pars/flash_0/file.res/widget.swf" width="406" height="140" style="visibility: visible;"> ... </object>
object-src 'self' https://helpx.adobe.com ;
Media
Embed
CSP 1.1 introduces the concept of plugin-types (not recognized in 1.0), allowing you to specify the MIME type of object
and embded
tags.
CSP also enforces that the expected MIME type (in the type attribute) matches the actual MIME type from the server header; make sure they agree.
<embed src="/static/stack_smashing.pdf" type="application/pdf" width="800" height="150"></embed>
object-src 'self' ; plugin-types application/pdf ;