我知道有几种方法可以做到这一点,但它们都有一些缺点。有没有一种"公认的"、被认为是最好的方法?
我曾经使用Microsoft.Security.Application.AntiXss.JavaScriptEncode()
,这是伟大的,但AntiXSS已经结束了生命,因为编码器现在包含在。net 4.5。
然而,由于某种原因,System.Web.Security.AntiXss.AntiXssEncoder
不包括JavaScriptEncode
方法。
有System.Web.HttpUtility.JavaScriptStringEncode()
,但它使用黑名单方法进行编码,所以它不太可能像白名单编码器那样好。
我看到一些建议使用System.Web.Script.Serialization.JavaScriptSerializer.Serialize()
,但这只是调用HttpUtility.JavaScriptStringEncode()
。
所以什么是目前最好的接受,白名单方法编码值写成JS变量?
如果您希望在<script>
块中包含JavaScript代码,如:
<script>
var myVariable = '<%=thisIsWrong %>';
</script>
那么在这个上下文中应该使用HttpUtility.JavaScriptStringEncode
。此函数还正确地编码特殊字符,因此,如果要在脚本标记中呈现</script>
,试图关闭HTML脚本标记以准备接受XSS攻击,则将其呈现为:
u003c/scriptu003e
是正确的编码,JavaScript可以将其理解为</script>
,但浏览器不会将其解释为文字结束脚本标记。一些天真编写的JavaScript编码例程不会转换它,因为序列不包含,
"
或'
字符。
如果您不确保关闭脚本标签没有呈现,那么像这样的攻击是可能的。假设这是应用程序的输入:
</script><script>alert(1)</script>
在浏览器中呈现为
<script type="text/javascript">
alert('</script><script>alert(1)</script>');
</script>
,浏览器将解释以alert('</script>
结尾的脚本标记,并简单地执行新脚本标记中的内容。
对于JavaScriptStringEncode
函数,这是安全的,因为它呈现为:
<script type="text/javascript">
alert('u003c/scriptu003eu003cscriptu003ealert(1)u003c/scriptu003e');
</script>
不包含</script>
以便浏览器解释。
有system . web . httutility . javascriptstringencode(),但是它使用了黑名单方法进行编码,所以它不太可能像白名单编码器。
. net中的一些其他编码函数确实使用了黑名单方法,但是在我自己的测试中,JavaScriptStringEncode
似乎是足够的。
OWASP对JavaScript的推荐值是
除字母数字字符外,转义小于的所有字符256与xHH格式,以防止切换出数据值
以便您可以轻松地编写自己的代码来遵守此规则。
注意,如果你想在属性标签中包含代码:
<a href="http://example.com" onclick="alert('<%=wrong>')">Click</a>
则OWASP方法意味着您不必太关心HTML编码(因为实际上没有输出具有特殊含义的HTML字符)。如果没有(例如使用JavaScriptScriptEncode
),您还需要HTML编码。
说了这么多,一个更安全的方法是接近它,就像我的问题的答案在外部JavaScript文件中插入动态值的安全方式。使用data-
属性将动态值放在DOM中(在HTML中),然后使用JavaScript提取这些值。
如果不将不安全的字符串放在可以解释为脚本的地方(例如在<script>
标记之间),则永远不必担心XSS。我知道不是所有东西都可以很容易地重新设计,但如果我是您,我会尝试将数据(HTML编码)放入隐藏的HTML元素中并在JavaScript中读取该元素的值,或者以JSON格式对变量进行AJAX请求。
Encoder.cs的源代码是可用的,并且"受Microsoft许可许可"约束"
public static string JavaScriptEncode(string input, bool emitQuotes)
{
// Input validation: empty or null string condition
if (string.IsNullOrEmpty(input))
{
return emitQuotes ? JavaScriptEmptyString : string.Empty;
}
// Use a new char array.
int outputLength = 0;
int inputLength = input.Length;
char[] returnMe = new char[inputLength * 8]; // worst case length scenario
// First step is to start the encoding with an apostrophe if flag is true.
if (emitQuotes)
{
returnMe[outputLength++] = ''';
}
for (int i = 0; i < inputLength; i++)
{
int currentCharacterAsInteger = input[i];
char currentCharacter = input[i];
if (SafeListCodes[currentCharacterAsInteger] != null || currentCharacterAsInteger == 92 || (currentCharacterAsInteger >= 123 && currentCharacterAsInteger <= 127))
{
// character needs to be encoded
if (currentCharacterAsInteger >= 127)
{
returnMe[outputLength++] = '\';
returnMe[outputLength++] = 'u';
string hex = ((int)currentCharacter).ToString("x", CultureInfo.InvariantCulture).PadLeft(4, '0');
returnMe[outputLength++] = hex[0];
returnMe[outputLength++] = hex[1];
returnMe[outputLength++] = hex[2];
returnMe[outputLength++] = hex[3];
}
else
{
returnMe[outputLength++] = '\';
returnMe[outputLength++] = 'x';
string hex = ((int)currentCharacter).ToString("x", CultureInfo.InvariantCulture).PadLeft(2, '0');
returnMe[outputLength++] = hex[0];
returnMe[outputLength++] = hex[1];
}
}
else
{
// character does not need encoding
returnMe[outputLength++] = input[i];
}
}
// Last step is to end the encoding with an apostrophe if flag is true.
if (emitQuotes)
{
returnMe[outputLength++] = ''';
}
return new string(returnMe, 0, outputLength);
}