Home / My Disclaimer / Who am I? / Search... / Sign in

// steve

Change of Scenery

by Steve Syfuhs / November 29, 2011 12:56 PM

Every once in a while you need to make a life-altering decision.

Last night I sent an email to the ObjectSharp team telling them I had resigned (I had spoken to the bosses in person prior).

Boy, talk about blunt, eh?

Every once in a while you are offered a once in a lifetime opportunity to do something pretty amazing. I’ve had three of these opportunities. The first was Woodbine Entertainment where I got my start in the Toronto development world. The second was ObjectSharp where I have been able to learn so much from some of the brightest minds in the industry. The third was two weeks ago in Vancouver.

Two weeks ago I was offered a position to lead development of a product for an ISV in BC.

Me? A leader? Wait. Huh?

Well okay, not quite. Cue the screeching cut-away-from-sappy noise.

So what's the deal? I'm not saying, yet. At this point I'm sure a few people could guess though. Smile

Suffice to say I'll be moving to BC at the end of the year. I'll be at ObjectSharp until December 16th and then will be moving right around the new year.

I'm not sure I can really describe how excited I am about this new position. I'll be working on an awesome product with an awesome group of people.

Of course, it's a little sad leaving ObjectSharp. They have a such a great team of people, and some of the smartest people in the industry.

So it should be an interesting experience.

Input Validation: The Good, The Bad, and the What the Hell are you Doing?

by Steve Syfuhs / November 28, 2011 11:00 AM

Good morning class!

Pop quiz: How many of you do proper input validation in your ASP.NET site, WebForms, MVC, or otherwise?

Some Background

There is an axiom in computer science: never trust user input because it's guaranteed to contain invalid data at some point.

In security we have a similar axiom: never trust user input because it's guaranteed to contain invalid data at some point, and your code is bound to contain a security vulnerability somewhere, somehow. Granted, it doesn't flow as well as the former, but the point still stands.

The solution to this problem is conceptually simple: validate, validate, validate. Every single piece of input that is received from a user should be validated.

Of course when anyone says something is a simple concept it's bound to be stupidly complex to get the implementation right. Unfortunately proper validation is not immune to this problem. Why?

The Problem

Our applications are driven by user data. Without data our applications would be pretty useless. This data is usually pretty domain-specific too so everything we receive should have particular structures, and there's a pretty good chance that a few of these structures are so specific to the organization that there is no well-defined standard. By that I mean it becomes pretty difficult to validate certain data structures if they are custom designed and potentially highly-complex.

So we have this problem. First, if we don't validate that the stuff we are given is clean, our application starts behaving oddly and that limits the usefulness of the application. Second, if we don't validate that the stuff we are given is clean, and there is a bug in the code, we have a potential vulnerability that could wreak havoc for the users.

The Solution

The solution as stated above is to validate all the input, both from a business perspective and from a security perspective. We want it to go something like this:

In this post we are going to look at the best way to validate the security of incoming data within ASP.NET. This requires looking into how ASP.NET processes input from the user.

When ASP.NET receives something from the user it can come from four different vectors:

  • Within the Query String (?foo=bar)
  • Within the Form (via a POST)
  • Within a cookie
  • Within the server variables (a collection generated from HTTP headers and internal server configuration)

These vectors drive ASP.NET, and you can potentially compromise an application by maliciously modifying any of them.

Pop quiz: How many of you check whether custom cookies exist before trying to use them? Almost everyone, good. Now, how many of you validate that the data within the cookies is, well, valid before using them?

What about checking your HTTP headers?

The Bypass

Luckily ASP.NET has some out-of-the-box behaviors that protect the application from malicious input. Unfortunately ASP.NET isn't very forgiving when it comes to validation. It doesn't distinguish between quasi-good input and bad input, so anything containing an angle bracket causes a YSoD.

The defacto fix to this is to do one of two things:

  • Disable validation in the page declaration within WebForms, or stick a [ValidateInput(false)] attribute on an MVC controller
  • Set <pages validateRequest="false"> in web.config

What this will do is tell ASP.NET to basically skip validating the four vectors and let anything in. It was assumed that you would do validation on your own.

Raise your hand if you think this is a bad idea. Okay, keep your hands up if you've never done this for a production application. At this point almost everyone should have put their hands down. I did.

The reason we do this is because as I said before, ASP.NET isn't very forgiving when it comes to validation. It's all or nothing.

What's worse, as ASP.NET got older it started becoming pickier about what it let in so you had more reasons for disabling validation. In .NET 4 validation occurs at a much earlier point. It's a major breaking change:

The request validation feature in ASP.NET provides a certain level of default protection against cross-site scripting (XSS) attacks. In previous versions of ASP.NET, request validation was enabled by default. However, it applied only to ASP.NET pages (.aspx files and their class files) and only when those pages were executing.

In ASP.NET 4, by default, request validation is enabled for all requests, because it is enabled before the BeginRequest phase of an HTTP request. As a result, request validation applies to requests for all ASP.NET resources, not just .aspx page requests. This includes requests such as Web service calls and custom HTTP handlers. Request validation is also active when custom HTTP modules are reading the contents of an HTTP request.

Since backwards compatibility is so important, a configuration attribute was also added to tell ASP.NET to revert to the 2.0 validation mode meaning that it occurs later in the request lifecycle like in ASP.NET 2.0:

<httpRuntime requestValidationMode="2.0" />

If you do a search online for request validation almost everyone comes back with this solution. In fact, it became a well known solution with the Windows Identity Foundation in ASP.NET 4.0 because when you do a federated sign on, WIF receives the token as a chunk of XML. The validator doesn't approve because of the angle brackets. If you set the validation mode to 2.0, the validator checks after the request passes through all HttpModules, which is how WIF consumes that token via the WSFederationAuthenticationModule.

The Proper Solution

So we have the problem. We also have built in functionality that solves our problem, but the way it does it kind of sucks (it's not a bad solution, but it's also not extensible). We want a way that doesn't suck.

In earlier versions of ASP.NET the best solution was to disable validation and within a HttpModule check every vector for potentially malicious input. The benefit here is that you have control over what is malicious and what is not. You would write something along these lines:

public class ValidatorHttpModule : IHttpModule
{
    public void Dispose() { }

    public void Init(HttpApplication context)
    {
        context.BeginRequest += new EventHandler(context_BeginRequest);
    }

    void context_BeginRequest(object sender, EventArgs e)
    {
        HttpApplication context = (HttpApplication)sender;

        foreach (var q in context.Request.QueryString)
        {
            if (CheckQueryString(q))
            {
                throw new SecurityException("Bad validation");
            }
        }

        foreach (var f in context.Request.Form)
        {
            if (CheckForm(f))
            {
                throw new SecurityException("Bad validation");
            }
        }

        foreach (var c in context.Request.Cookies)
        {
            if (CheckCookie(c))
            {
                throw new SecurityException("Bad validation");
            }
        }

        foreach (var s in context.Request.ServerVariables)
        {
            if (CheckServerVariable(s))
            {
                throw new SecurityException("Bad validation");
            }
        }
    }

    // <snip />
}

The downside to this approach though is that you are stuck with pretty clunky validation logic. It executes on every single request, which may not always be necessary. You are also forced to execute the code in order of whenever your HttpModule is initialized. It won't necessarily execute first, so it won't necessarily protect all parts of your application. Protection from an attack that doesn't protect everything from that particular attack isn't very useful.  <Cynicism>Half-assed protection is only good when you have half an ass.</Cynicism>

