Safely creating script tag and attaching to DOM



  • I have a need to to create and attach a script tag to DOM at runtime. The script is a remote webpack bundle (via a Module Federation plugin). I would like to be able to change the URL to that hosted remote bundle at runtime (for A/B testing and debugging purposes). This is my own code, and I trust it as far as not having malicious code in it.

    The code would look something like this

    var myScript = document.createElement('script');
    myScript.src = 'https://my.remote.domain.com/path/to/myScript.js';
    document.head.appendChild(myScript);
    ... other code to make the webpack bundle run
    

    My questions are:

    1. Is it safe to allow passing the https://my.remote.domain.com/path/to/myScript.js as a URL query param. Ex: https://my.site.com?scriptUrl=https://my.remote.domain.com/path/to/myScript.js? The code would look like this:

      var urlParams = new URLSearchParams(window.location.search);
      var myScript = document.createElement('script');
      myScript.src = urlParams.get('post');
      document.head.appendChild(myScript);
      

      It feels like this could open up a hole for an attacker to pass in a URL to an attacker script and execute it on my page. Am I on the right track here, or is this not a concern?

    2. What are the ways to secure the code above and allow the dynamic script attachments?

    3. If this code is an issue, what are the specific attacks that could happen here?



    1. Is it safe to allow passing the [script URL] as a URL query param?

    No, this is very dangerous. See question #3.

    1. If this code is an issue, what are the specific attacks that could happen here?

    Since an attacker could pass any script into the URL, and have it run in your sites origin, you are basically opening yourself up to XSS attacks. That is very bad.

    1. What are the ways to secure the code above and allow the dynamic script attachments?

    You need to somehow make sure that only scripts that you trust can be used. I can see two possible ways to do that:

    • Whitelisting. Instead of having the script URL as a query param, either have the value A or B for your A/B-testing. Then you keep a dictionary in your code of what URL A and B refers to. This way, only scripts you have allowed in the dictionary can be used.
    • Enforce that the script only comes from domains you trust. You will need to be careful about file upload functionality and open redirects with this solution though.

    If not to impractical, I would go with the whitelist.

    It might be tempting to use CSP for this, but I would strongly advice as using that as your only line of defense here. Browser support is not perfect, and it is easy to loose the header with a small server configuration mishap.



Suggested Topics

  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2