random .NET and web development musings

I’m fed up of writing:

<%= Html.Encode(bla) %>

throughout my views. Not only is it messy, but ASP.NET’s default behaviour of “be as insecure as possible” means you have to remember to do this everywhere. In addition to this, it simply uses:

System.Web.HttpUtility.HtmlEncode()

underneath, which isn’t particularly good at preventing XSS.

OpenRasta (a brilliant alternative to MVC which you should be using) has an excellent solution to this problem, by using a custom CSharCodeProvider to help with view compilation.

Below is a simplified version of the code OpenRasta uses, demonstrating how you can get automatic HTML encoding of all code expressions in your views. This also works for WebForms.

It uses an IoC service locator to request an arbitrary IHtmlEncoder. This allows you to use whatever encoding library you like, such as Microsoft AntiXss.

public class AutoHtmlEncodingCSharpCodeProvider : CSharpCodeProvider
{
	public AutoHtmlEncodingCSharpCodeProvider()
	{
	}

	public AutoHtmlEncodingCSharpCodeProvider(IDictionary<string, string> providerOptions) : base(providerOptions)
	{
	}

	public override void GenerateCodeFromStatement(CodeStatement statement, TextWriter writer, CodeGeneratorOptions options)
	{
		var codeExpressionStatement = statement as CodeExpressionStatement;
		if (codeExpressionStatement != null)
		{
			var methodInvokeExpression = codeExpressionStatement.Expression as CodeMethodInvokeExpression;
			if (methodInvokeExpression != null)
			{
				if (methodInvokeExpression.Method.MethodName == "Write" && methodInvokeExpression.Parameters.Count == 1)
				{
					var parameter = methodInvokeExpression.Parameters[0] as CodeSnippetExpression;

					if ((parameter != null) && (!string.IsNullOrEmpty(parameter.Value)))
						parameter.Value = "global::" + GetType().FullName + ".PreProcessObject(this, " + parameter.Value + ")";
				}
			}
		}

		base.GenerateCodeFromStatement(statement, writer, options);
	}

	public static string PreProcessObject(object source, object value)
	{
		if(value is Raw)
			return ((Raw)value).Value;

		var encoder = ServiceLocator.Current.TryGetInstance<IHtmlEncoder>();
		if (encoder != null)
			return encoder.HtmlAttributeEncode(value.ToString());

		return HttpUtility.HtmlAttributeEncode(value.ToString());
	}
}

public class Raw
{
	public string Value { get; set; }

	public static explicit operator Raw(string text)
	{
		return new Raw { Value = text };
	}

	public static implicit operator string(Raw output)
	{
		return output.Value;
	}
}

You need to register this in your Web.config like so:

<system.codedom>
	<compilers>
		<compiler language="c#;cs;csharp" extension=".cs" warningLevel="4" type="MyAssembly.AutoHtmlEncodingCSharpCodeProvider, MyAssembly">
			<providerOption name="CompilerVersion" value="v3.5" />
			<providerOption name="WarnAsError" value="false" />
		</compiler>
	</compilers>
</system.codedom>

Then in your pages, you can do this:

<p><%= "<script>alert('i'm encoded, so i wont popup');</script>" %></p>
<p><%= (Raw)"<strong>i'm bold because im escaped with (Raw)!</strong>" %></p>

Which will be rendered as:

<script>alert(‘i’m encoded, so i wont popup’);</script>
i’m bold because im escaped with (Raw)!

There you go!

Because this code has been inspired by/copied from/a modification of code from OpenRasta, according to its license I must reproduce the copyright notice. If you also wish to use this code, you must do the same.

 Permission is hereby granted, free of charge, to any person obtaining
 a copy of this software and associated documentation files (the
 "Software"), to deal in the Software without restriction, including
 without limitation the rights to use, copy, modify, merge, publish,
 distribute, sublicense, and/or sell copies of the Software, and to
 permit persons to whom the Software is furnished to do so, subject to
 the following conditions:
 
 The above copyright notice and this permission notice shall be
 included in all copies or substantial portions of the Software.
 
 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
NO COMMENTS
Post a comment