What we want is something that executes before everything else. In our HttpModule we are validating on BeginRequest, but we want to validate before BeginRequest.

The way we do this is with a custom RequestValidator. On a side note, this post may qualify as having the longest introduction ever. In any case, this custom RequestValidator is set within the httpRuntime tag within the web.config:

<httpRuntime requestValidationType="Syfuhs.Web.Security.CustomRequestValidator" />

We create a custom request validator by creating a class with a base class of System.Web.Util.RequestValidator. Then we override the IsValidRequestString method.

This method allows us to find out where the input is coming from, e.g. from a Form or from a cookie etc. This validator is called on each value within the four collections above, but only when a value exists. It saves us the trouble of going over everything in each request. Within an HttpModule we could certainly build out the same functionality by checking contents of each collection, but this saves us the hassle of writing the boilerplate code. It also provides us a way of describing the problem in detail because we can pass an index location of where the problem exists. So if we find a problem at character 173 we can pass that value back to the caller and ASP.NET will throw an exception describing that index. This is how we get such a detailed exception from WIF:

A Potentially Dangerous Request.Form Value Was Detected from the Client (wresult="<t:RequestSecurityTo...")

Our validator class ends up looking like:

public class MyCustomRequestValidator : RequestValidator
{
    protected override bool IsValidRequestString(HttpContext context, string value, RequestValidationSource requestValidationSource, string collectionKey, out int validationFailureIndex)
    {
        validationFailureIndex = 0;

        switch (requestValidationSource)
        {
            case RequestValidationSource.Cookies:
                return ValidateCookie(collectionKey, value, out validationFailureIndex);
                break;

            case RequestValidationSource.Form:
                return ValidateFormValue(collectionKey, value, out validationFailureIndex);
                break;

            // <snip />
        }

        return base.IsValidRequestString(context, value, requestValidationSource, collectionKey, out validationFailureIndex);
    }

    // <snip />
}

Each application has different validation requirements so I've just mocked up how you would create a custom validator.

If you use this design you can easily validate all inputs across the application, and you don't have to turn off validation.

So once again, pop quiz: How many of you do proper input validation?

Strongly Typed Claims

by Steve Syfuhs / November 12, 2011 04:03 PM

Sometimes it's a pain in the neck working with Claims. A lot of times you need to look for particular claim and that usually means looping through the claims collection and parsing the value to a particular type.

This little dance is the trade-off for having such a simple interface to a potentially arbitrary collection of claims. Most of the time this works, but every once in a while you need to create a basic user object that contains some strongly typed properties. You could build up a basic object like:

public class User
{
    public string UserName { get; set; }

    public string EmailAddress { get; set; }

    public string Department { get; set; }

    public List<string> Roles { get; set; }
}

This would require you to intercept the IClaimsIdentity object and search through the claims collection setting each property manually whenever you wanted to get access to the data. This can get tiresome and is error prone.

I think I've come up with a relatively complete solution to this problem. Basically it works by creating a custom IClaimsIdentity class that sets a User property through reflection. You can then access the user through Thread.CurrentPrincipal.Identity like this:

TypedClaimsIdentity ident = Thread.CurrentPrincipal.Identity as TypedClaimsIdentity;
string email = ident.User.EmailAddress.Value;
var userRoles = ident.User.Roles;

Once you've defined the particular types and their associated claims, the particular values will be set through reflection. So to declare your user properties, create a class like this:

public class MyTypedClaimsUser : TypedClaims
{
    public MyTypedClaimsUser()
    {
        this.Name = new TypedClaim<string>();
        this.EmailAddress = new TypedClaim<string>();
        this.Roles = new List<TypedClaim<string>>();
        this.Expiration = new TypedClaim<DateTime>();
        this.AuthenticationMethod = new TypedClaim<string>();
    }

    [TypedClaim(ClaimTypes.Name, false)]
    public TypedClaim<string> Name { get; private set; }

    [TypedClaim(ClaimTypes.Email, false)]
    public TypedClaim<string> EmailAddress { get; private set; }

    [TypedClaim(ClaimTypes.Role, true)]
    public List<TypedClaim<string>> Roles { get; private set; }

    [TypedClaim(ClaimTypes.Expiration, true)]
    public TypedClaim<DateTime> Expiration { get; private set; }

    [TypedClaim(ClaimTypes.AuthenticationMethod, false)]
    public TypedClaim<string> AuthenticationMethod { get; private set; }

    [TypedClaim(ClaimTypes.GroupSid, false)]
    public TypedClaim<string> GroupSid { get; private set; }
}

Each property must be defined a certain way. Each property must have a particular attribute set: TypedClaimAttribute. This attribute will help the reflection code associate the property with the expected claim. That way the Name property will always be mapped to the ClaimTypes.Name claim type, which is the http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name claim. It also helps by warning the code that it's going to likely have multiple potential values, like the Role claim.

Each property is also of a particular type: TypedClaim<T>. In theory I could have just used simple types like strings but by going this route you can get access to claim metadata like Name.ClaimType or Name.Issuer. TypedClaim<T> is inherited from Claim.

So how does this all work? Well first you need to be able to add the User object into the Identity object. This is done by creating a custom IClaimsIdentity class:

[Serializable]
public class TypedClaimsIdentity : IClaimsIdentity
{
    public TypedClaimsIdentity(IClaimsIdentity identity)
    {
        user = new MyTypedClaimsUser();

        if (identity.Claims != null)
            this.claims = identity.Claims;
        else
            claims = new ClaimCollection(identity);

        this.Actor = identity.Actor;
        this.AuthenticationType = identity.AuthenticationType;

        Update();
    }

    private void Update()
    {
        user.Update(this.claims);
    }

    private MyTypedClaimsUser user;

    public MyTypedClaimsUser User
    {
        get
        {
            Update();
            return user;
        }
    }

    private ClaimCollection claims;

    public ClaimCollection Claims
    {
        get
        {
            Update();
            return claims;
        }
    }

    public IClaimsIdentity Actor { get; set; }

    public SecurityToken BootstrapToken { get; set; }

    public IClaimsIdentity Copy()
    {
        ClaimsIdentity claimsIdentity = new ClaimsIdentity(this.AuthenticationType);

        if (this.Claims != null)
        {
            claimsIdentity.Claims.AddRange(claims.CopyWithSubject(claimsIdentity));
        }

        claimsIdentity.Label = this.Label;
        claimsIdentity.NameClaimType = this.NameClaimType;
        claimsIdentity.RoleClaimType = this.RoleClaimType;
        claimsIdentity.BootstrapToken = this.BootstrapToken;

        return claimsIdentity;
    }

    public string Label { get; set; }

    public string NameClaimType { get; set; }

    public string RoleClaimType { get; set; }

    public string AuthenticationType { get; private set; }

    public bool IsAuthenticated { get { return claims.Count > 0; } }

    public string Name { get { return User.Name.Value; } }
}

There isn't anything spectacularly interesting about this class. The important part is the constructor. It only accepts an IClaimsIdentity object because it's designed as a way to wrap around an already created identity. It then updates the User object through Update().

The User object is updated through reflection. The Update() method calls User.Update(…) which is defined within the base class of MyTypedClaimsUser. This will call into a helper class that looks through the User object and find any properties that contain the TypedClaimAttribute.

EDIT: When it comes to reflection, there is always a better way to do something. My original code was mostly a PoC and didn't make use of existing .NET-isms. I've edited this bit to include the code changes.

