1

So, the backstory is that I am the developer of a pretty big CMS. hundreds of thousands of lines of code.

So when implementing a Content Security Policy I was forced to use "unsafe-inline" for the style-src and script-src directives, since the CMS outputs a lot of inline CSS in elements and also a lot of onclick-attributes for scripting.

In order to extract the inline styles, I have updated the code that creates the actual HTML to make a crc32 hash of the inline style, apply a class to the element and then print all the CSS seperately in a style tag that has a nonce-attribute. The end result looks like this:

enter image description here

With inline scripts, or rather onclick attributes, I have done something similar, create a crc32 of the command, add that as a inline class for the element and then adding it to a script tag. The problem is that I am using jQuery to attach these events to the elements, and the old live() function is deprecated, so I've attached it as a click event on the body element, so that hidden elements will still fire. The end result looks like this:

enter image description here

So, all of these were earlier <a onclick="pop('....')"> or something similar. I am wondering what the best practice for this fix is.

And before you suggest that each of these instances should have their actions attached more logically or replaced by some form of internal logic to make them fire the correct command - I agree! But it's a lot of code! My first step is to make a fix and then start addressing each instance to make a more beautiful fix.

So is there a better way to approach this?

Sandman
  • 2,323
  • 5
  • 28
  • 34
  • I have tried moving the command from onclick="" to data-onclick="" and then run it via a unified script that would run the script in the data-onclick attribute, but without unsafe-eval, the browser won't allow it, which is logical. – Sandman Apr 29 '21 at 13:52

1 Answers1

0

Ok, so I may have found a workaround. If my system is to print this code:

<a onclick="dialog('/url.php')">Do stuff</a>

My code now outputs this:

<a data-cmd="dialog" data-arg="/url.php">Do stuff</a>

And then I have this global code:

$("body").on("click", "[data-cmd]", function(){
    switch ($(this).attr("data-cmd")){
        case "dialog" : dialog($(this).attr("data-arg")); break;
        case "bbe" : bbe($(this).attr("data-arg")); break;
        case "visa" : visa($(this).attr("data-arg")); break;
        case "svisa" : svisa($(this).attr("data-arg")); break;
        case "pop" : pop($(this).attr("data-arg")); break;
        case "dialog" : dialog($(this).attr("data-arg")); break;
        default: return false;
    }
    return false;
})

So using this I can have a safelist of sorts of commands that can appear in data-cmd and I don't have to eval() anything, which is violation with the content security policy.

I'm sure I'll find a lot of outliers that this doesn't work for (I can imagine onclick="hide('el1');show('el2')" failing, but then I can fallback to my earlier fix and put them in a separate script block with nonce.

Sandman
  • 2,323
  • 5
  • 28
  • 34