TicketDesk 3 Dev Diary – Thoughts on Security

hunts point, the bronx, new yo... by andre dos santosIn this post, I’m brainstorming new approaches to handling security in TicketDesk 3, and reviewing the limitations of TicketDesk 2’s old approach.

Security in  TicketDesk 2:

TicketDesk 2 shipped with support for either active directory, or local security. Local security via the built-in membership providers worked reasonably well for most deployments, but the AD option has always been problematic.

Windows authentication, in conjunction with the WindowsTokenRoleProvider, works well enough for the basic question, “who are you?”, but windows authentication severely limits in the amount of information you can obtain about the user. You get the windows account name and a list of the user’s AD groups –and that’s pretty much all you get.

Some features in TD required more than that. It needed display names and email addresses, and it needed to list all users in some AD groups; information the built-in providers are incapable of obtaining.

To support these requirements, TicketDesk 2 used a complicated background processes for AD concerns. It maintains a local cache of AD data to reduce the number of queries, and moved AD query processing off of the user servicing request threads.

Even with the abstractions in System.DirectoryServices, querying AD is painfully obtuse. In addition, response times for AD queries are often measured in the tens of seconds –even in single domain environments. You can’t reasonably query AD in real-time without bringing the entire application to a standstill. The custom module alleviates these issues, but was a  complete nightmare to code and maintain.

The other significant problem has been the difficulty of extending the old system to support more features, such as custom roles and permissions. This is why TicketDesk still uses hard-coded roles with pre-defined permissions –I simply never had the time or energy to re-factor the AD side of the system.

Security technologies of the present:

I’ve played a bit with the SimpleMembership and Universal Providers, which have replaced the original SqlMembership providers. ASP.NET’s security system is still based on the same provider framework that shipped with .Net 2.0 back in 2005. Sadly, the framework is a fundamentally flawed system, and in need of a complete redesign.

In recent years, there have been two attempts to modernize the ASP.NET providers, but neither really addresses the underlying problems of the core framework itself. Both are just extensions layered on top of the original infrastructure.

Universal Providers are an evolutionary upgrade of the original SQL providers from ASP.NET 2.0. They mainly just move data access into Entity Framework, which allows them to work with any database back-end supported by EF. Otherwise, there isn’t a much of a difference between them.

SimpleMembership was created for ASP.NET WebPages, but has become the officially recommended replacement in MVC projects as well. This is the default security provider in the newer MVC project templates that come with Visual Studio 2012. As I wrote in another post though, this system has some deep design flaws of its own. You almost have to learn how to hack up a customized version of SimpleMembership just to make it useful in any real-world scenario.

The good news is that both SimpleMembership and Universal Providers include oAuth support backed by the popular DotNetOpenAuth libraries. So, while flawed and build on a shaky foundation, at least they have been modernized.

Traditional windows authentication itself has not changed in any significant way, and there have been no official replacements for the windows security providers that I know of. ASP.NET still uses the same windows provider options that shipped in .Net 2.0, all of which are spectacularly terrible.

The major action for Active Directory authentication has been with Active Directory Federation Services. This is an STS token server that exposes AD to clients that support WS-federation. The Windows Identity Foundation (WIF), originally introduced in WCF, was pulled directly into the core of the .Net framework with the 4.5 release. This provides the infrastructure ASP.NET needs to leverage federated authentication and authorization. So, if you setup federation in ASP.NET you can easily connect it to ADFS to authenticate your AD users. Best of all, ADFS can share any information stored in AD.

In Windows Azure land, you have the Access Control Service (ACS), which is also a WS-Federation token service. From ASP.NET, connecting to ACS is similar to connecting to ADFS. The real advantage of ACS is that it can act as an aggregator and proxy for any number of other external authentication sources. For example, you can connect your ACS to your domain’s ADFS service and to oAuth providers.

The new identity features in .NET 4.5 also incorporate claims based identity as a pervasive and normative feature used throughout the entire framework. You don’t have to use WS-federation in order to use claims based identities, since all identities are now claims based.

Ideas for TicketDesk 3:

For TicketDesk 3, I hope to support the following authentication options:

  • local user accounts (forms with user registration)
  • oAuth (sign-in with google, facebook, twitter, etc.)
  • Windows (AD or local system accounts)
  • WS-Federation (ADFS, Azure ACS, etc.)

My challenge is to find a way to bring all these pieces together in TicketDesk 3 in a sane way.

I want TicketDesk’s business logic to make use of claims based identity for its authorization decisions. Since claims are baked in, this seems to be the most sensible way of doing things. The key will be to map all users, regardless of how they are authenticated, to a common set of claims that TicketDesk understands. The business logic can then rely on having those claims attached to every user’s identity.