The helper class was originally a bit clunky because all it did was look through the properties and if/else if's through their types and parses them:

if (type == typeof(string))
{
    return new TypedClaim<string>(selectedClaims.First()) { Value = selectedClaims.First().Value };
}

This really isn't the smartest way to do it because .NET already contains some pretty strong conversion functions; specifically Convert.ChangeType(value, type).

Going this route requires generating the proper TypedClaim<T> though. Many thanks to Anna Lear because she pointed out the MakeGenericType(…) method, which allows you to take a type and convert it to a generic type with the specified type parameters. That way I could dynamically pass a type into a generic without hardcoding anything. This allows the TypedClaim<T> to be set at runtime without having to code for each particular parameter. So you end up with basic logic along the lines of:

Type constructed = typeof(TypedClaim<>).MakeGenericType(new Type[] { genericParamType });

object val = Convert.ChangeType(claim.Value, genericParamType);

return Activator.CreateInstance(constructed, claim.ClaimType, val);

The Activator.CreateInstance method will construct an instance of the particular type which will eventually be passed into PropertyInfo.Value.SetValue(…).

Finally, it's time to integrate this into your web application. The best location is probably going to be through a custom ClaimsAuthenticationManager. It works like this:

public class TypedClaimsAuthenticationManager : ClaimsAuthenticationManager
{
    public override IClaimsPrincipal Authenticate(string resourceName, IClaimsPrincipal incomingPrincipal)
    {
        if (!incomingPrincipal.Identity.IsAuthenticated)
            return base.Authenticate(resourceName, incomingPrincipal);

        for (int i = 0; i < incomingPrincipal.Identities.Count; i++)
            incomingPrincipal.Identities[i] = new TypedClaimsIdentity(incomingPrincipal.Identities[i]);

        return base.Authenticate(resourceName, incomingPrincipal);
    }
}

Then to tell WIF about this new CAM you need to make a change to the web.config. Within the Microsoft.IdentityModel/Service section, add this:

<claimsAuthenticationManager type="Syfuhs.IdentityModel.TypedClaimsAuthenticationManager, Syfuhs.IdentityModel" />

By dynamically setting the values of the user object, you can create a fairly robust identity model for your application.

You can download the updated code here: typedclaimsv2.zip (6.21 kb)

You can download the original code here: typedclaims.zip (5.61 kb)

Tamper-Evident Configuration Files in ASP.NET

by Steve Syfuhs / September 28, 2011 04:00 PM

A couple weeks ago someone sent a message to one of our internal mailing lists. His message was pretty straightforward: how do you prevent modifications of a configuration file for an application [while the user has administrative rights on the machine]?

There were a couple responses including mine, which was to cryptographically sign the configuration file with an asymmetric key. For a primer on digital signing, take a look here. Asymmetric signing is one possible way of signing a file. By signing it this way the configuration file could be signed by an administrator before deploying the application, and all the application needed to validate the signature was the public key associated with the private key used to sign the file. This separated the private key from the application, preventing the configuration from being re-signed maliciously. It’s similar in theory to how code-signing works.

In the event that validation of the configuration file failed, the application would not load, or would gracefully fail and exit the next time the file was checked (or the application had an exclusive lock on the configuration file so it couldn’t be edited while running).

We are also saved the problem of figuring out the signature format because there is a well-respected XML signature schema: http://www.w3.org/2000/09/xmldsig#. WCF uses this format to sign messages. For a good code-walkthrough see Barry Dorrans’ Beginning ASP.NET Security. More on the code later here though.

Technically, this won’t prevent changes to the file, but it will prevent the application from accepting those changes. It’s kind of like those tamper-evident tags manufacturers stick on the enclosures of their equipment. It doesn’t prevent someone from opening the thing, but they will get caught if someone checks it. You’ll notice I didn’t call them “tamper-resistance” tags.

Given this problem, I went one step further and asked myself: how would I do this with a web application? A well-informed ASP.NET developer might suggest using aspnet_regiis to encrypt the configuration file. Encrypting the configuration does protect against certain things, like being able to read configuration data. However, there are a couple problems with this.

  • If I’m an administrator on that server I can easily decrypt the file by calling aspnet_regiis
  • If I’ve found a way to exploit the site, I can potentially overwrite the contents of the file and make the application behave differently
  • The encryption/decryption keys need to be shared in web farms

Consider our goal. We want to prevent a user with administrative privileges from modifying the configuration. Encryption does not help us in this case.  Signing the configuration will help though (As an aside, for more protection you encrypt the file then sign it, but that’s out of the scope of this) because the web application will stop working if a change is made that invalidates the signature.

Of course, there’s one little problem. You can’t stick the signature in the configuration file, because ASP.NET will b-itch complain about the foreign XML tag. The original application in question was assumed to have a custom XML file for it’s configuration, but in reality it doesn’t, so this problem applies there too.

There are three possible solutions to this:

  • Create a custom ConfigurationSection class for the signature
  • Create a custom configuration file and handler, and intercept all calls to web.config
  • Stick the signature of the configuration file into a different file

The first option isn’t a bad idea, but I really didn’t want to muck about with the configuration classes. The second option is, well, pretty much a bad idea in almost all cases, mainly because I’m not entirely sure you can even intercept all calls to the configuration classes.

I went with option three.

The other file has two important parts: the signature of the web.config file, and a signature for itself. This second signature prevents someone from modifying the signature for the web.config file. Our code becomes a bit more complicated because now we need to validate both signatures.

This makes us ask the question, where is the validation handled? It needs to happen early enough in the request lifecycle, so I decided to stick it into a HTTP Module, for the sake of modularity.

Hold it, you say. If the code is in a HTTP Module, then it needs to be added to the web.config. If you are adding it to the web.config, and protecting the web.config by this module, then removing said module from the web.config will prevent the validation from occurring.

Yep.

There are two ways around this:

  • Add the validation call into Global.asax
  • Hard code the addition of the HTTP Module

It’s very rare that I take the easy approach, so I’ve decided to hard code the addition of the HTTP Module, because sticking the code into a module is cleaner.

In older versions of ASP.NET you had to make some pretty ugly hacks to get the module in because it needs to happen very early in startup of the web application. With ASP.NET 4.0, an assembly attribute was added that allowed you to call code almost immediately after startup:

[assembly: PreApplicationStartMethod(typeof(Syfuhs.Security.Web.Startup), "Go")]

Within the Startup class there is a public static method called Go(). This method calls the Register() within an instance of my HttpModule. This module inherits from an abstract class called DynamicallyLoadedHttpModule, which inherits from IHttpModule. This class looks like:

public abstract class DynamicallyLoadedHttpModule : IHttpModule
{
    public void Register()
    {
        DynamicHttpApplication.RegisterModule(delegate(HttpApplication app) { return this; });
    }

    public abstract void Init(HttpApplication context);

    public abstract void Dispose();
}

The DynamicHttpApplication class inherits from HttpApplication and allows you to load HTTP modules in code. This code was not written by me. It was originally written by Nikhil Kothari:

using HttpModuleFactory = System.Func<System.Web.HttpApplication, System.Web.IHttpModule>;

public abstract class DynamicHttpApplication : HttpApplication
{
    private static readonly Collection<HttpModuleFactory> Factories = new Collection<HttpModuleFactory>();
    private static object _sync = new object();
    private static bool IsInitialized = false;

    private List<IHttpModule> modules;

