Showing posts with label ASP.Net MVC. Show all posts
Showing posts with label ASP.Net MVC. Show all posts

Monday, 28 October 2013

Html Encoding in ASP.Net MVC (and avoiding XSS)

Stop using <%= %> syntax in your ASP.Net sites...

Cross-site script injection (XSS) attacks are the most common security issues associated with web applications. Guarding against cross-site scripting attacks is relatively easy; just make sure that rendered output is HTML encoded within a page. This helps ensures that any content that might have been input/modified by an end-user cannot be output back onto a page containing <script> elements.


Ways of encoding with ASP.Net

ASP.Net applications (especially those using ASP.Net MVC) often rely on using <%= %> code-nugget expressions to render output. You can use the Server.HtmlEncode() or HttpUtility.Encode() helper methods within these expressions to HTML encode the output before it is rendered; this however can be verbose and easily forgotten!
A more convenient way was introduced in ASP.Net 4 (MVC 2), and that's the <%: %> syntax, this takes care of the encoding for you. If you're using the Razor engine introduced in ASP.Net MVC 3 then this also takes care of the encoding for you.
<div id="old">
<%= HtmlUtility.Encode(Model.Message) %>
</div>

<div id="new">
<%: Model.Message %>
</div>

<div id="newRazor">
<%: Model.Message %>
</div>

Avoiding double encoding

While HTML encoding content is often a good best practice, there are times when the content you are outputting is meant to be HTML or is already encoded – in which case you don’t want to HTML encode it again. The smart guys on the ASP.Net team decided to introduce a new IHtmlString interface (along with a concrete implementation: HtmlString) that you can implement on types to indicate that its value is already properly encoded (or otherwise examined) for displaying as HTML, and that therefore the value should not be HTML-encoded again. The <%: %> and Razor syntax checks for the presence of the IHtmlString interface and will not HTML encode the output of the code expression if its value implements this interface. 

For example, with ASP.Net MVC 2: the Html.TextBox() helper method returns markup like <input type="text" />. With ASP.Net MVC 2 these helper methods now by default return HtmlString types – which indicates that the returned string content is safe for rendering and should not be encoded by <%: %> syntax. This allows you to use these methods within both <%= %> syntax blocks, within <%: %> syntax blocks and within Razor engine syntax blocks. In both cases above the HTML content returned from the helper method will be rendered to the client as HTML – the <%: %> and Razor engine syntax will avoid double-encoding it.

Always use the <%: %&gt or Razor engine syntax to encode your HTML output; there's no reason to use the <%= %> syntax anymore!

Wednesday, 30 January 2013

Don't assume anything about the lifetime of ActionFilters in MVC3

In previous versions of ASP.NET MVC, action filters were created per request except in a few cases. This behavior was never a guaranteed behavior but merely an implementation detail and the contract for filters was to consider them stateless. In ASP.NET MVC 3, filters are cached more aggressively. Therefore, any custom action filters which improperly store instance state might be broken.

Taken from the 'breaking changes' section of the release notes for MVC3.

The statement is clear enough to someone feeling the sting still from a recent live issue, but I've outlined a bad and good example of custom action filters below. Basically, if you find yourself needing to store state for the lifetime of a request, use the filterContext.HttpContext.Items collection.


Bad custom action filter

public class BadActionFilter : ActionFilterAttribute
{
    private string _userState;

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        _userState = someSortOfDataUniqueToUser();
    }

    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        // using _userState here is a bad idea!
    }
}

Good custom action filter

public class GoodActionFilter : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var userState = someSortOfDataUniqueToUser();
        filterContext.HttpContext.Items["userState"] = userState;
    }

    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        var userState = filterContext.HttpContext.Items["userState"];
        // using userState here is perfectly fine!
    }
}

Sunday, 13 January 2013

PaaS offerings and AppHarbor

This may sound a little obvious, but writing the code for web apps is often the easy part for developers - because that's what developers are good at. I believe developers should be free to focus on writing the code and adding business value. In reality, the developers often get involved with the headaches that come from deploying, load balancing, scaling and monitoring the apps. But with more and more Platform as a Service (PaaS) offerings popping up, it seems like our lives as developers are beginning to get a little easier.

