Encoding notes around MvcHtmlString Class and <%: %>/@Model.<…> code syntax

The MvcHtmlString class:
Represents an HTML-encoded string that should not be encoded again. This class contains some interesting methods from security perspective like:

Create Creates an HTML-encoded string using the specified text value.
ToHtmlString Returns an HTML-encoded string that represents the current object.

The MvcHtmlString.Create method:
The Create method of the MvcHtmlString class in System.Web.Mvc namespace creates an HTML-encoded string using the specified text value in the parameter.

The method signature for the Create method is:

image

The <%: %> code syntax for the MVC ASPX engine:
<%: %> is a new syntax for HTML Encoding output in ASP.NET 4 and ASP.NET MVC 2. This syntax renders output like <%= %> blocks do, but at the same time, also automatically HTML encodes it before rendering.

Considering that ASP.NET 4 introduced a new IHtmlString interface (along with a concrete implementation: HtmlString) that a developer can implement on types to indicate that its value is already properly encoded for displaying as HTML, and that therefore the value should not be HTML-encoded again, a possibility of Double Encoding arises when <%:%> and instances of IHtmlString are used together.

To avoid this, <%: %> code-nugget syntax checks for the presence of the IHtmlString interface implementation and will not HTML encode the output of the code expression if its value implements this interface.

The @model.<…> syntax for the MVC Razor engine:
This syntax automatically encodes value that is represented by <…>. Only exceptions will be when <…> is of MvcHtmlString type.

Possible permutations:

<%: MvcHtmlString %> <%:%> will not encode the MvcHtmlSTring to avoid double encoding as this is assumed to be already encoded
<%: NormalString %> <%:%> will Html encode the NormalString before rendering the same.
@model. <somethingNonMvcHtmlString> This will be HTML encoded and rendered.
@model. <somethingMvcHtmlString> This will be not get HTML encoded as this is assumed to be already encoded somethingMvcHtmlString is already assumed to be encoded.

So why document these when these details are already available in MSDN/TechNet/web?
Because MvcHtmlString.Create method is not generating an HTML-encoded string as documented. And when these strings gets rendered within the <%:%> or @model.<…> syntax, they are not encoded and as a result a non-encoded string gets rendered into the browser.

 

Demo:
A small MVC 3 application is created just to check the workings of the Create method of MvcHtmlString class.
The application takes a user string and renders it into the browser. It provides us an option to render as a normal string or as a HtmlMvcString. The UI looks as below:

image

Once I click on Test, the content entered is rendered as below:

image

The view code that handles this rendering part is:

image The strUserString is a normal string object.
The data model to which this view is bound has the following code:

image

Now to check the encoding behaviour, we pass some script values and try to render it in the browser.
Behaviour for Normal String:

image

This text is encoded and rendered as below:

image

Now the same text is rendered as an MvcHtmlString type:

image

The output looks as below:

image

The MvcHtmlString.Create method did not encode the string as documented. This needs to be taken care of during code reviews particularly when values of type MvcHtmlString is rendered within the <%: %> code nugget expressions or in @Model.<…> in ASP.NET 4 and ASP.NET MVC 2 onwards.
<%: %> and @Model.<…> will NOT automatically encode an object of type MvcHtmlString.

Some points to keep in mind when reviewing ASP.NET 4 and ASP.NET MVC onwards:
1. Check for <%: %> and MvcHtmlString usage.
2. Check for @Model.XXXX and MvcHtmlString usage
3. If found, check the type of value that is being rendered between the <%: %> code nugget expressions. Same for @Model.XXXX
4. If the type is normal string, <%: %>/@Model.XXXX will HTML encode the value. But if the type is MvcHtmlString then the value rendered within <%: %> will not be encoded. So if you see <%: %> in code do not assume that every value within it is encoded by default.

References:
1. http://weblogs.asp.net/scottgu/archive/2010/04/06/new-lt-gt-syntax-for-html-encoding-output-in-asp-net-4-and-asp-net-mvc-2.aspx
2. http://stevesmithblog.com/blog/default-encoding-of-strings-in-asp-net-mvc-2/

Advertisements

Secure File upload best practices

Secure file uploads are critical to applications that support this feature.