For authentication, I have little choice but to rely on existing mechanisms. I am not a crypto expert, and have no intention of rolling my own password and user verification code. But, to support multiple authentication sources, I will likely need to roll my own module to intercept user requests, and dynamically route them to the appropriate pre-built authentication mechanism as needed at runtime.

Each of the possible authentication systems will produce a ClaimsIndentity, but the structure and content of those claims varies. Some authentication generates very few usable claims, while others may contain all the claims TicketDesk could ever need. To bridge the gap, TicketDesk will need an identity transformer. This component will map incoming identities, obtained during authentication, to a TicketDesk identity with all the claims TicketDesk needs. Since some claims cannot be obtained during authentication, TicketDesk will need to obtain any missing data from the user, or an administrator, then store and manage that data locally.

As you can tell, this is all in the early conceptual stages. I’m doing a lot of experiments and research, but I’m at the point where I have a good grasp of most of the pieces that I’ll need to smash together.

I have several other projects with similar security requirements, and I’ve grown tired of approaching each new project with the question, “how much hacking will ASP.NET membership need this time?” So, no matter what I come up with, I hope to roll it into a re-usage package.

ASP.NET MVC SimpleMembership – Initilization and New Project Template Issues

The ASP.NET MVC new project templates have been upgraded in Visual Studio 2012. Among the many improvements, was the change to using SimpleMembershipProvider. Simple they may be named, but I’ve found using it to be anything but simple. There are some deep design flaws in the provider, as well as how it is used in the stock MVC project templates.

The issues start with web.config. If you have worked with asp.net in the past, the most obvious difference in new web.config files will be the lack of configuration elements related to security providers.

Old templates had something like this:

       ... stuff ...
            <add name=".... />
            <add name=".... />
            <add name=".... />
    ... stuff ...

The new templates have none of these elements. There is a default connection in web.config, called DefaultConnection, and the templates use this connection for membership as well application data by default.

So far, so simple.

If you need to change the default behavior of SimpleMembership though, where do you make those changes? For example, I don’t like the default password length requirements, so I’ve always changed that in config. You can explicitly add the SimpleMembership provider to web.config, but doing so is useless. SimpleMembership just ignores settings in web.config. If you want to change the behavior, you have to track down the entity classes SimpleMembership is using (models/AccountModels.cs in the stock templates), and manually change the attributes in code.

While modifying settings in code isn’t hard, it is a truly horrible design. Your security configuration is scattered all over your model classes, and the runtime security policy is buried deep within compiled code, instead of a centralized configuration file where your server admin can get at it. This design also eliminates the option of using config transformations during build, publish, or deployment to customize the security policy for different target environments.

If you want to change the connection used by SimpleMembership, you no longer do this through web.config either. Instead, you have to track down the code that initializes the security system, which is inconveniently located in the /Filters/InitializeSimpleMembershipAttribute.cs file. Here, you need to update the call to WebSecurity.InitializeDatabaseConnection to pass in the correct name of your connection string. Not very intuitive, but that’s not the truly weird part either…

The weird part is that initialization for SimpleMembership, in the stock project templates, is done from an ActionFilterAttribute. This attribute decorates the AccountController class, and only that class. The result is that none of the SimpleMembership system will be initialized until a real user physically visits one of your account pages; login, register, or whatever.

If you write code in a page that allows anonymous access, or in a background or start-up routine, and it tries to do something related to membership, roles, or profiles you will get bizzare behavior. You would think that the code would just fail –the providers haven’t been initialized right?

The truth is much, much worse.

If code tries to do security stuff before SimpleMembership is explicitly initialized, asp.net will initialize the default security providers automatically. Instead of the SimpleMembership provider that you expected, it will initialize a SqlMembershipProvider using a connection named “LocalSqlServer”. This stuff is configured in the machine.config file, which includes the default configuration settings for SqlMembership, and points it at the local SQLExpress instance.

So, lets say you open up your new MVC project, which you created from the VS 2012 default template. You go to the HomeController, and edit the Index action to call Membership.GetAllUsers(). That’s all. You don’t need to do anything with the users, just ask for a list of them.

Then you run your application.

What do you think happens?

If your system is setup like most, you probably have SQL express installed. So, suddenly a mysterious aspnet.mdf file shows up in your app_data folder, and gets attached to your SQLExpress instance… and this all probably happens without you noticing it. If you don’t have a SQLExpress instance, your application will hang on startup for a long while, then timeout with “The system cannot find the file specified”.

I don’t know about anyone else, but I REALLY hate when an application starts making new databases on SQL servers that it shouldn’t even know exist.

Overall, I am not impressed with SimpleMembership, nor how the stock templates implement it. The providers are prone to unexpected and unintended behavior unless you have deep knowledge about their internal workings. Customizing SimpleMembership is the opposite of simple. And for configuration, it is confusing that it plugs into a standard asp.net feature, then ignores the traditional means of configuring that feature. And to top it off, requiring basic security configuration to be handled in code is a severely deranged design choice.