Our technical director at work (Adam Bird) made me aware of AppHarbor.com around 12 months ago. Adam likes Ruby and had been using Heroku, which was a big hit with Ruby developers because it was an easy-to-use PaaS offering. AppHarbor has been called the 'Heroku for .Net'. It runs on Amazon's AWS infrastructure and is designed to take the pain out of quickly and safely deploying, scaling and managing any .NET web application. Appharbour also offers an impressive add-on feature catalog, allowing developers to easily integrate and configure a wide array of database, queuing, monitoring, indexing and email services with their applications.

Integration with AppHarbor can be achieved using various repository hosting services (e.g. BitBucket, GitHub and Codeplex). Developers can use Git, Mercurial or TFS when deploying code to AppHarbor; integrating deployment in the developer's workflow by letting developers use the tools they love.

My experiences

I've been really impressed with what AppHarbor are offering and was surprised at how easy (and low cost) it was to get an application I had running on an virtual machine elsewhere ported over. One of the biggest selling points for me is integration with GitHub and BitBucket. If you are migrating apps over you need to bear in mind certain limitations too (see below). AppHarbor offer a free tier for you to play around with, and a lot of the add-ons do too. I'd strongly recommend you give it a try.

I've listed what I liked most below, followed by some potential issues/warnings I came across.

Likes...

  • Low start-up costs
  • Your first deploy can take just a manner of minutes
  • Automatically deploy code from GitHub (or any of the other supported repository hosting services)
  • Support for compiling and running project tests; if the tests fail the app isn't deployed
  • Reverting to a previous app version is easily achieved through the control panel
  • Ability to integrate popular application monitoring add-ons (like New Relic) very easily

Potential issues/warnings...

  • If you are sharing forms auth cookies across apps hosted elsewhere you'll need to implement your own version of form auth and not rely on the .Net libraries (see AppHarbor blog)
  • File system access should be avoided due to the way Appharbor scales your app - put files in a shared data store
  • AppHarbor building and running tests is not going to work for larger projects with complicated tests
  • During my time playing around I've had a handful of intermittent yellow screens of death appear whilst using their control panel (not my own app) - which has made me question their stability

Tuesday, 7 December 2010

Padding Oracle Crypto Attack (Update Released)

In case you missed it, you should by now all have updated your servers with the windows update to fix the Padding Oracle Crypto Attack:

Points worth noting:

  • You'll need to apply th fix to all your servers in the web farm as the encryption/decryption mechanism has changed.
  • Your users will have to log in again - forms auth tickets issued by your app prior to the update will no longer be valid.

Catch-All MVC Route

A weird scenario may come up where you want all requests to an application routed to just one controller action in an ASP.Net MVC application, here's the catch-all route you'll need :)

public static void RegisterRoutes(RouteCollection routes)
{
   routes.MapRoute(
      "Default", // Route name
      "{*catchall}", // URL with parameters
      new { controller = "Home", action = "Index" } // Parameter defaults
   );
}

Saturday, 23 October 2010

A potentially dangerous Request.Form value was detected from the client

You'll get this error from the .Net framework when it detects potentially harmful content in a web request submission. Want to disable this validation? Read on...

For normal ASP.Net Web forms apps you can disable request validation by setting validateRequest=false in the page directive (top of your aspx page) or in the configuration section.

For ASP.Net MVC apps add the [ValidateInput(false)] attribute to the appropriate controller action.

Be careful when turning off this value as you are making your site vulnerable by accepting potentially harmful input. You should explicitly validate your input in your code if you disable the built in .Net request validation.

Thursday, 16 September 2010

Padding Oracle Crypto Attack Affects Millions of ASP.NET Apps

So I saw this headline on Slashdot IT and it immediately made me pay attention. So I loaded up the ASP.Net security vulnerability article detailing the exploit and started reading.

In short, it totally destroys ASP.NET security

It soon became obvious that the article was a little bit sensationalist to say the least (see quote above). Basically the exploit is low-risk. You should only be worried if your applications aren't communicating over SSL and if you have put sensitive information in you ASP.Net application's cookies and are trusting the cookie data blindly. Sensitive data should all be stored server side - but you knew that anyway!