It is important that the files adhere to the best practices of secure file upload listed below:

  1. DO NOT RELY on client side validation. Whatever you do on the client side ALL the following checks MUST be implemented on the server side.
  2. Check for the SIZE OF THE FILE UPLOADED – absolutely critical must do. If you need very large files to be uploaded go back to the white board and see what alternatives can be explored. Generally any file upload should not be more than 4 MB.
  3. Ensure that only a set of known good file extensions/types are allowed to be uploaded– USE A WHITELIST of allowed extensions. DO NOT use a list of bad extensions to be avoided – NO BLACKLIST`ing.
  4. Generate the file names; DO NOT USE/AVOID files names that are provided by the end user.
  5. Upload the file to a location decided by the application WITHOUT any end user influence. DO NOT let the user have a say in where the file is uploaded.
  6. Upload in a location that is a NON SYSTEM drive.
  7. AVOID uploading the file to the WEB ROOT.
  8. ALWAYS DO a Virus scan on the uploaded files.
  9. Run a MIME check on the allowed MIME types. Note this can be manipulated by any user and by no means should be the only mechanism to secure a file upload. <<Adding some more to the original list>>
  10. Check “Magic Numbers” rather than just checking the file extensions. Check what a Magic Number is here.
  11. Pay attention to the decompression of compressed files.
  12. Ensure that the file upload is Audited.
  13. Refrain from overwriting of a uploaded file.
  14. Do not allow execute permissions on folder where the file is uploaded.

The validateFileToUpload class implements the three checks below using ASP.Net:

  • CHECK #1   Check for the file size
  • CHECK #2   Check for the file extension and map it to a white list of allowed extensions
  • CHECK #3   MIME Type Validation
public void validateFileToUpload(FileUpload objFile)
{
int MAX_FILE_SIZE = (4 * 1024 * 1024); //5 MB file size limit
//————————————————————
//CHECK #1
//You can set the httpRunTime maxRequestLength to the required
//file size in the web.config file too
//————————————————————
int fileSize = objFile.PostedFile.ContentLength;
if (fileSize > MAX_FILE_SIZE)
{
returnMessage = “File size exceeds the safe file size limit.  Reduce the file size.”;
return returnMessage;
}
//—————————————
//CHECK #2
//Check with a white list of extension
//—————————————
string chosenFileExtension = System.IO.Path.GetExtension(objFile.FileName);
string[] allowedExtensions = { “.doc”, “.xls”, “.ppt”, “.pptx”, “.txt” };
if (!allowedExtensions.Contains(chosenFileExtension))
{
returnMessage = “Upload Failed: Files with ” + chosenFileExtension + ” extensions are not allowed.”;
return returnMessage;
}
//—————————————
//CHECK #3
//Check with a white list of MIME types
//—————————————
string[] allowedMimeTypes = { “text/plain”, “text/xml” };
string chosenFileMiMeType = objFile.PostedFile.ContentType;
if (!allowedMimeTypes.Contains(chosenFileMiMeType))
{
returnMessage = “Upload Failed: Files with ” + chosenFileMiMeType + ” MIME types are not allowed.”;
return returnMessage;
}
There are some additional checks that needs to be kept in mind in addition to the 9 checks listed above like double extension files, filename checks for files coming from different operating systems (#4 kind of handles this though).Learnt to keep these checks in mind in a VERY HARD way today :’((

ASP.Net validateRequest page attribute should never be the single point of check against scripting inputs

 

ValidateRequest is a good feature that ASP.Net provides to help developers to code well to protect their applications against the menace of the all prevalent web application security issue – XSS.

ValidateRequest is a page attribute that is intended to get or set a value that determines whether ASP.NET examines input from the browser for dangerous values. If that is enabled and the input in the browser contains any “dangerous” tags – ones that can be used to execute scripts/write code that the browser will execute then an error is generated: clip_image002

The above error is generated when the url contains the script segment as a query string parameter. The query string looks something like this for test purpose:

<asp:MenuItem NavigateUrl="~/About.aspx?h=123&y=alert(‘sp@wned’);” Text=”About”/>

So I just wanted to check the extent of usability of validateRequest. In the “About.aspx” page, I have a script snippet that parses the url and evaluates the querystring parameters to arrange the display of the page. If I want to bypass the validateRequest limitation imposed, I need to make my querystring parameter free of any script tags. Considering that the script code in my page uses a java script Eval, I changed the query string parameter as below and validateRequest did not complain:

clip_image004

So it is not a very good idea to put too much trust on validateRequest to do the magic of input validation for an application. It can be easily and effectively bypassed.

HTTP 500 Internal Server Error – While deploying a website created in ASP.Net and .Net framework 4.0 in IIS 7.5.

 

I created a website in ASP.Net and published it to a location in the hard drive within the default projects folder of VS2010 (C:\Users\\Documents\Visual Studio 2010\Projects\\PrecompiledWeb).

This was a simple website project, nothing fancy, that looked like this:

clip_image001

I created an Application in IIS 7.5 under local host (Root -> Sites -> Default Website) and provided the physical path of the website to the location of my precompiled bit. By default the AppPool used is the DefaultAppPool that by default runs on the AppPoolIdentity. But when I try to access the site I got the following:

clip_image003

For me this error kept cropping up and the reason was that the account on which the app pool was running did not have access to the physical location of the website files. You can check the account by going to Application pool node -> Select the app pool -> click Advanced settings on the left menu and scroll down. Ensure that you provide read access to the physical location. Once you do the error goes away.