    public override void Init()
    {
        base.Init();

        if (Factories.Count == 0)
            return;

        List<IHttpModule> dynamicModules = new List<IHttpModule>();

        lock (_sync)
        {
            if (Factories.Count == 0)
                return;

            foreach (HttpModuleFactory factory in Factories)
            {
                IHttpModule m = factory(this);

                if (m != null)
                {
                    m.Init(this);
                    dynamicModules.Add(m);
                }
            }
        }

        if (dynamicModules.Count != 0)
            modules = dynamicModules;

        IsInitialized = true;
    }

    public static void RegisterModule(HttpModuleFactory factory)
    {
        if (IsInitialized)
            throw new InvalidOperationException(Exceptions.CannotRegisterModuleLate);

        if (factory == null)
            throw new ArgumentNullException("factory");

        Factories.Add(factory);
    }

    public override void Dispose()
    {
        if (modules != null)
            modules.ForEach(m => m.Dispose());

        modules = null;
            
        base.Dispose();

        GC.SuppressFinalize(this);
    }
}

Finally, to get this all wired up we modify the Global.asax to inherit from DynamicHttpApplication:

public class Global : DynamicHttpApplication { ... }

Like I said, you could just add the validation code into Global (but where’s the fun in that?)…

So, now that we’ve made it possible to add the HTTP Module, lets actually look at the module:

public sealed class SignedConfigurationHttpModule : DynamicallyLoadedHttpModule
{
    public override void Init(HttpApplication context)
    {
        if (context == null)
            throw new ArgumentNullException("context");

        context.BeginRequest += new EventHandler(context_BeginRequest);
        context.Error += new EventHandler(context_Error);
    }

    private void context_BeginRequest(object sender, EventArgs e)
    {
        HttpApplication app = (HttpApplication)sender;

        SignatureValidator validator = new SignatureValidator(app.Request.PhysicalApplicationPath);

        validator.ValidateConfigurationSignatures(CertificateLocator.LocateSigningCertificate());
    }

    private void context_Error(object sender, EventArgs e)
    {
        HttpApplication app = (HttpApplication)sender;

        foreach (var exception in app.Context.AllErrors)
        {
            if (exception is XmlSignatureValidationFailedException)
            {
                // Maybe do something
                // Or don't...
                break;
            }
        }
    }

    public override void Dispose() { }
}

Nothing special here. Just hooking into the context.BeginRequest event so validation occurs on each request. There would be some performance impact as a result.

The core validation is contained within the SignatureValidator class, and there is a public method that we call to validate the signature file, ValidateConfigurationSignatures(…). This method accepts an X509Certificate2 to compare the signature against.

The specification for the schema we are using for the signature will actually encode the public key of the private key into the signature element, however we want to go one step further and make sure it’s signed by a particular certificate. This will prevent someone from modifying the configuration file, and re-signing it with a different private key. Validation of the signature is not enough; we need to make sure it’s signed by someone we trust.

The validator first validates the schema of the signature file. Is the XML well formed? Does the signature file conform to a schema we defined (the schema is defined in a Constants class)? Following that is validates the signature of the file itself. Has the file been tampered with? Following that it validates the signature of the web.config file. Has the web.config file been tampered with?

Before it can do all of this though, it needs to check to see if the signature file exists. The variable passed into the constructor is the physical path of the web application. The validator knows that the signature file should be in the App_Data folder within the root. This file needs to be here because the folder by default will not let you access anything in it, and we don’t want anyone downloading the file. The path is also hardcoded specifically so changes to the configuration cannot bypass the signature file validation.

Here is the validator:

internal sealed class SignatureValidator
{
    public SignatureValidator(string physicalApplicationPath)
    {
        this.physicalApplicationPath = physicalApplicationPath;
        this.signatureFilePath = Path.Combine(this.physicalApplicationPath, "App_Data\\Signature.xml");
    }

    private string physicalApplicationPath;
    private string signatureFilePath;

    public void ValidateConfigurationSignatures(X509Certificate2 cert)
    {
        Permissions.DemandFilePermission(FileIOPermissionAccess.Read, this.signatureFilePath);

        if (cert == null)
            throw new ArgumentNullException("cert");

        if (cert.HasPrivateKey)
            throw new SecurityException(Exceptions.ValidationCertificateHasPrivateKey);

        if (!File.Exists(signatureFilePath))
            throw new SecurityException(Exceptions.CouldNotLoadSignatureFile);

        XmlDocument doc = new XmlDocument() { PreserveWhitespace = true };
        doc.Load(signatureFilePath);

        ValidateXmlSchema(doc);

        CheckForUnsignedConfig(doc);

        if (!X509CertificateCompare.Compare(cert, ValidateSignature(doc)))
            throw new XmlSignatureValidationFailedException(Exceptions.SignatureFileNotSignedByExpectedCertificate);

        List<XmlSignature> signatures = ParseSignatures(doc);

        ValidateSignatures(signatures, cert);
    }

    private void CheckForUnsignedConfig(XmlDocument doc)
    {
        List<string> signedFiles = new List<string>();

        foreach (XmlElement file in doc.GetElementsByTagName("File"))
        {
            string fileName = Path.Combine(this.physicalApplicationPath, file["FileName"].InnerText);

            signedFiles.Add(fileName.ToUpperInvariant());
        }

        CheckConfigFiles(signedFiles);
    }

    private void CheckConfigFiles(List<string> signedFiles)
    {
        foreach (string file in Directory.EnumerateFiles(this.physicalApplicationPath, "*.config", SearchOption.AllDirectories))
        {
            string path = Path.Combine(this.physicalApplicationPath, file);

            if (!signedFiles.Contains(path.ToUpperInvariant()))
                throw new XmlSignatureValidationFailedException(string.Format(CultureInfo.CurrentCulture, Exceptions.ConfigurationFileWithoutSignature, path));
        }
    }

    private void ValidateXmlSchema(XmlDocument doc)
    {
        using (StringReader fileReader = new StringReader(Constants.SignatureFileSchema))
        using (StringReader signatureReader = new StringReader(Constants.SignatureSchema))
        {
            XmlSchema fileSchema = XmlSchema.Read(fileReader, null);
            XmlSchema signatureSchema = XmlSchema.Read(signatureReader, null);

            doc.Schemas.Add(fileSchema);
            doc.Schemas.Add(signatureSchema);

            doc.Validate(Schemas_ValidationEventHandler);
        }
    }

    void Schemas_ValidationEventHandler(object sender, ValidationEventArgs e)
    {
        throw new XmlSignatureValidationFailedException(Exceptions.InvalidSchema, e.Exception);
    }

    public static X509Certificate2 ValidateSignature(XmlDocument xml)
    {
        if (xml == null)
            throw new ArgumentNullException("xml");

        XmlElement signature = ExtractSignature(xml.DocumentElement);

        return ValidateSignature(xml, signature);
    }

    public static X509Certificate2 ValidateSignature(XmlDocument doc, XmlElement signature)
    {
        if (doc == null)
            throw new ArgumentNullException("doc");

        if (signature == null)
            throw new ArgumentNullException("signature");

        X509Certificate2 signingCert = null;

        SignedXml signed = new SignedXml(doc);
        signed.LoadXml(signature);

        foreach (KeyInfoClause clause in signed.KeyInfo)
        {
            KeyInfoX509Data key = clause as KeyInfoX509Data;

            if (key == null || key.Certificates.Count != 1)
                continue;

            signingCert = (X509Certificate2)key.Certificates[0];
        }

        if (signingCert == null)
            throw new CryptographicException(Exceptions.SigningKeyNotFound);

        if (!signed.CheckSignature())
            throw new CryptographicException(Exceptions.SignatureValidationFailed);

        return signingCert;
    }

