在 ASP.NET 4.5 中,我应该如何编码字符串以用作 JavaScript 变量,以防止 XSS 攻击

In ASP.NET 4.5, how should I encode a string to be used as a JavaScript variable, to prevent XSS attacks

我知道有几种方法可以做到这一点,但它们都有一些缺点。有没有 "accepted" 被认为是最好的方法?

我曾经使用过很棒的 Microsoft.Security.Application.AntiXss.JavaScriptEncode(),但是 AntiXSS 已经过时了,因为从 4.5 开始编码器现在包含在 .NET 中。

但是,由于某些原因,System.Web.Security.AntiXss.AntiXssEncoder 不包括 JavaScriptEncode 方法。

System.Web.HttpUtility.JavaScriptStringEncode(),但它使用黑名单编码方式,所以不太可能像白名单编码器那样好。

而且我看到了一些使用 System.Web.Script.Serialization.JavaScriptSerializer.Serialize() 的建议,但这只是调用 HttpUtility.JavaScriptStringEncode()

那么目前接受度最高的白名单方法是什么,用于将写出的值编码为 JS 变量?

Encoder.cs 的源代码可用并且 "subject to the Microsoft Permissive License"

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

如果你不把不安全的字符串放在一个可以被解释为脚本的地方(比如 <script> 标签之间),你永远不必担心 XSS。我知道不是所有的东西都可以轻易地重新设计,但如果我是你,我会尝试将数据(HTML 编码)放入一个隐藏的 HTML 元素中并读取该元素的值 JavaScript,或者对 JSON 格式的变量进行 AJAX 请求。

如果您希望在 <script> 块中包含 JavaScript 代码,如下所示:

<script>
var myVariable = '<%=thisIsWrong %>';
</script>

那么在这种情况下应该使用。此函数还可以正确编码特殊字符,因此如果要在脚本标记中呈现 </script> 以试图关闭准备好进行 XSS 攻击的 HTML 脚本标记,它将呈现为:

\u003c/script\u003e

这是 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/script\u003e\u003cscript\u003ealert(1)\u003c/script\u003e');

</script>

其中不包含 </script> 供浏览器解释。

There's System.Web.HttpUtility.JavaScriptStringEncode(), but it uses a blacklist method for encoding, so it's unlikely to be as good as a whitelist encoder.

Some of the other encoding functions in .NET 确实使用黑名单方法,但是在我自己的测试中 JavaScriptStringEncode 似乎足够了。

OWASP recommendation for JavaScript

Except for alphanumeric characters, escape all characters less than 256 with the \xHH format to prevent switching out of the data value into the script context or into another attribute.

因此您可以轻松编写自己的代码来遵守此规定。

请注意,如果您想在属性标签中包含代码:

<a href="http://example.com" onclick="alert('<%=wrong>')">Click</a>

那么 OWASP 方法意味着您也不必处理 HTML 编码(因为实际上没有输出具有特殊含义的 HTML 字符)。如果没有(例如 JavaScriptScriptEncode),您也需要 HTML 编码。

说了这么多,更安全的方法是像我的问题的答案一样处理它 Secure way of inserting dynamic values in external JavaScript files。使用 data- 属性将动态值放在 DOM 中(在 HTML 中),然后使用 JavaScript 提取这些值。这将避免所有 JavaScript 编码问题。