问题
I know of several ways to do this, but they all have some downside. Is there an "accepted" way of doing it, that is considered the best?
I used to use the Microsoft.Security.Application.AntiXss.JavaScriptEncode()
which is great, but AntiXSS has been end-of-lifed because the encoder is now included in .NET as of 4.5.
However, for some reason, System.Web.Security.AntiXss.AntiXssEncoder
doesn't include the JavaScriptEncode
method.
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.
And I've seen some recommendations to use System.Web.Script.Serialization.JavaScriptSerializer.Serialize()
, but that just calls HttpUtility.JavaScriptStringEncode()
.
So what's currently the best accepted, whitelist method for encoding values written out as JS variables?
回答1:
If you wish to include JavaScript code within a <script>
block like this:
<script>
var myVariable = '<%=thisIsWrong %>';
</script>
Then in this context HttpUtility.JavaScriptStringEncode should be used. This function also correctly encodes special characters, so if </script>
was to be rendered in a script tag in an attempt to close the HTML script tag ready for an XSS attack, it would be rendered as:
\u003c/script\u003e
which is the correct encoding for JavaScript to understand it as </script>
, but without the browser interpreting it as a literal closing script tag. Some naively written JavaScript encoding routines would not convert this because the sequence does not contain \
, "
or '
characters.
If you don't make sure that closing script tags are not rendered, then an attack like so is possible. Imagine this is the input to your application:
</script><script>alert(1)</script>
which the renders in the browser as
<script type="text/javascript">
alert('</script><script>alert(1)</script>');
</script>
and the browser will interpret the script tag ending at alert('</script>
and simply execute what is in the new script tag.
With the JavaScriptStringEncode
function this is safe as it is rendered as:
<script type="text/javascript">
alert('\u003c/script\u003e\u003cscript\u003ealert(1)\u003c/script\u003e');
</script>
which does not contain </script>
for the browser to interpret.
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 do use blacklist methods, however in my own testing JavaScriptStringEncode
seems to be ample.
The OWASP recommendation for JavaScript is
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.
so you could easily write your own to comply with this.
Note that if you want to include code in attribute tags:
<a href="http://example.com" onclick="alert('<%=wrong>')">Click</a>
then the OWASP method means you don't have to take care of HTML encoding too (because no HTML characters with special meaning are actually output). Without (e.g. with JavaScriptScriptEncode
) you need to HTML encode too.
Having said all this, a safer way is to approach it like the answer to my question Secure way of inserting dynamic values in external JavaScript files. Use data-
attributes to place the dynamic values in the DOM (in HTML) and then use JavaScript to extract these values. This will prevent all JavaScript encoding headaches.
回答2:
The source code for Encoder.cs is available and "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);
}
回答3:
If you don't put an unsafe string into a place where it can be interpreted as script (like between <script>
tags), you never have to worry about XSS. I know not everything can be redesigned easily, but if I were you, I'd try to either put the data (HTML Encoded) into a hidden HTML element and read the value of that element in JavaScript, or do an AJAX request for the variable in JSON format.
来源:https://stackoverflow.com/questions/30606628/in-asp-net-4-5-how-should-i-encode-a-string-to-be-used-as-a-javascript-variable