    private static void ValidateSignatures(List<XmlSignature> signatures, X509Certificate2 cert)
    {
        foreach (XmlSignature signature in signatures)
        {
            X509Certificate2 signingCert = ValidateSignature(signature.Document, signature.Signature);

            if (!X509CertificateCompare.Compare(cert, signingCert))
                throw new XmlSignatureValidationFailedException(
                    string.Format(CultureInfo.CurrentCulture, 
                    Exceptions.SignatureForFileNotSignedByExpectedCertificate, signature.FileName));
        }
    }

    private List<XmlSignature> ParseSignatures(XmlDocument doc)
    {
        List<XmlSignature> signatures = new List<XmlSignature>();

        foreach (XmlElement file in doc.GetElementsByTagName("File"))
        {
            string fileName = Path.Combine(this.physicalApplicationPath, file["FileName"].InnerText);

            Permissions.DemandFilePermission(FileIOPermissionAccess.Read, fileName);

            if (!File.Exists(fileName))
                throw new FileNotFoundException(string.Format(CultureInfo.CurrentCulture, Exceptions.FileNotFound, fileName));

            XmlDocument fileDoc = new XmlDocument() { PreserveWhitespace = true };
            fileDoc.Load(fileName);

            XmlElement sig = file["FileSignature"] as XmlElement;

            signatures.Add(new XmlSignature()
            {
                FileName = fileName,
                Document = fileDoc,
                Signature = ExtractSignature(sig)
            });
        }

        return signatures;
    }

    private static XmlElement ExtractSignature(XmlElement xml)
    {
        XmlNodeList xmlSignatureNode = xml.GetElementsByTagName("Signature");

        if (xmlSignatureNode.Count <= 0)
            throw new CryptographicException(Exceptions.SignatureNotFound);

        return xmlSignatureNode[xmlSignatureNode.Count - 1] as XmlElement;
    }
}

You’ll notice there is a bit of functionality I didn’t mention. Checking that the web.config file hasn’t been modified isn’t enough. We also need to check if any *other* configuration file has been modified. It’s no good if you leave the root configuration file alone, but modify the <authorization> tag within the administration folder to allow anonymous access, right?

So there is code looks through the site for any files that have the “config” extension, and if that file isn’t in the signature file, it throws an exception.

There is also a check done at the very beginning of the validation. If you pass an X509Certificate2 with a private key it will throw an exception. This is absolutely by design. You sign the file with the private key. You validate with the public key. If the private key is present during validation that means you are not separating the keys, and all of this has been a huge waste of time because the private key is not protected. Oops.

Finally, it’s important to know how to sign the files. I’m not a fan of generating XML properly, partially because I’m lazy and partially because it’s a pain to do, so mind the StringBuilder:

public sealed class XmlSigner
{
    public XmlSigner(string appPath)
    {
        this.physicalApplicationPath = appPath;
    }

    string physicalApplicationPath;

    public XmlDocument SignFiles(string[] paths, X509Certificate2 cert)
    {
        if (paths == null || paths.Length == 0)
            throw new ArgumentNullException("paths");

        if (cert == null || !cert.HasPrivateKey)
            throw new ArgumentNullException("cert");

        XmlDocument doc = new XmlDocument() { PreserveWhitespace = true };
        StringBuilder sb = new StringBuilder();

        sb.Append("<Configuration>");
        sb.Append("<Files>");

        foreach (string p in paths)
        {
            sb.Append("<File>");

            sb.AppendFormat("<FileName>{0}</FileName>", p.Replace(this.physicalApplicationPath, ""));
            sb.AppendFormat("<FileSignature><Signature xmlns=\"http://www.w3.org/2000/09/xmldsig#\">{0}</Signature></FileSignature>", 
            SignFile(p, cert).InnerXml);

            sb.Append("</File>");
        }

        sb.Append("</Files>");
        sb.Append("</Configuration>");

        doc.LoadXml(sb.ToString());

        doc.DocumentElement.AppendChild(doc.ImportNode(SignXmlDocument(doc, cert), true));

        return doc;
    }

    public static XmlElement SignFile(string path, X509Certificate2 cert)
    {
        if (string.IsNullOrWhiteSpace(path))
            throw new ArgumentNullException("path");

        if (cert == null || !cert.HasPrivateKey)
            throw new ArgumentException(Exceptions.CertificateDoesNotContainPrivateKey);

        Permissions.DemandFilePermission(FileIOPermissionAccess.Read, path);

        XmlDocument doc = new XmlDocument();
        doc.PreserveWhitespace = true;
        doc.Load(path);

        return SignXmlDocument(doc, cert);
    }

    public static XmlElement SignXmlDocument(XmlDocument doc, X509Certificate2 cert)
    {
        if (doc == null)
            throw new ArgumentNullException("doc");

        if (cert == null || !cert.HasPrivateKey)
            throw new ArgumentException(Exceptions.CertificateDoesNotContainPrivateKey);

        SignedXml signed = new SignedXml(doc) { SigningKey = cert.PrivateKey };

        Reference reference = new Reference() { Uri = "" };

        XmlDsigC14NTransform transform = new XmlDsigC14NTransform();
        reference.AddTransform(transform);

        XmlDsigEnvelopedSignatureTransform envelope = new XmlDsigEnvelopedSignatureTransform();
        reference.AddTransform(envelope);
        signed.AddReference(reference);

        KeyInfo keyInfo = new KeyInfo();
        keyInfo.AddClause(new KeyInfoX509Data(cert));
        signed.KeyInfo = keyInfo;

        signed.ComputeSignature();

        XmlElement xmlSignature = signed.GetXml();

        return xmlSignature;
    }
}

To write this to a file you can call it like this:

XmlWriter writer = XmlWriter.Create(@"C:\Dev\Projects\Syfuhs.Security.Web\Syfuhs.Security.Web.WebTest\App_Data\Signature.xml");
XmlSigner signer = new XmlSigner(Request.PhysicalApplicationPath);

XmlDocument xml = signer.SignFiles(new string[] { 
@"C:\Dev\Projects\Syfuhs.Security.Web\Syfuhs.Security.Web.WebTest\Web.config",
@"C:\Dev\Projects\Syfuhs.Security.Web\Syfuhs.Security.Web.WebTest\Web.debug.config",
@"C:\Dev\Projects\Syfuhs.Security.Web\Syfuhs.Security.Web.WebTest\Web.release.config",
@"C:\Dev\Projects\Syfuhs.Security.Web\Syfuhs.Security.Web.WebTest\Account\Web.config",
@"C:\Dev\Projects\Syfuhs.Security.Web\Syfuhs.Security.Web.WebTest\test.config"
}, 
new X509Certificate2(@"C:\Dev\Projects\Syfuhs.Security.Web\Syfuhs.Security.Web.WebTest\cert.pfx", "1"));

xml.WriteTo(writer);
writer.Flush();

Now within this code, you have to pass in a X509Certificate2 with a private key, otherwise you can’t sign the files.

These processes should occur on different machines. The private key should never be on the server hosting the site. The basic steps for deployment would go something like:

