Using Json.NET
JsonConvert.SerializeObject(new { Property = "<script>alert('o hai');</script>" })
returns
{"Property":"<script>alert('o hai');</script>"}
Is it possible for the value to be escaped by SerializeObject to prevent a hostile script from executing? I'd prefer not to make changes to the object itself.
Edit: Ideally I'd like to integrate the sanitizing into the SerializeObject call without having to process the object before or after SerializeObject.
Edit: The string output from JsonConvert.SerializeObject is assigned to a global variable in a script block, which I believe is where the XSS issue is.
Functionality to achieve this was added in version 4.5.11
This allows you to add various type of escaping to the output.
This is my LinqPad test:
var settings = new JsonSerializerSettings();
settings.StringEscapeHandling = StringEscapeHandling.EscapeHtml;
var output = JsonConvert.SerializeObject(new { Property = "<script>alert('o hai');</script>" }, settings);
Debug.Print(output);
outputs
{"Property":"\u003cscript\u003ealert(\u0027o hai\u0027);\u003c/script\u003e"}
Just as a disclaimer, this isn't a golden bullet to fix xss, but should help you mitigate it a bit given your example.
This may not be ideal but this is my solution (for now, at least):
JsonConvert.SerializeObject(new { Property = "<script>alert('o hai');</script>" }, new HtmlEncodeStringPropertiesConverter());
with a simple JsonConverter that performs HtmlEncode on the value if it is a string
public class HtmlEncodeStringPropertiesConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(string);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteValue(Encoder.HtmlEncode(value.ToString()));
}
}
(Encoder is Microsoft.Security.Application.Encoder from the AntiXSS library)
In .NET Core 3.0, System.Text.Json.JsonSerializer escapes html characters by default.
var text = "<script>alert('o hai');</script>";
var json = System.Text.Json.JsonSerializer.Serialize(new { Property = text });
Console.WriteLine(json);
Output
// .NETCoreApp,Version=v3.0
{"Property":"\u003Cscript\u003Ealert(\u0027o hai\u0027);\u003C/script\u003E"}
Documentation
How to serialize and deserialize JSON in .NET in Serialization behavior section says:
The default encoder escapes non-ASCII characters, HTML-sensitive characters within the ASCII-range, and characters that must be escaped according to the JSON spec.
Please also check the remarks for JavaScriptEncoder.UnsafeRelaxedJsonEscaping because it contains a series of remarks starting with Unlike the Default.
I don't want to escape it
JavaScriptEncoder.UnsafeRelaxedJsonEscaping needs to be set to turn off the default behavior.
var options = new System.Text.Json.JsonSerializerOptions() {
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
var json2 = System.Text.Json.JsonSerializer.Serialize(new { Property = text }, options);
Console.WriteLine(json2);
{"Property":"<script>alert('o hai');</script>"}
UnsafeRelaxedJsonEscaping's Documentation
Remarks for UnsafeRelaxedJsonEscaping:
Unlike the Default encoder, this encoder instance does not escape HTML-sensitive characters such as <, >, &. As a result, it must be used cautiously; for example, it can be used if the output data is within a response whose content-type is known with a charset set to UTF-8.
Unlike the Default encoding, the quotation mark is encoded as " rather than \u0022.
Unlike the Default encoding (which only allows UnicodeRanges.BasicLatin), using this encoder instance allows UnicodeRanges.All to go through unescaped.
Unlike the Default encoder, this encoder instance allows some other characters (such as '+') to go through unescaped and therefore must be used cautiously.
No, JSON.NET is a JSON serializer. It is not a XSS sanitizer. You may take a look at the AntiXSS library from Microsoft. Here's an article on MSDN about its usage (a little outdated but still relevant).
Related
Recently discovered that existing serialization code fails when the object to convert is too large. I know how to increase the maximum length in .NET's JavascriptSerializer. But how do I do the same thing in JsonConvert.SerializeObject?
References to web.config setting:
<jsonSerialization maxJsonLength="50000000"/>
Say that it is only respected by WCF. We aren't using WCF. This is a simple .NET library that requires serialization of objects.
The relevant code is:
public static string Serialize(object value)
{
return JsonConvert.SerializeObject(value, SerializerSettings);
}
This is how I create the settings:
private static JsonSerializerSettings SerializerSettings
{
get
{
var settings = new JsonSerializerSettings();
settings.Converters.Add(new StringEnumConverter());
settings.ContractResolver = new CamelCasePropertyNamesContractResolver();
settings.NullValueHandling = NullValueHandling.Ignore;
return settings;
}
}
I don't see a property I need in JsonSerializerSettings. My object is multilayered but mostly of string types. I convert an uploaded file and store it as a string. The size of that file is the element that I am changing when I see the failire.
Unfortunately the library I am using doesn't log so I can't find the exact error. But the code works with smaller files (<150KB) and fails with larger ones >500KB.
There's a property Path on JToken.
But it escapes the name of object with "[' ']" if the object name contains a "."
XPATH : dir/nested_dir/file.txt
JSON: dir.nested_dir.['file.txt']
Is there some other property that will return the path as an array of string ?
As pointed out here: https://stackoverflow.com/a/19727164/1555435
Use brackets and quotes around your field. For example, if your field is valid.key.with.dot
Refer to it as ['valid.key.with.dot'] and in JsonPath, try
JsonPath.read(jsonString, "$.['valid.key.with.dot']")
Check out this dotNetFiddle
There is not a built-in property that does this, but you can make an extension method that does what you want easily enough:
public static class JsonExtensions
{
public static string[] PathAsArray (this JToken token)
{
return token.AncestorsAndSelf()
.OfType<JProperty>()
.Select(p => p.Name)
.Reverse()
.ToArray();
}
}
Then use it like this:
var pathArray = token.PathAsArray();
Console.WriteLine(string.Join("/", pathArray));
Fiddle: https://dotnetfiddle.net/GOdo7t
Note: the above extension method ignores any JArrays that might be in the path. You will need to make adjustments to the code if you need to handle arrays.
According to Microsoft's documentation, FieldInfo.GetValue(object) will throw a NotSupportedException if:
"A field is marked literal, but the field does not have one of the
accepted literal types."
I have no idea what it means to
"mark a field as literal."
I want to understand this so that I may know how to guard against this exception.
A literal is a const field. Every const field has its value determined at compile time, by initialization from a literal. Look at this code
using System;
using System.Reflection;
public class Program
{
const int literal_int = 5;
readonly int literal_int_two = 5;
const string literal_string = "Fun";
const Random literal_random = null;
int non_literal;
public static void Main()
{
foreach (FieldInfo f in typeof(Program).GetFields(BindingFlags.Instance
| BindingFlags.NonPublic
| BindingFlags.Static
| BindingFlags.FlattenHierarchy))
{
Console.WriteLine("{0} is literal - {1}", f.Name, f.IsLiteral);
try
{
Console.WriteLine("GetValue = {0}", f.GetValue(null));
}
catch{}
}
}
}
Output:
literal_int is literal - True
GetValue = 5
literal_int_two is literal - False
literal_string is literal - True
GetValue = Fun
literal_random is literal - True
GetValue =
non_literal is literal - False
However,
but the field does not have one of the accepted literal types
is open to interpretation and I couldn't find an example for a literal that doesn't have 'one of the accepted literal types' (whatever that means).
By briefly looking at the source code, I couldn't find a relative piece of code to stand for this exception. You should be safe ignoring this clause.
Have a look at the FieldInfo.IsLiteral Property:
Gets a value indicating whether the value is written at compile time
and cannot be changed.
The IsLiteral property is set when the FieldAttributes.Literal
attribute is set. If this attribute is set, the field cannot be
changed and is constant.
The exception stated by
"A field is marked literal, but the field does not have one of the accepted literal types."
... have to be related to FieldInfo instances created by dynamic or custom code (rather than gathered through Reflection), where often certain validations are delayed or omitted.
A FieldInfo object contains meta-data that represents a field, but its class is inheritable and any derived implementation could potentially allow the bad type condition.
I have a .Net Web Service function that can accept one string.
That function will then serialize that string to JSON, but I only want to serialize it if it's value is not "".
I found these instructions:
http://msdn.microsoft.com/en-us/library/aa347792.aspx
[DataContract]
public class MyClass
{
[DataMember (EmitDefaultValue=false)]
public string myValue = ""
}
Unfortunatelly I can not hide the myValue from the serialization because "" is not the .Net default value for a string (how dumb is that!)
One of two option ocurred
On the web service have some kind of attribute that sets the "" to null
Have some condition on the class
I would prefer the 1st because it makes the code cleaner but an opinion would be great.
Thanks
You can explicitly set what the default value is (for the purposes of serialization) using the DefaultValueAttribute class:
[DataContract]
public class MyClass
{
[DataMember (EmitDefaultValue=false)]
[DefaultValue("")]
public string myValue = ""
}
I think you have at least a couple of options here. It's extra work but worth it.
You can encapsulate the string in a reference type. Since reference types are null if not present, that lets you know right away if a string was present or not (because the encapsulating reference type would be either non-null or null, if the string is non-empty or not.)
A final option you have is to add an extra complementary variable (perhaps a boolean) that is set on OnDeserializing/OnDeserialized/OnSerializing/OnSerialized and use this to track whether or not something was actually present on the wire. You might, for example, set this complementary variable to true only when you're actually serializing out a non-empty string and similarly
I have constructed several web services that successfully serialize my .NET types into JSON. However, I'm running into an issue getting the JSON to be fully compatible with the YUI library and the DataTable in particular.
The YUI DataTable can be fully configured using JSON objects. I created the following struct in .NET to represent a given YUI column definition:
public struct YuiColumnDefinition{
public string key;
public string label;
public bool sortable;
public string formatter;
}
The 'formatter' property is how you instruct the YUI table to use a custom javascript function when displaying a given column. The problem is that, since formatter is defined as a String, ASP.NET wraps it's value in double quotes upon serialization, and YUI no longer recognizes the value as a JavaScript token:
JSON YUI expects
[
{key:"1", label:"Order Date", formatter:myCustomJavaScriptFunction, sortable:true},
{key:"2", label:"Customer Name", formatter:null, sortable:false}
]
JSON ASP.NET creates
[
{key:"1", label:"Order Date", formatter:"myCustomJavaScriptFunction", sortable:true},
{key:"2", label:"Customer Name", formatter:null, sortable:false}
]
Anyone have a solution outside of modifying the YUI source code?
Thanks
Change your parsing code.
Think of json as a replacement for xml, you wouldnt put a variable/function in xml. In either, you could easily identify the name or type (say from a list or enum) of the formatter to use. Then your parsing code would know that it should assign a variable/method as the "formatter" property.
Its just incorrect to return an actual variable/function in a callback like that. You could make it work but honestly its not the way to go.
I would do the following...
Change your return json to this.
[
{key:"1", label:"Order Date", formatterName:"myCustomJavaScriptFunction", sortable:true},
{key:"2", label:"Customer Name", formatterName:null, sortable:false}
]
Then in JS, lets assume that json is stored in variable returnedObj
function augmentReturnedObject(returnedObj)
{
// validate that returnObj.formatterName (as a variable) is not undefined
var isValidObj = (window[returnedObj.formatterName] !== undefined);
if (isValidObj)
{
// this will return the actual function / variable, here we are assigning it to returnedObj.formatter
returnedObj.formatter = window[returnedObj.formatterName];
}
else
{
returnedObj.formatter = null;
}
}
You could easily reduce that down to this without much thought
function augmentReturnedObject(returnedObj)
{
var specifiedMethod = window[returnedObj.formatterName];
returnedObj.formatter = (specifiedMethod === undefined) ? null : window[returnedObj.formatterName];
}
So in the end you'd take your json object, and do augmentReturnedObject(returnedObj); and at that point you can pass returnedObj to YUI
As Allen correctly pointed out:
Its just incorrect to return an actual variable/function in a callback like that.
Still, I couldn't find anywhere in the YUI documentation that spells out how to deal with javascript functions returned as strings from either JSON or XML.
Thank goodness for blogs. As pointed out in this one, the "proper" way to register custom javascript formatting functions is by using YAHOO.widget.DataTable.Formatter:
JSON Column Definitions as returned from .ASMX
[
{key:"1", label:"Order Date", formatter:"myCustomJavaScriptFunction1", sortable:true},
{key:"2", label:"Customer Name", formatter:null, sortable:false}
{key:"3", label:"State", formatter:"myCustomJavaScriptFunction2", sortable:false}
]
Javscript to wire up the YUI DataTable
YAHOO.widget.DataTable.Formatter.myCustomJavaScriptFunction1= this.myCustomJavaScriptFunction1;
YAHOO.widget.DataTable.Formatter.myCustomJavaScriptFunction2= this.myCustomJavaScriptFunction2;
function myCustomJavaScriptFunction1(elCell, oRecord, oColumn, oData) {
//do something
}
function myCustomJavaScriptFunction2(elCell, oRecord, oColumn, oData){
//do something
}