eworldproblems
  • Home
  • About
  • Awesome Ideas That Somebody Else Already Thought Of
  • Perl defects
  • Books & Resources
Follow

Securing SignalR to your site’s users



As you know if you read my last post on hub authorization, when a new hub connection is created, the transport connection stays open whether or not the hub connection(s) are authorized. In my case, I am using SignalR in a carefully controlled, secure authenticated environment, and as a common-sense security measure, I wanted to just close down any connections to SignalR from random clients that had no business connecting to my servers. Unfortunately, as of 1.0 RC1 there’s no built-in way to do this, but with some quick pointers from David Fowler I was able to put together something with Owin that causes my code to be run on every request first, before handing it off to SignalR. The result seemed like it might be useful enough to other people to be worthy of a blog post.

Disclaimers: This code was written for SignalR 1.0 RC1 and the version of owin that was around at that time. A visitor has posted some updates in the comments on this post’s page for SignalR 1.1.3. This applies to SignalR on top of IIS; I don’t know how it would be different if you’re hosting in some other way. This applies to requests that are routed to SignalR hubs (hub transport connections, method invocations, hubs auto-javascript.) You may need to do a similar thing for Persistent Connections too if you use them. Also, most of the calls and callbacks to and from Owin are just what “seemed to work,” but that doesn’t mean this is the best or most correct possible way to do it. We’ll just have to wait for Owin to be documented for that.

Step 1: Ditch RouteTable.Routes.MapHubs();

You should recognize this call from your application startup code (if you added SignalR via nuget, it’s in App_Start/RegisterHubs.cs). Internally, MapHubs tells Owin to route requests with certain urls right into SignalR code. This isn’t how we want to route. What we want to do is route them to our code first, where we’ll decide if we want to let the request through to SignalR or close it. So, we have to do do some routing calls ourselves:

// in addition to the usings you already have,
using Microsoft.AspNet.SignalR.SystemWeb.Infrastructure;
using Microsoft.Owin.Host.SystemWeb;
using Owin; // actually defined in a SignalR assembly and probably going to change soon...

public static void Start()
{
	// RouteTable.Routes.MapHubs(); not doing this anymore
	MapHubsWithSecurityInspector(RouteTable.Routes, "~/signalr", GlobalHost.DependencyResolver);
}

private static RouteBase MapHubsWithSecurityInspector(RouteCollection routes, string url, IDependencyResolver resolver)
{
	var existing = routes["signalr.hubs"];
	if (existing != null)
	{
		routes.Remove(existing);
	}

	var routeUrl = url.TrimStart('~').TrimStart('/');

	var locator = new Lazy(() => new BuildManagerAssemblyLocator());
	resolver.Register(typeof(IAssemblyLocator), () => locator.Value);

	return routes.MapOwinRoute("signalr.hubs", routeUrl, map => OwinSetup(map, resolver));
}

private static void OwinSetup(IAppBuilder builder, IDependencyResolver resolver)
{
	builder.Use(typeof(MySecurityInspectionHandler));

	builder.MapHubs(resolver);
}

Step 2: Write MySecurityInspectionHandler

After you’ve replaced MapHubs with the above, Owin will need you to define a MySecurityInspectionHandler class, because it will try to call it on every incoming request to routeUrl. The signature of MySecurityInspectionHandler is a bit magical, as the constructor and callback Owin actually looks for isn’t expressed anywhere in docs or as an interface or anything. Here’s what seems to work:

using System.Threading.Tasks;

// u might want to put the rest  in your app's namespace...
using AppFunc = Func<IDictionary<string, object>, Task>;

public class MySecurityInspectionHandler
{
	private AppFunc _app;

	public MySecurityInspectionHandler(AppFunc app)
	{
		_app = app;
	}

	public Task Invoke(IDictionary<string, object> environment)
	{
		// Finally, here's where we can examine the incoming request and allow or reject it
		if(RequestHasRightCookiesOrSomething(environment))
		{
			// continue processing the request
			return _app.Invoke(environment);
		} else {
			return StopRequestProcessing(environment);
		}
	}