1. Compile web application.
2. Configure site and configuration files on staging server.
3. Run application that signs the configuration and generates the signature file.
4. Drop the signature.xml file into the App_Data folder.
5. Deploy configured and signed application to production.

There is one final note (I think I’ve made that note a few times by now…) and that is the CertificateLocator class. At the moment it just returns a X509Certificate2 from a particular path on my file system. This isn’t necessarily the best approach because it may be possible to overwrite that file. You should store that certificate in a safe place, and make a secure call to get it. For instance a web service call might make sense. If you have a Hardware Security Module (HSM) to store secret bits in, even better.

Concluding Bits

What have we accomplished by signing our configuration files? We add a degree of trust that our application hasn’t been compromised. In the event that the configuration has been modified, the application stops working. This could be from malicious intent, or careless administrators. This is a great way to prevent one-off changes to configuration files in web farms. It is also a great way to prevent customers from mucking up the configuration file you’ve deployed with your application.

This solution was designed in a way mitigate quite a few attacks. An attacker cannot modify configuration files. An attacker cannot modify the signature file. An attacker cannot view the signature file. An attacker cannot remove the signature file. An attacker cannot remove the HTTP Module that validates the signature without changing the underlying code. An attacker cannot change the underlying code because it’s been compiled before being deployed.

Is it necessary to use on every deployment? No, probably not.

Does it go a little overboard with regard to complexity? Yeah, a little.

Does it protect against a real problem? Absolutely.

Unfortunately it also requires full trust.

Overall it’s a fairly robust solution and shows how you can mitigate certain types of risks seen in the real world.

And of course, it works with both WebForms and MVC.

You can download the full source: https://syfuhs.blob.core.windows.net/files/c6afeabc-36fc-4d41-9aa7-64cc9385280d-configurationsignaturevalidation.zip

Protecting Against BEAST and it's Man in the Middle Attack on SSL/TLS 1.0

by Steve Syfuhs / September 22, 2011 04:00 PM

Last week some researchers announced they figured out how to exploit a vulnerability in the SSL 3.0/TLS 1.0 protocol.

This particular vulnerability has been known for a few years, and our friends on the OpenSSL project had written a paper discussing it in 2004. This has been a theoretical problem for a while, but nobody was really able to weaponize the vulnerability.

In theory these researchers have.

The vulnerability is only found in the encryption algorithms that support CBC because of the nature of block ciphers (as apposed to stream ciphers). For more information read the OpenSSL paper above.  The way it works is a method called plaintext-recovery. Basically, if I know a particular value of plaintext, I can mangle the encrypted data in such a way that makes it possible to guess the rest of the plaintext. The reason this stayed theoretical was because there wasn’t wasn’t an easy way to guess the plaintext. The researchers have potentially found an easier way to guess.

Now, this vulnerability is only found under certain conditions:

  • You must be running SSL 3.0/TLS 1.0 (TLS 1.1 and 1.2 are not affected)
  • You must be using a block cipher for encryption

Without making this sound like FUD, it should be noted that this hasn’t necessarily become a problem. The security industry hasn’t really made a decision on whether this is a serious problem yet. It’s kind of a wait and see scenario.

Now, why is this problem? Most web servers are still stuck on TLS v1. And by most, I really mean most. The reason for this is because most web browsers only support TLS v1.

You can thank Mozilla Network Security Services (NSS). This is the crypto plugin that Firefox and Chrome use, and NSS doesn’t support TLS 1.1 or 1.2.

Chrome’s latest developer release now has a fix for this vulnerability, by switching to a stream cipher instead of block cipher.

Curiously Internet Explorer does support TLS 1.1 and 1.2 (and has since IE7). IIS 7 also supports it.

However, IIS only supports TLS v1 by default. You need to enable TLS 1.1 and 1.2.

It’s pretty straightforward, just add the following registry keys:

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2]

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Client]
"Enabled"=dword:00000001
"DisabledByDefault"=dword:00000000

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Server]
"Enabled"=dword:00000001
"DisabledByDefault"=dword:00000000

While you are at it, it might not be a bad idea to disable SSL v2 as well:

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 2.0\Server]
"DisabledByDefault"=dword:00000001

EDIT: I guess I should clarify that you should also disable TLS 1.0 as well, otherwise the browser can request that TLS v1 be used instead.

Set DisabledByDefault=1 under TLS 1.0\Server.

Next, reboot the server.

If you want to automate the procedure for multiple servers, Derek Seaman has a great little PowerShell script that sets everything up for you. He also shows you how you can test to make sure it actually worked, which is nice.

Windows Live and Windows 8

by Steve Syfuhs / September 12, 2011 04:00 PM

So. I guess I wasn't the only one with this idea: http://www.syfuhs.net/post/2011/02/28/making-the-internet-single-sign-on-capable.aspx

Sweet. Smile

Announced earlier today at the Build conference, Microsoft is creating a tighter integration between Windows 8 and Windows Live.  More details to come when I download the bits later tonight.

The Importance of Elevating Privilege

by Steve Syfuhs / August 28, 2011 04:00 PM

The biggest detractor to Single Sign On is the same thing that makes it so appealing – you only need to prove your identity once. This scares the hell out of some people because if you can compromise a users session in one application it's possible to affect other applications. Congratulations: checking your Facebook profile just caused your online store to delete all it's orders. Let's break that attack down a little.

  • You just signed into Facebook and checked your [insert something to check here] from some friend. That contained a link to something malicious.
  • You click the link, and it opens a page that contains an iframe. The iframe points to a URL for your administration portal of the online store with a couple parameters in the query string telling the store to delete all the incoming orders.
  • At this point you don't have a session with the administration portal and in a pre-SSO world it would redirect you to a login page. This would stop most attacks because either a) the iframe is too small to show the page, or b) (hopefully) the user is smart enough to realize that a link from a friend on Facebook shouldn't redirect you to your online store's administration portal. In a post-SSO world, the portal would redirect you to the STS of choice and that STS already has you signed in (imagine what else could happen in this situation if you were using Facebook as your identity provider).
  • So you've signed into the STS already, and it doesn't prompt for credentials. It redirects you to the administration page you were originally redirected away from, but this time with a session. The page is pulled up, the query string parameters are parsed, and the orders are deleted.

There are certainly ways to stop this as part of this is a bit trivial. For instance you could pop up an Ok/Cancel dialog asking "are you sure you want to delete these?", but for the sake of discussion lets think of this at a high level.

The biggest problem with this scenario is that deleting orders doesn't require anything more than being signed in. By default you had the highest privileges available.

This problem is similar to the problem many users of Windows XP had. They were, by default, running with administrative privileges. This lead to a bunch of problems because any application running could do whatever it pleased on the system. Malware was rampant, and worse, users were just doing all around stupid things because they didn't know what they were doing but they had the permissions necessary to do it.

The solution to that problem is to give users non-administrative privileges by default, and when something required higher privileges you have to re-authenticate and temporarily run with the higher privileges. The key here is that you are running temporarily with higher privileges. However, security lost the argument and Microsoft caved while developing Windows Vista creating User Account Control (UAC). By default a user is an administrator, but they don't have administrative privileges. Their user token is a stripped down administrator token. You only have non-administrative privileges. In order to take full advantage of the administrator token, a user has to elevate and request the full token temporarily. This is a stop-gap solution though because it's theoretically possible to circumvent UAC because the administrative token exists. It also doesn't require you to re-authenticate – you just have to approve the elevation.

