Skip to content

getScript requires 'unsafe-inline' CSP rule #3969

Closed

Description

@koczkatamas

jQuery should be able to load scripts via $.getScript without causing unsafe-inline CSP violations.

Look at the following test case:

index.html

<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' https://code.jquery.com 'sha256-DOU0/sVUxitk2MNb5zXL6o3XIe+ZQ5oKG5jmGDO9PmQ='">
<script src="https://code.jquery.com/jquery-3.3.1.js"></script>
<input id="btnJQuery" type="button" value="Load script via jQuery" />
<input id="btnScriptTag" type="button" value="Load script via script tag" />

<script>
  console.log("start");
  
  $("#btnJQuery").on("click", function() {
    $.getScript("payload.js");
  });
  
  $("#btnScriptTag").on("click", function() {
    let scriptEl = document.createElement("script");
    scriptEl.onload = function() { console.log('onload', arguments); };
    scriptEl.onerror = function() { console.log('onerror', arguments); };
    scriptEl.src = "payload.js";
    document.head.appendChild(scriptEl);  
  });
</script>

payload.js

console.log("OK");

If you click on the Load script via jQuery button the following error is shown in the browser's console (Chrome in this case):

jquery-3.3.1.js:111 Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self' https://code.jquery.com 'sha256-DOU0/sVUxitk2MNb5zXL6o3XIe+ZQ5oKG5jmGDO9PmQ='". Either the 'unsafe-inline' keyword, a hash ('sha256-oKrRnv4NSECe1z+2Q8HSC47J9uP6ALT0+UguOrbK7UU='), or a nonce ('nonce-...') is required to enable inline execution.

On the other hand if you click on the Load script via script tag button then the script is loaded without issues and the text OK is shown in the console.

Note: I am reporting this issue, because third-party libraries use $.getScript() and they consider this a jQuery issue (rightly) and won't fix this by their own because they expect the fix from jQuery.

For example see this issue: jackocnr/intl-tel-input#541

Security considerations

Using constructs which require unsafe-inline or unsafe-eval are generally not recommended and the security community considers them as bad practice.

Mozilla MDN states the following about unsafe-inline:

Disallowing inline styles and inline scripts is one of the biggest security wins CSP provides.

Google recommends the same in their Web Fundamentals guide

If you must have inline script and style, you can enable it by adding 'unsafe-inline' as an allowed source in a script-src or style-src directive. You can also use a nonce or a hash (see below), but you really shouldn't. Banning inline script is the biggest security win CSP provides, and banning inline style likewise hardens your application.

you can enable them by adding 'unsafe-eval' as an allowed source in a script-src directive, but we strongly discourage this.

So I hope you will consider changing the current behavior for the sake of a safer web (taking into account how widespread your library is).

