6

Ok, yes, it's 2020 but don't laugh. I'm trying update some ASP.NET web forms. My goal is to lock them down, making them more secure by applying a more restrictive Content Security Policy (CSP). To that end, I'm using a nonce, rather than allowing unsafe-inline for scripting.

For "simple" web forms, it's working fine. However, I hit a problem whenever there's an ASP control that results in a post back. When I look in the page source, I see stuff like this:

<script type="text/javascript">
//<![CDATA[
var theForm = document.forms['form1'];
if (!theForm) {
    theForm = document.form1;
}
function __doPostBack(eventTarget, eventArgument) {
    if (!theForm.onsubmit || (theForm.onsubmit() != false)) {
        theForm.__EVENTTARGET.value = eventTarget;
        theForm.__EVENTARGUMENT.value = eventArgument;
        theForm.submit();
    }
}

and

<script type="text/javascript">
    function previewFile() {
        var preview = document.querySelector('#Body_Main_LogoImage');
        var file = document.querySelector('#Body_Main_logoUpload').files[0];
        var reader = new FileReader();

        reader.onloadend = function () {
            preview.src = reader.result;
        }

        if (file) {
            reader.readAsDataURL(file);
        } else {
            preview.src = "";
        }
    }
</script>

This code is generated by some of the MS web form code, I believe. The problem is that neither of these script elements have a nonce, which I'd like to supply, so it is in violation of my CSP (which does not include unsafe-inline). Is there anything I can override to customize this behaviour?

Michael
  • 8,362
  • 6
  • 61
  • 88
Notre
  • 1,093
  • 3
  • 13
  • 26

3 Answers3

4

Some of the offending code is defined in a private method (e.g. RenderPostBackScript) inside the Page class, which in turn is called by internal methods. So, overriding it the normal way isn't an option unless I replace a large bunch of code. As an alternative, I overrode CreateHtmlTextWriter in my page:

protected override System.Web.UI.HtmlTextWriter CreateHtmlTextWriter(TextWriter tw)
{
    return new HtmlTextWriter(tw, this);
}

and then in my HtmlWriter class, I do this:

public override void Write(string s)
{
    if (s != null && s.IndexOf("<script") > -1 && s.IndexOf("nonce") == -1 && s.IndexOf("src") == -1)
    {
        s = s.Replace("<script", "<script nonce=\"" + this._page.GetNonce() + "\"");
    }
    base.Write(s);
}

I used a similar approach in my HtmlTextWriter for AddAttribute to avoid inline javascript: inside href, which would required unsafe-inline for script-src (and preclude the use of a nonce).

public override void AddAttribute(HtmlTextWriterAttribute key, string value)
{
    if (key == HtmlTextWriterAttribute.Href && value != null && value.IndexOf("javascript:") > -1)
    {
        base.AddAttribute(key, "#");
        var newScript = value.Replace("javascript:", "");
        newScript += "; return false;";
        base.AddAttribute(HtmlTextWriterAttribute.Onclick, newScript);
    }
    else
    {
        base.AddAttribute(key, value);
    }
}
Michael
  • 8,362
  • 6
  • 61
  • 88
Notre
  • 1,093
  • 3
  • 13
  • 26
  • 4
    How / Where did you add the nonce back to the request header in your web-forms page? Did you also encounter issues with `WebResource.axd` files that would require unsafe-eval? – display-name Feb 02 '21 at 13:54
  • @rm-code, I added the nonce back in the script tag in the override of the Write method of the the HtmlWriter class. Please see answer above. I didn't run into any issues with WebResource.axd (guess I got lucky) – Notre Jun 13 '22 at 23:47
  • I must be being thick, how did you define _page to access the GetNonce method? I'm using VB.Net and it's not as forgiving as C#. Thanks – JonoT Mar 14 '23 at 00:41
1

You could override __DoPostBack and use your own function instead.

var __original= __doPostBack;
__doPostBack = myFunction();

some more ideas over here: How to intercept any postback in a page? - ASP.NET

NicoTek
  • 1,127
  • 1
  • 14
  • 34
1

If you open up the dev tools in Chrome, you'll likely see a message like

Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self'. Either the 'unsafe-inline' keyword, a hash ('sha256-2NqnatcPqy5jjBXalTpZyJMO/0fUaYUb3ePlviUP4II='), or a nonce ('nonce-...') is required to enable inline execution.

If you look carefully at that message, it's telling you what the hash would be: sha256-2NqnatcPqy5jjBXalTpZyJMO/0fUaYUb3ePlviUP4II=

So if you don't want to go the nonce route, you can instead go the hash route and add

Content-Security-Policy: script-src 'self' 'sha256-2NqnatcPqy5jjBXalTpZyJMO/0fUaYUb3ePlviUP4II=' 'unsafe-eval';

You may have to add unsafe-eval in some cases as well for this to work.

Michael
  • 8,362
  • 6
  • 61
  • 88
codeMonkey
  • 4,134
  • 2
  • 31
  • 50