As more and more things are moving to the web it's important that we don't lose control over privileges. It's still very important that you don't have administrative privileges by default because, frankly, you probably don't need them all the time.

Some web applications are requiring elevation. For instance consider online banking sites. When I sign in I have a default set of privileges. I can view my accounts and transfer money between my accounts. Anything else requires that I re-authenticate myself by entering a private pin. So for instance I cannot transfer money to an account that doesn't belong to me without proving that it really is me making the transfer.

There are a couple ways you can design a web application that requires privilege elevation. Lets take a look at how to do it with Claims Based Authentication and WIF.

First off, lets look at the protocol. Out of the box WIF supports the WS-Federation protocol. The passive version of the protocol supports a query parameter of wauth. This parameter defines how authentication should happen. The values for it are mostly specific to each STS however there are a few well-defined values that the SAML protocol specifies. These values are passed to the STS to tell it to authenticate using a particular method. Here are some most often used:

Authentication Type/Credential Wauth Value
Password urn:oasis:names:tc:SAML:1.0:am:password
Kerberos urn:ietf:rfc:1510
TLS urn:ietf:rfc:2246
PKI/X509 urn:oasis:names:tc:SAML:1.0:am:X509-PKI
Default urn:oasis:names:tc:SAML:1.0:am:unspecified

When you pass one of these values to the STS during the signin request, the STS should then request that particular type of credential. the wauth parameter supports arbitrary values so you can use whatever you like. So therefore we can create a value that tells the STS that we want to re-authenticate because of an elevation request.

All you have to do is redirect to the STS with the wauth parameter:

https://yoursts/authenticate?wa=wsignin1.0&wtrealm=uri:myrp&wauth=urn:super:secure:elevation:method

Once the user has re-authenticated you need to tell the relying party some how. This is where the Authentication Method claim comes in handy:

http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod

Just add the claim to the output identity:

protected override IClaimsIdentity GetOutputClaimsIdentity(IClaimsPrincipal principal, RequestSecurityToken request, Scope scope)
{
    IClaimsIdentity ident = principal.Identity as IClaimsIdentity;
    ident.Claims.Add(new Claim(ClaimTypes.AuthenticationMethod, "urn:super:secure:elevation:method"));
    // finish filling claims...
    return ident;
}

At that point the relying party can then check to see whether the method satisfies the request. You could write an extension method like:

public static bool IsElevated(this IClaimsPrincipal principal)
{
    return principal.Identity.AuthenticationType == "urn:super:secure:elevation:method";
}

And then have a bit of code to check:

var p = Thread.CurrentPrincipal as IClaimsPrincipal;
if (p != null && p.IsElevated())
{
    DoSomethingRequiringElevation();
}

This satisfies half the requirements for elevating privilege. We need to make it so the user is only elevated for a short period of time. We can do this in an event handler after the token is received by the RP.  In Global.asax we could do something like:

void Application_Start(object sender, EventArgs e)
{
    FederatedAuthentication.SessionAuthenticationModule.SessionSecurityTokenReceived 
        += new EventHandler<SessionSecurityTokenReceivedEventArgs> (SessionAuthenticationModule_SessionSecurityTokenReceived);
}
void SessionAuthenticationModule_SessionSecurityTokenReceived(object sender, SessionSecurityTokenReceivedEventArgs e)
{
    if (e.SessionToken.ClaimsPrincipal.IsElevated())
    {
        SessionSecurityToken token = new SessionSecurityToken(e.SessionToken.ClaimsPrincipal, e.SessionToken.Context, e.SessionToken.ValidFrom, e.SessionToken.ValidFrom.AddMinutes(15));
        e.SessionToken = token;
    }
}

This will check to see if the incoming token has been elevated, and if it has, set the lifetime of the token to 15 minutes.

There are other places where this could occur like within the STS itself, however this value may need to be independent of the STS.

As I said earlier, as more and more things are moving to the web it's important that we don't lose control of privileges. By requiring certain types of authentication in our relying parties, we can easily support elevation by requiring the STS to re-authenticate.

Dear Recipient

by Steve Syfuhs / July 18, 2011 04:00 PM

Got this email just now.  I was amused (mainly because it got past the spam filters)…

Dear Email user,

This message is from Administration center Maintenance Policy verified that your mailbox exceeds (10.9GB) its limit, you may be unable to receive new email or send mails, To re- set your SPACE on our database prior to maintain your IN BOX, You must Reply to this email by Confirming your account details below.

Surname:

First Name:

User-Name:

Password:

Date Of Birth:

Failure to do this will immediately render your Web-email address deactivated from our database.

Thanks

Admin Help Desk© 2011

And my Reply…

Dear Admin Help Desk© 2011,

Thank you for the warning that my SPACE on your database prior to maintain my IN BOX exceeds its limit. Please find the following information handy for future reference.

Surname: Jobs

First Name: Steve

User-Name: Jobbers1

Password: CenterOfTheUniverse

Date Of Birth: December 25th, 0001

If for whatever reason you cannot reset my SPACE please email me back at tips@fbi.gov and I will be sure to help you.

Adjusting the Home Realm Discovery page in ADFS to support Email Addresses

by Steve Syfuhs / July 12, 2011 04:00 PM

Over on the Geneva forums a question was asked:

Does anyone have an example of how to change the HomeRealmDiscovery Page in ADFSv2 to accept an e-mail address in a text field and based upon that (actually the domain suffix) select the correct Claims/Identity Provider?

It's pretty easy to modify the HomeRealmDiscovery page, so I thought I'd give it a go.

Based on the question, two things need to be known: the email address and the home realm URI.  Then we need to translate the email address to a home realm URI and pass it on to ADFS.

This could be done a couple ways.  First it could be done by keeping a list of email addresses and their related home realms, or a list of email domains and their related home realms.  For the sake of this being an example, lets do both.

I've created a simple SQL database with three tables:

image

Each entry in the EmailAddress and Domain table have a pointer to the home realm URI (you can find the schema in the zip file below).

Then I created a new ADFS web project and added a new entity model to it:

image

From there I modified the HomeRealmDiscovery page to do the check:

//------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------

using System;

using Microsoft.IdentityServer.Web.Configuration;
using Microsoft.IdentityServer.Web.UI;
using AdfsHomeRealm.Data;
using System.Linq;

public partial class HomeRealmDiscovery : Microsoft.IdentityServer.Web.UI.HomeRealmDiscoveryPage
{
    protected void Page_Init(object sender, EventArgs e)
    {
    }

    protected void PassiveSignInButton_Click(object sender, EventArgs e)
    {
        string email = txtEmail.Text;

        if (string.IsNullOrWhiteSpace(email))
        {
            SetError("Please enter an email address");
            return;
        }

        try
        {
            SelectHomeRealm(FindHomeRealmByEmail(email));
        }
        catch (ApplicationException)
        {
            SetError("Cannot find home realm based on email address");
        }
    }

    private string FindHomeRealmByEmail(string email)
    {
        using (AdfsHomeRealmDiscoveryEntities en = new AdfsHomeRealmDiscoveryEntities())
        {
            var emailRealms = from e in en.EmailAddresses where e.EmailAddress1.Equals(email) select e;

            if (emailRealms.Any()) // email address exists
                return emailRealms.First().HomeRealm.HomeRealmUri;

            // email address does not exist
            string domain = ParseDomain(email);

            var domainRealms = from d in en.Domains where d.DomainAddress.Equals(domain) select d;

            if (domainRealms.Any()) // domain exists
                return domainRealms.First().HomeRealm.HomeRealmUri;

            // neither email nor domain exist
            throw new ApplicationException();
        }
    }

