Customize web form script generation

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?


Solution 1:

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);
    }
}

Solution 2:

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