Needless to say, I've not wrote any code lately that checks for an SuperUser=true value in the cookie!!!

Thursday, 24 June 2010

Multiple types were found that match the controller

I'm starting to make use of the new Areas feature in my MVC 2 applications. I've found it great for separating my applications into distinct sections rather than staring at a massive list of controllers/views all bundled into one folder. I came across an issue when I created a controller with the same name as a controller within a different or the default area:

Multiple types were found that match the controller named XXXXXXXX...

I thought it was a little cheap of Microsoft requiring you to give your controllers unique names across all areas. I was relieved to find out giving the same name within different areas is perfectly OK, you just need to specify the full namespace for the controller when setting up the route for it, like so:

public override void RegisterArea(AreaRegistrationContext context)
{
 context.MapRoute(
  "Members_default",
  "Members/{controller}/{action}/{id}",
  new { controller = "Home", action = "Index", id = UrlParameter.Optional },
  new string[] { "MyWebApp.Areas.Members.Controllers" }
 );
}

I knew Microsoft wouldn't let me down ;)

Friday, 18 June 2010

Mock ControllerContexts in ASP.Net MVC

After performing an upgrade to MVC 2 for a web application last week, I had a number of tests failing. This gave me an opportunity to refactor some controller tests and create a base class for my controller test classes. This new base class is straight forward and allows me to mock HttpRequestBase and HttpSessionStateBase easily on the mock ControllerContext - it also has an overload for mocking AJAX requests too. I expect this class to grow in time but it serves my needs for now:

public class TestController
{
 public static ControllerContext GetMockControllerContext()
 {
  return GetMockControllerContext("GET", false, null, null);
 }

 public static ControllerContext GetMockControllerContext(string httpMethod, bool isAjaxRequest)
 {
  return GetMockControllerContext(httpMethod, isAjaxRequest, null, null);
 }

 public static ControllerContext GetMockControllerContext(Mock httpRequestBase, Mock httpSessionStateBase)
 {
  return GetMockControllerContext("GET", false, httpRequestBase, httpSessionStateBase);
 }

 public static ControllerContext GetMockControllerContext(string httpMethod, bool isAjaxRequest, Mock httpRequestBase, Mock httpSessionStateBase)
 {
  var headerCollection = new NameValueCollection();

  // Add header value for ajax requests
  if (isAjaxRequest) headerCollection.Add("X-Requested-With", "XMLHttpRequest");

  if (httpRequestBase == null)
  {
   httpRequestBase = new Mock();
   httpRequestBase.Setup(r => r.HttpMethod).Returns(httpMethod);
   httpRequestBase.Setup(r => r.Headers).Returns(headerCollection);
   httpRequestBase.Setup(r => r.Form).Returns(new NameValueCollection());
   httpRequestBase.Setup(r => r.QueryString).Returns(new NameValueCollection());
  }

  if (httpSessionStateBase == null)
  {
   httpSessionStateBase = new Mock();
  }

  var mockHttpContext = new Mock();
  mockHttpContext.Setup(c => c.Request).Returns(httpRequestBase.Object);
  mockHttpContext.SetupGet(c => c.Session).Returns(httpSessionStateBase.Object);

  return new ControllerContext(mockHttpContext.Object, new RouteData(), new Mock().Object);
 }
}

Friday, 11 June 2010

Time to Upgrade to ASP.Net MVC Web Apps to MVC 2?

Wondering if you should be upgrading to ASP.Net MVC 2? It is worth some serious thought as there's some powerful new features, the upgrade process is nice and simple, plus there aren't many breaking code changes. Why not take a peek at what's new in ASP.Net MVC 2 and I bet you'll see at least a handful of features that'll get you itching to upgrade ;)

If you decide to upgrade your web application then review the MVC 2 upgrade notes. They include instructions for upgrading manually or you can choose to use the upgrade tool. I've used the upgrade tool and it seems to work great for most people - but not me, I had two problems :( After the conversion completed I compiled my project and got an error:

It is an error to use a section registered as allowDefinition='MachineToApplication' ...