    private string ParseDomain(string email)
    {
        if (!email.Contains("@"))
            return email;

        return email.Substring(email.IndexOf("@") + 1);
    }

    private void SetError(string p)
    {
        lblError.Text = p;
    }
}

 

If you compare the original code, there was some changes.  I removed the code that loaded the original home realm drop down list, and removed the code to choose the home realm based on the drop down list's selected value.

You can find my code here: http://www.syfuhs.net/AdfsHomeRealm.zip

Part 5: Incident Response Management with Team Foundation Server

by Steve Syfuhs / July 01, 2011 02:00 PM

Over on the Canadian Solution Developer's blog I have a series on the basics of writing secure applications. It's a bit of an introduction to all the things we should know in order to write software that doesn't contain too many vulnerabilities. This is part five of the series, unedited for all to enjoy.

There are only a few certainties in life: death, taxes, me getting this post in late, and one of your applications getting attacked.  Throughout the lifetime of an application it will undergo a barrage of attack – especially if it's public facing.  If you followed the SDL, tested properly, coded securely, and managed well, you will have gotten most of the bugs out.

Most.

There will always be bugs in production code, and there will very likely always be a security bug in production code.  Further, if there is a security bug in production code, an attacker will probably find it.  Perhaps the best metric for security is along the lines of mean-time-to-failure.  Or rather, mean-time-to-breach.  All safes for storing valuables are rated in how long they can withstand certain types of attacks – not whether they can, but how long they can.  There is no one-single thing we can do to prevent an attack, and we cannot prevent all attacks.  It's just not in the cards.  So, it stands to reason then that we should prepare for something bad happening.  The final stage of the SDL requires that an Incident Response Plan is created.  This is the procedure to follow in the event of a vulnerability being found.

In security parlance, there are protocols and procedures.  The majority of the SDL is all protocol.  A protocol is the usual way to do things.  It's the list of steps you follow to accomplish a task that is associated with a normal working condition, e.g. fuzzing a file parser during development.  You follow a set of steps to fuzz something, and you really don't deviate from those steps.  A procedure is when something is different.  A procedure is reactive.  How you respond to a security breach is a procedure.  It's a set of steps, but it's not a normal condition.

An Incident Response Plan (IRP - the procedure) serves a few functions:

  • It has the list of people to contact in the event of the emergency
  • It is the actual list of steps to follow when bad things happen
  • It includes references to other procedures for code written by other teams

This may be one of the more painful parts of the SDL, because it's mostly process over anything else.  Luckily there are two wonderful products by Microsoft that help: Team Foundation Server.  For those of you who just cringed, bear with me.

Microsoft released the MSF-Agile plus Security Development Lifecycle Process Template for VS 2010 (it also takes second place in the longest product name contest) to make the entire SDL process easier for developers.  There is the SDL Process Template for 2008 as well.

It's useful for each stage of the SDL, but we want to take a look at how it can help with managing the IRP.  First though, lets define the IRP.

Emergency Contacts (Incident Response Team)

The contacts usually need to be available 24 hours a day, seven days a week.  These people have a range of functions depending on the severity of the breach:

  • Developer – Someone to comprehend and/or triage the problem
  • Tester – Someone to test and verify any changes
  • Manager – Someone to approve changes that need to be made
  • Marketing/PR – Someone to make a public announcement (if necessary)

Each plan is different for each application and for each organization, so there may be ancillary people involved as well (perhaps an end user to verify data).  Each person isn't necessarily required at each stage of the response, but they still need to be available in the event that something changes.

The Incident Response Plan

Over the years I've written a few Incident Response Plans (Never mind that I was asked to do it after an attack most times – you WILL go out and create one after reading this right?).  Each plan was unique in it's own way, but there were commonalities as well.

Each plan should provide the steps to answer a few questions about the vulnerability:

  • How was the vulnerability disclosed?  Did someone attack, or did someone let you know about it?
  • Was the vulnerability found in something you host, or an application that your customers host?
  • Is it an ongoing attack?
  • What was breached?
  • How do you notify the your customers about the vulnerability?
  • When do you notify them about the vulnerability?

And each plan should provide the steps to answer a few questions about the fix:

  • If it's an ongoing attack, how do you stop it?
  • How do you test the fix?
  • How do you deploy the fix?
  • How do you notify the public about the fix?

Some of these questions may not be answerable immediately – you may need to wait until a postmortem to answer them.

This is the high level IRP for example:

  1. The Attack – It's already happened
  2. Evaluate the state of the systems or products to determine the extent of the vulnerability
    • What was breached?
    • What is the vulnerability
  3. Define the first step to mitigate the threat
    • How do you stop the threat?
    • Design the bug fix
  4. Isolate the vulnerabilities if possible
    • Disconnect targeted machine from network
    • Complete forensic backup of system
    • Turn off the targeted machine if hosted
  5. Initiate the mitigation plan
    • Develop the bug fix
    • Test the bug fix
  6. Alert the necessary people
    • Get Marketing/PR to inform clients of breach (don't forget to tell them about the fix too!)
    • If necessary, inform the proper legal/governmental bodies
  7. Deploy any fixes
    • Rebuild any affected systems
    • Deploy patch(es)
    • Reconnect to network
  8. Follow up with legal/governmental bodies if prosecution of attacker is necessary
    • Analyze forensic backups of systems
  9. Do a postmortem of the attack/vulnerability
    • What went wrong?
    • Why did it go wrong?
    • What went right?
    • Why did it go right?
    • How can this class of attack be mitigated in the future?
    • Are there any other products/systems that would be affected by the same class?

Some of procedures can be done in parallel, hence the need for people to be on call.

Team Foundation Server

So now that we have a basic plan created, we should make it easy to implement.  The SDL Process Template (mentioned above) creates a set of task lists and bug types within TFS projects that are used to define things like security bugs, SDL-specific tasks, exit criteria, etc..

image

While these can (and should) be used throughout the lifetime of the project, they can also be used to map out the procedures in the IRP.  In fact, a new project creates an entry in Open SDL Tasks to create an Incident Response Team:

image

A bug works well to manage incident responses.

image

Once a bug is created we can link a new task with the bug.

image

And then we can assign a user to the task:

image

 

Each bug and task are now visible in the Security Exit Criteria query:

image

Once all the items in the Exit Criteria have been met, you can release the patch.

Conclusion

Security is a funny thing. A lot of times you don't think about it until it's too late. Other times you follow the SDL completely, and you still get attacked.

In the last four posts we looked at writing secure software from a pretty high level.  We touched on common vulnerabilities and their mitigations, tools you can use to test for vulnerabilities, some thoughts to apply to architecting the application securely, and finally we looked at how to respond to problems after release.  By no means will these posts automatically make you write secure code, but hopefully they have given you guidance to start understanding what goes into writing secure code.  It's a lot of work, and sometimes its hard work.

Finally, there is an idea I like to put into the first section of every Incident Response Plan I've written, and I think it applies to writing software securely in general:

Something bad just happened.  This is not the time to panic, nor the time to place blame.  Your goal is to make sure the affected system or application is secured and in working order, and your customers are protected.

Something bad may not have happened yet, and it may not in the future, but it's important to plan accordingly because your goal should be to protect the application, the system, and most importantly, the customer.

// About

Steve is a renaissance kid when it comes to technology. He spends his time in the security stack.