Activity

  1. mgol commented on Feb 7, 2018

    @mgol
    Member

    Thanks for the report. This looks like a duplicate of #3541 so I'm closing it.

  2. koczkatamas commented on Feb 7, 2018

    @koczkatamas
    Author

    I am not sure that the linked PR in #3541 resolves this problem. It is similar problem, but I am not sure it's the same that's why I opened this issue.

  3. dmethvin commented on Feb 7, 2018

    @dmethvin
    Member

    Yeah i don't think it's the same. You can avoid the problem by adding a nonce or hash but we should discuss whether to always inject a script tag for external files.

  4. koczkatamas commented on Feb 7, 2018

    @koczkatamas
    Author

    It would be great workaround for me if you could add a global flag for jQuery or an ajaxSetup setting that if enabled, then jQuery would use the script tag + src method for every request. It could be disabled by default of course.

    AFAIS this could be a workaround for those who disabled the unsafe-inline usage in their CSP.

  5. mgol commented on Feb 7, 2018

    @mgol
    Member

    Ideally, as I suggested in #3541 (comment), we'd feature detect script.onerror support and use the script tag + src approach if it's supported. If it's not possible to feature detect that, though, then maybe we should introduce a flag that enables CSP support and avoid stuff that violates it even if it makes some very old browsers unusable.

    Feature detection ideas welcome!

  6. mgol commented on Feb 7, 2018

    @mgol
    Member

    I'll add this to the 3.4.0 milestone, this sounds like something we should resolve.

  7. added this to the 3.4.0 milestone on Feb 7, 2018
  8. mgol commented on Feb 7, 2018

    @mgol
    Member

    Hmm, instead of detecting script.onerror support we could detect if CSP is enabled by:

    var cspMode = ( function() {
      try {
        new Function( "" );
        return false;
      } catch ( e ) {
        return true;
      }
    } )();

    This is similar to what e.g. AngularJS does. What do you think?

  9. dmethvin commented on Feb 7, 2018

    @dmethvin
    Member

    Agreed on 3.4.0 and landing this with gh-3782 would be nice in order to deal with all CSP issues in one release. I think it would be better and more consistent to always use a script tag regardless of CSP, if we have to make an exception for Android 4.0 to work then we'd only get/eval there.

    @koczkatamas In general we've sworn off global flags because they make it very difficult for a large page full of plugins written at different times by different people to depend on consistent behavior. The jQuery.ajax documentation is already pretty difficult to understand because of all the options and the way they interact. Changing the rules retroactively makes it worse. Subtle behavior changes like this are also difficult for jQuery Migrate to detect and warn about.

    Keep in mind that this won't help the original case you linked to since they were using jQuery 1.12 which has not been maintained for a while. They would need to update to 3.4.0 to get this fix.

  10. koczkatamas commented on Feb 7, 2018

    @koczkatamas
    Author

    @dmethvin If you can auto-detect onerror support then I consider that a superior solution, so feel free to forget my 'global flag' idea. :)

    The issue I linked was opened by somebody else, we are using jQuery 3 and we are prepared to switch to latest jQuery at any time. (Especially if the CSP fix lands.)

    @mgol please note that new Function( "" ); technically tests for unsafe-eval and not unsafe-inline and AFAIK it will send a warning to the report-uri endpoint configured in the CSP which can cause unnecessary traffic.

    I am not sure how, but I think Angular (at least the latest versions) solves this otherwise. We use Angular and we don't get any unsafe-* errors from it.

  11. mgol commented on Feb 7, 2018

    @mgol
    Member

    I think it would be better and more consistent to always use a script tag regardless of CSP, if we have to make an exception for Android 4.0 to work then we'd only get/eval there.

    @dmethvin It's not just Android 4.0 but all <=4.3. How would you make an exception for those Android Browsers without UA-sniffing?

  12. dmethvin commented on Feb 8, 2018

    @dmethvin
    Member

    @mgol yeah it looks like although Android sets the property to a function if you create an attribute, it doesn't run the function on a 404. Maybe it does on a script error but I didn't try.
    https://jsfiddle.net/dmethvin/cxby538z/

  13. 19 remaining items

  14. dmethvin commented on Feb 9, 2019

    @dmethvin
  15. locked as resolved and limited conversation to collaborators on Feb 9, 2019
  16. mgol commented on Jul 23, 2020

    @mgol
    Member

    I've just looked at this issue and I'm a bit puzzled by @gibson042's comment:

    To document what was discussed in the meeting today, we SHOULD support restrictive CSP configuration but MUST NOT break currently-working strict ordering of script evaluation in domManip (property 1 in #2126 (comment) ). A proper fix will probably adopt script elements for even same-site content, but also implement manual serialization similar to the suggestion (leveraging something like rel=preload if performance is an issue).

    Maybe I'm missing something but this seems to concern synchronous script execution that we do during DOM manipulation but this issue is about $.getScript which is already asynchronous. As far as I understand, the only blocker is that some legacy browsers like Android Browser can't do error handling for non-inline scripts as they lack the onerror attribute support. Does that mean we can just flip the behavior of $.getScript for 4.0 without all that complexity? The issue about synchronous script execution in DOM manip is already tracked in #1895.

  17. added
    Discuss in MeetingReserved for Issues and PRs that anyone would like to discuss in the weekly meeting.
    on Jul 23, 2020
  18. removed
    Discuss in MeetingReserved for Issues and PRs that anyone would like to discuss in the weekly meeting.
    on Jul 27, 2020
  19. self-assigned this
    on Jul 27, 2020
  20. added a commit that references this issue on Jul 29, 2020
    a6b9e90
  21. mgol commented on Jul 29, 2020

    @mgol
    Member

    PR: #4763.

  22. mgol commented on Aug 24, 2020

    @mgol
    Member

    Adding the Behavior Change label per @dmethvin's comment: #4763 (review)

  23. added a commit that references this issue on Aug 24, 2020
    0ddb0e0
  24. added a commit that references this issue on Aug 25, 2020
    07a8e4a
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions