好吧,是的,现在是 2020 年,但不要笑。我正在尝试更新一些 ASP.NET Web 表单。我的目标是锁定它们,通过应用更具限制性的内容安全策略 (CSP) 使它们更加安全。为此,我使用的是随机数,而不是允许
unsafe-inline
编写脚本。
对于“简单”的网络表单,它工作正常。但是,每当有导致回发的 ASP 控件时,我都会遇到问题。当我查看页面源代码时,我看到这样的东西:
<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();
}
}
和
<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>
我相信这段代码是由一些 MS web 表单代码生成的。问题是这些脚本元素都没有随机数,我想提供它,所以它违反了我的 CSP(不包括
unsafe-inline
)。有什么我可以覆盖以自定义此行为吗?
一些有问题的代码是在
RenderPostBackScript
类中的私有方法(例如 Page
)中定义的,该方法又被内部方法调用。因此,除非我替换一大堆代码,否则无法以正常方式覆盖它。作为替代方案,我在我的页面中覆盖了CreateHtmlTextWriter
:
protected override System.Web.UI.HtmlTextWriter CreateHtmlTextWriter(TextWriter tw)
{
return new HtmlTextWriter(tw, this);
}
然后在我的
HtmlWriter
课上,我这样做:
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);
}
我在我的
HtmlTextWriter
中为AddAttribute
使用了类似的方法来避免内联javascript:
在href
内,这需要unsafe-inline
用于script-src
(并排除使用随机数)。
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);
}
}
您可以重写
__DoPostBack
并使用您自己的函数。
var __original= __doPostBack;
__doPostBack = myFunction();
这里有更多想法: 如何拦截页面中的任何回发? -ASP.NET
如果你在 Chrome 中打开开发工具,你可能会看到这样的消息
拒绝执行内联脚本,因为它违反了以下内容安全策略指令:
。"script-src 'self'
关键字、散列 ('unsafe-inline'
) 或随机数 ('sha256-2NqnatcPqy5jjBXalTpZyJMO/0fUaYUb3ePlviUP4II='
) 是启用内联执行所必需的。'nonce-...'
如果您仔细查看该消息,它会告诉您哈希值是什么:
sha256-2NqnatcPqy5jjBXalTpZyJMO/0fUaYUb3ePlviUP4II=
所以如果你不想走 nonce 路线,你可以改走哈希路线并添加
Content-Security-Policy: script-src 'self' 'sha256-2NqnatcPqy5jjBXalTpZyJMO/0fUaYUb3ePlviUP4II=' 'unsafe-eval';
在某些情况下,您可能还必须添加
unsafe-eval
才能起作用。