	private Task StopRequestProcessing(IDictionary<string, object> environment)
	{
		environment.Add(ResponseStatusCode, 403);

		object responseStreamObj;
		environment.TryGetValue(ResponseBody, out responseStreamObj);
		Stream responseStream = (Stream)responseStreamObj;

		var streamWriter = new StreamWriter(responseStream);
		streamWriter.Write("403 Forbidden.");
		streamWriter.Close();

		var tcs = new TaskCompletionSource<bool>();
		tcs.SetResult(false);
		return tcs.Task;
	}
}

Then all you have to do is implement your actual logic to detect if this is a request from an authorized source or not, in a RequestHasRightCookiesOrSomething method. Just about everything you could ever want to know about the incoming request can be found in that environment IDictionary, unfortunately as objects with string keys that Intellisense couldn’t help you with. When you get to this stage just shove a breakpoint in and take a look at the Keys / Values inside environment to find what you need.

Step 3: Test

Visit some urls to your SignalR hubs manually in a browser, both with and without the expected authentication features sent along. When you do not send the authentication information, you should get a 403 forbidden back from URLs like

  • http://localhost/myproj/signalr
  • http://localhost/myproj/signalr/hubs
  • http://localhost/myproj/signalr/anything_else

When you do send authentication, /signalr visited manually currently produces an unknown transport exception, and /signalr/hubs produces the auto-javascript.

Hope this helps. Happy routing!

Posted in SignalR - Tagged Security, SignalR
SHARE THIS Twitter Facebook Delicious StumbleUpon E-mail
← SignalR Hub Authorization
Making Ubuntu resolve names like Windows →

3 Comments

  1. Tan's Gravatar Tan
    September 26, 2013 at 4:41 am | Permalink

    Thank you for your post,
    When I used your code with SignalR 1.1.3, I encountered some problems, so I have to change your code in Step 1. In Global.asax, write this code:
    protected void Application_Start()
    {
    RouteTable.Routes.MapHubs(“/signalr”, new HubConfiguration(), (builder) => { builder.Use(typeof(MySecurityInspectionHandler)); });
    //RouteTable.Routes.MapHubs();
    ….
    }

    Reply
    • Tan's Gravatar Tan
      September 26, 2013 at 4:46 am | Permalink

      Btw, one more thing is in Step 2, please change:
      – In line 29, from ResponseStatusCode to “owin.ResponseStatusCode”.
      – In line 32, from ResponseBody to “owin.ResponseBody”.
      Thanks.

      Reply
  2. Mike's Gravatar Mike
    September 26, 2013 at 7:07 pm | Permalink

    Glad to hear this code still approximately works, and thanks for the updates! I’ll put in a note to see your comments for more current versions of SignalR.

    Reply

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Recent Posts

  • Reset connection rate limit in pfSense
  • Connecting to University of Minnesota VPN with Ubuntu / NetworkManager native client
  • Running nodes against multiple puppetmasters as an upgrade strategy
  • The easiest way to (re)start MySQL replication
  • Keeping up on one’s OpenSSL cipher configurations without being a fulltime sysadmin

Categories

  • Computing tips
    • Big Storage @ Home
    • Linux
  • dev
    • devops
    • Drupal
    • lang
      • HTML
      • JavaScript
      • PHP
    • SignalR
  • Product Reviews
  • Uncategorized

Tags

Apache iframe malware performance Security SignalR YWZmaWQ9MDUyODg=

Archives

  • June 2018
  • January 2018
  • August 2017
  • January 2017
  • December 2016
  • November 2016
  • July 2016
  • February 2016
  • January 2016
  • September 2015
  • March 2015
  • February 2015
  • November 2014
  • August 2014
  • July 2014
  • April 2014
  • February 2014
  • January 2014
  • October 2013
  • August 2013
  • June 2013
  • January 2013
  • December 2012
  • November 2012
  • September 2012
  • August 2012
  • July 2012

Blogroll

  • A Ph.D doing DevOps (and lots else)
  • gavinj.net – interesting dev blog
  • Louwrentius.com – zfs@home with 4x the budget, other goodies
  • Me on github
  • My old edulogon.com blog
  • My old GSOC blog
  • My wife started baking a lot
  • Now it's official, my wife is a foodie

Meta

  • Log in
  • Entries feed
  • Comments feed
  • WordPress.org

EvoLve theme by Theme4Press  •  Powered by WordPress eworldproblems