...I managed to fix this without pulling my hair out though. The only other issue I had was around 10% of my project tests suddenly started failing; a result of some change to the controller context processing (this resulted in me improving my controller tests by creating a base TestController). Again this was a nuisance rather than a show stopper - the upgrade was worth the effort :)

Wednesday, 9 June 2010

ASP.Net allowDefinition='MachineToApplication' Error

I've come across this problem a number of times now and it was beginning to annoy me. A few times when building projects on continuous integration servers and today when upgrading my MVC web app to MVC 2 I hit the following error:

It is an error to use a section registered as allowDefinition='MachineToApplication' beyond application level This error can be caused by a virtual directory not being configured as an application in IIS.

Everytime I failed to figure out what the problem was and instead I had to admit defeat and use a work-around :(

Well the error message is quite helpful for some people getting this error at runtime and tells them how to fix the problem - configure the web apps virtual directory to be an application. However, I'd been stumbling upon this delightful error when compiling projects - not at runtime!

Luckily I had a light bulb moment... the compiler was not referring to my main web.config for the application, but instead was moaning about a web.config within a sub directory of my application (a backup of the project). Deleting the backup sub directory solved my problems!

I got 99 problems but my web.config ain't one!

Tuesday, 8 June 2010

Handling Unexpected Exceptions in ASP.Net MVC

There are way too many yellow screens of death out there on the net for my liking. But rather than trying to bullet proof your MVC web apps by putting try catch blocks in all your MVC actions to handle unexpected exceptions, there is a far easier way to do it and it keeps your code nice and DRY - see my code snippet below:

public class BaseController : Controller
{
 
 protected override void OnException(ExceptionContext filterContext)
 {
  // Record the error
  Log.Error("Unexpected error in controller", filterContext.Exception);

  // Uses customErrors element from web.config
  if (filterContext.HttpContext.IsCustomErrorEnabled)
  {
   // Set exception as handled and status code 500
   filterContext.ExceptionHandled = true;
   filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;

   this.View("Error").ExecuteResult(this.ControllerContext);
  }
 }

}

I override the OnException method within a base controller for my other controllers to inherit from. Any unhandled exceptions in my action methods will hit this and I can gracefully log the exceptions and send the user to a helpful error view (as long as customErrors for the app are set to true) - it's worth noting I also set an appropriate HTTP status code :)

Wednesday, 21 April 2010

Compile Your ASP.Net MVC Views

A big problem with ASP.Net MVC views is the amount of inline code you put in your views that could potentially result in errors that aren't apparent until runtime. Microsoft did spot this as a problem and kindly introduced a way of checking your views for compilation errors when you compile your source, here's how to enable the feature:

Open up your project file for editing either in Visual Studio by right clicking the project file and choosing 'Edit Project File' or just use your favourite text editor. At the top of the project file you'll see a PropertyGroup XML element with a child element of MvcBuildViews, change the value of this to true...


 true

This results in a build event at the bottom of your project file getting called...


 

That's it! You now are that little bit more confident in your deploys :)

Further Points

Increase in Build Time and How to Avoid

This will add an overhead to your builds, so depending on the size of your MVC project this could extend your build time substantially. However, thanks to a discussion with some other team members at Esendex (Alex and Jon) there is a way of avoiding the overhead the majority of the time. Create a new project configuration in Visual Studio, called something like Debug with Views Compilation. This will add a new PropertyGroup section in your project file for this configuration. Set the MvcBuildViews element to true in this new section. Remember to ensure the default section value for MvcBuildViews is false but set the release section value to true.



   false



   true



   true

Quite useful if you are working within your MVC app and not changing the views at all.

Problems with Non-Standard Project Builds

If you are building a standard MVC project on a developer machine then everything should work fine. If your MVC project isn't standard you may find you have to play with the PhysicalPath part of the AspNetCompiler. To get our CI build machine building this project I had to change the PhysicalPath value to $(WebProjectOutputDir), this value is passed in as a parameter when calling the build on your project.


 

MSBuild.exe myproject.web.csproj /target:Rebuild,ResolveReferences,_CopyWebApplication /p:Configuration=Release;
Architecture=x86;WebProjectOutputDir=build\release\web\;OutDir=build\release\web\bin\ 

Hope this info is useful to someone out there!

Thursday, 8 April 2010

Running IIS in 64 Bit Mode

Internet Information Services (IIS) 6.0 supports both the 32-bit mode and the 64-bit mode (as long as your OS is 64-bit too!). However IIS 6.0 does not support running both modes at the same time.

  • ASP.NET 1.1 runs only in 32-bit mode (but who's still using this?!)
  • ASP.NET 2.0 (and up) runs in 32-bit mode or in 64-bit mode
  • To run ASP.NET 1.1 and ASP.NET 2.0 web applications at the same time, you're stuck with IIS in 32-bit mode unfortunately

So here's how I setup IIS and ASP.Net to run in 64-bit mode:

  1. Go to command prompt
  2. Disable the 32-bit mode in IIS using the command line argument: cscript %SYSTEMDRIVE%\inetpub\adminscripts\adsutil.vbs SET W3SVC/AppPools/Enable32bitAppOnWin64 0
  3. Install the 64-bit version of ASP.NET 2.0 on IIS using the command line argument: %SYSTEMROOT%\Microsoft.NET\Framework64\v2.0.50727\aspnet_regiis.exe -i
  4. Make sure that the status of ASP.NET version 2.0.50727 is set to Allowed in the Web service extension list in IIS Manager. If you had been using ASP.Net in 32-bit mode before, you may need to Prohibit that version ASP.NET version 2.0.50727 (32 bit) first.

ASP.Net SQL Server Registration Tool & Session State

The ASP.NET SQL Server Registration tool is used to create a Microsoft SQL Server database for use by the SQL Server providers in ASP.NET, or to add or remove options from an existing database. The tool can be found @ Windows\Microsoft.NET\Framework\VERSION\aspnet_regsql.exe.

Run the executable without arguments to load the wizard, then use the wizard to create or modify database elements for the membership, role management, profile, web parts personalization, and health monitoring features.

SQL Session State (ASPState)

ASP.Net Session State supports several different storage options for session data. To make use of SQL Server Session State you'll need to create the ASPState database on your SQL server instance; but this database isn't created by the aspnet_regsql wizard. Instead you'll need to run the executable with additional arguments. Use MSDN for a full list of arguments available, below is the minimum you'll need to get it installed.

aspnet_regsql.exe -ssadd -S {SERVER} -U {USER} -P {PASSWORD}

The -ssadd command-line option == add the session state database

Tuesday, 6 April 2010

Getting IIS to Forward All Requests to ASP.Net

Setting up IIS to send all requests to ASP.Net (e.g. for a HttpHandler for a ReST service or for an ASP.Net MVC web app) is straight forward with .Net and IIS.

1) First ensure that your web application can handle all requests

This is already done for you in an ASP.Net MVC web app. But if you are creating a custom HttpHandler to handle all requests you'll need to go to the httpHandlers section of your web.config file add a reference to your HttpHandler for all HTTP verbs and extensions:

<system.web>
   <httpHandlers>
      <add verb="*" path="*" type="your.assembly.reference"/>
   </httpHandlers>
</system.web>

2) Setup IIS to route all request to your handler

a) IIS 5/5.1 (Win XP)

  • Open the website properties in IIS
  • Select the Home tab
  • Click the Configuration button
  • In new window select the Mappings tab
  • Click the Add button
  • In new window's executable field Browse to the ASP.Net ISAPI dll (usually something like C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll)
  • In the Extension field type .*
  • Untick the Check that file exists box and click OK
N.B. There is a bug that if your OK button is not active you need to click the executable field text box, you should see the middle section of the text in the box change from /.../ to the full file path this should now make the OK button active.

b) IIS 6 (XP x64 & Windows Server 2003)

  • Open the website properties in IIS
  • Select the Home tab
  • Click the Configuration button
  • In new window select the Mappings tab
  • Insert an entry in to the Wildcard application maps with the ASP.Net ISAPI dll (usually something like C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll)
  • Untick the check that file exists box and click OK

You should now see all requests being handle via your handler. If you are having issues, you may want to look at your IIS logs - but where are my IIS logs?. Good luck!