Sunday, February 24, 2008

Using IdentityModel: Typical Operations on Claim Sets

In the previous posts I talked about claims and claim sets. Now how do you use claim sets for authorization?

Let's take WCF as an example. In WCF you get access to the system generated claim sets via the AuthorizationContext (I have not yet explained where the AuthorizationContext comes from and how it is created - but I will in a future post). The typical code you write to access claim sets would be:

IEnumerable<ClaimSet> claimSets =
    ServiceSecurityContext.Current.AuthorizationContext.ClaimSets;

 

Once you have access to the claim sets you typically do the following operations:

  • Check if a specified claim is in one of the claim sets
  • Search for occurrences of a specified claim type in the claim sets

In addition you also want to specify the issuer of the claim you are looking for, e.g. search for the Name claim issued by 'System' or 'Windows'.

Unfortunately this simple task is a little cumbersome to achieve. Since you only have the FindClaim and ContainsClaim methods on ClaimSet itself, but not on the claim set list, you'd have to do this:

  1. Cycle through all the claim sets and inspect the issuer.
  2. If the issuer matches the one you are looking for use the issued claim set
  3. Search the claim set for the claim you are looking for

Extension methods to the rescue. In LeastPrivilege.IdentityModel I have extended IEnumerable<ClaimSet> with search operations that work across a list of claim sets:

  • ContainsClaim
  • FindClaim(s)
  • FindIdentityClaim
  • GetClaimSetsByIssuer

All these operations let you specify an issuer claim (or claim set) and the various overloads also allow to specify a claim comparer or if you want to search in issuer or issued claims.

The following code would achieve the above task (searching for the Windows account name):

Claim name = claimSets.FindClaim(ClaimTypes.Name, ClaimSet.Windows);

To get the Windows SID identity claim issued to the user, you could do this:

Claim sid = claimSets.FindIdentityClaim(ClaimSet.Windows);

...or directly convert to a SecurityIdentifier:

SecurityIdentifier sid =
    claimSets.FindIdentityClaim(ClaimSet.Windows).Get<SecurityIdentifier>();

 

This makes working with claim sets a little easier. In the next posts I will talk about how to create an AuthorizationContext yourself (e.g. to use the same programming model outside of WCF) and how authorization policies come mix in here.


IdentityModel | WCF
Sunday, February 24, 2008 11:28:24 AM UTC  #   

Using IdentityModel: Windows and X509Certificate Claim Sets

In System.IdentityModel.Claims you can find two more specialized claim sets for Windows accounts and X509 certificates called WindowsClaimSet and X509CertificateClaimSet respectively.

WCF uses these classes to create claim sets for Windows/certificate clients. But you can also use them "standalone".

The WindowsClaimSet converts the content of a Windows token to a claim set. You new up this class by passing in a WindowsIdentity. You can get one of these via network authentication, via LogonUser, or by calling WindowsIdentity.GetCurrent(). The generated claims are:

  • A SID identity claim containing the token's account SID (also available as a possess property)
  • SID possess property claims containing the group SIDs
  • Name possess property claim containing the user name

The issuer of this claim set will be always ClaimSet.Windows.

 

The X509CertificateClaimSet converts some of the attributes of an X509 certificate (and its issuers) to a claim set. Simply pass a X509Certificate2 to the constructor. The generated claims are:

  • A thumbprint claim containing the cert hash as identity and possess property.
  • The subject name as a X500DistinguishedName claim (possess property)
  • The public key of the certificate as an RSA claim (possess property)
  • Various extended name variations (if contained in the certificate). The claim set calls GetNameInfo on the certificate for DnsName, SimpleName, EmailName, UpnName and UrlName to create DNS, Name, Email, UPN and URI claims respectively.

The claim set issuer chain reflects the certificate issuer chain (by calling X509Chain.Build):

  • When the cert is self issued, the issuer will point to itself.
  • When the issuing cert is available, the issuer will be a X509CertificateClaimSet.
  • When the issuing cert is not available, the issuer will be a simple X509DistinguishedNameClaimSet containing the distinguished name of the issuer as identity and possess property.

IdentityModel
Sunday, February 24, 2008 9:07:27 AM UTC  #   
 Saturday, February 23, 2008

Using IdentityModel: Inspecting Claim Sets

The following code is useful for inspecting the contents of claim sets:

 

public static void ShowClaims(IEnumerable<ClaimSet> claimSets)
{
    int count = 0;
    foreach (ClaimSet set in claimSets)
    {
        Heading(String.Format("Claim Set #{0}", ++count), ConsoleColor.Yellow);
        ShowClaimSet(set, false);
    }
}

private static void ShowClaimSet(ClaimSet set, bool isIssuer)
{
    if (set.HasIssuer())
    {
        ShowClaimSet(set.Issuer, true);
    }

    string setType = set.GetType().Name;
   
string setName = isIssuer ? "Issuer" : "Issued";

    Heading(String.Format("\n{0} Claims ({1})\n", setName, setType),

      ConsoleColor.Green);

   
    foreach (Claim claim in set)
    {
        if (claim.Right.Equals(Rights.Identity))
        {
            Console.ForegroundColor = ConsoleColor.White;
        }

        Console.WriteLine(claim.ClaimType);
        Console.WriteLine(claim.Resource);
        Console.WriteLine(claim.Right);
        Console.WriteLine();

        Console.ResetColor();
    }
}

private static void Heading(string text, ConsoleColor color)
{
    Console.ForegroundColor = color;
    Console.WriteLine(text);
    Console.WriteLine();
    Console.ResetColor();
}


IdentityModel
Saturday, February 23, 2008 8:42:27 PM UTC  #   

Using IdentityModel: Claim Sets

In the previous post I talked about claims, what they are and how to create them. Usually a claim doesn't come on its own - but is grouped into a claim set.

To create a claim set you either derive from ClaimSet or new up a DefaultClaimSet. Which approach you choose depends on your needs. DefaultClaimSet provides a default implementation of a claim set whereas deriving from Claim allows doing your own internal data management (a little bit like deriving from GenericIdentity opposed to implementing the IIdentity interface manually). I used the Claim-derived approach in LeastPrivilege.IdentityModel e.g. to implement a claim set that does lazy loading of claims.

Regardless which approach you choose, a claim set always consists of two parts: a list of claims and an issuer.

The list of claims should contain a single identity claim - this acts as the identity of the claim set. Optionally there can be a number of possess property claims.

The issuer is also described using a claim set. The typical layout of an issuer claim set is a single identity claim that uses the System claim type (http://schemas.xmlsoap.org/ws/2005/05/identity/claims/system) and the same claim as a possess property. The value of these claims is up to the issuer.

System.IdentityModel provides two pre-defined issuer claim sets (available as static properties from the ClaimSet class):

  • ClaimSet.System
    Has a System claim type with a value of 'System' (identity and possess property).
    Used to describe claim sets that come from the 'System'.
  • ClaimSet.Windows
    Has a system claim types with a value of S-1-5 (identity and possess property).
    Used as an issuer for WindowsClaimSets.

If a claim set's issuer points to itself, you have reached the chain root (use ReferenceEquals to check this).

Typically you use these public methods from ClaimSet:

  • ContainsClaim
    Returns true/false if a specified claim can be found in the claim set
  • FindClaims
    Returns an IEnumerable<Claim> for all matches of a specifed claim type/right

LeastPrivilege.IdentityModel adds two extension methods to ClaimSet:

  • FindIdentityClaim
    Returns the identity claim of the claim set
  • HasIssuer
    Tells you if the claim set has an issuer

 

So much for the facts. In the next posts I will talk about where claim sets come from, what are typical operations you do on claim sets and how you use them for authorization.


IdentityModel
Saturday, February 23, 2008 8:26:01 PM UTC  #   
 Friday, February 22, 2008

thinktecture & DevelopMentor: Guerilla Enterprise .NET Kurs in München

Geballtes Know-How - für Sie!

thinktecture und DevelopMentor kündigen im Rahmen einer Partnerschaft das erste in Deutschland stattfindende "Enterprise .NET"-Event an. Es wird in der Woche vom 26. Mai 2008 in München stattfinden und im berühmt-berüchtigten "Guerilla"-Format abgehalten: Über 50 Stunden in 5 Tagen!

Lernen Sie, robuste verteilte Lösungen auf Basis der Windows Communication Foundation (WCF), Windows Workflow Foundation (WF) und Biztalk Server zu entwerfen und zu implementieren. Der „Guerilla Enterprise .NET“-Kurs zeigt Ihnen, wie man diese Technologien und Produkte anwendet und verteilte Systeme damit erstellt. Dabei wird auf den jeweiligen Stärken der Technologien aufgebaut und versucht, die Schwächen zu umgehen – alles im Sinne Service-orientierter Prinzipien.

Erhalten Sie Antworten auf diese und weitere Fragen:

  1. Was bedeutet Service-Orientierung wirklich?
  2. Wie erstelle ich interoperable Services?
  3. Wie kann ich einzelne „Building Block“ Services komponieren?
  4. Wie funktionieren Sessions und wann sollte ich sie nutzen?
  5. Wie kann ich lang andauernde Geschäftsvorfälle modellieren und ausführen?

Lernen Sie, skalierbare und flexible Service-basierte Anwendungen mit den aktuellen Technologien aus dem .NET Framework 3.5 zu bauen.

Highlights des Kurses

  • Verstehen Sie die Architektur von WCF, WF und Biztalk Server
  • Schreiben Sie Services für Internet- und Intranet-Szenarios
  • Erstellen Sie robuste lang laufende Workflows
  • Verstehen Sie die Unterschiede und jeweiligen Stärken von Biztalk Server und WF
  • Erstellen Sie asynchrone nachrichtenbasierte Anwendungen
  • Integrieren Sie Services und Workflows mit WCF und WF

Wir würden uns freuen, Sie am 26. Mai 2008 in München begrüßen zu dürfen - "Take your enterprise .NET programming skills to the next level!"

Registrierung und weitere Informationen können per Email an office@thinktecture.com abgewickelt werden.

Flyer

 

Infos zum Guerilla-Format:

Anders als bei "üblichen" Trainings werden Sie in den fünf Tagen ständig durch 3 Trainer betreut – Montag bis Donnerstag auch bis in den späten Abend hinein. Sie können Ihre Kenntnisse und Fähigkeiten bei Programmier-Challenges mit den anderen Teilnehmern messen und attraktive (und auf jeden Fall auch Geek-)Preise gewinnen.

Wir (Christian, Ingo und Ich) würden uns freuen Sie zu begrüßen!
Work in Progress
Friday, February 22, 2008 2:58:22 PM UTC  #   

 Tuesday, February 19, 2008

Using IdentityModel: Claims

A claim is a piece of information that you want to associate with an entity (usually a user) in your system. Commonly used claims are e.g. the name of a user or his roles.

The usual course of events is, that one part of a system creates some claims and associates them with a certain context (e.g. an HTTP request or the current user of the system) and another part of that system uses this information to make decisions. Most commonly claims are used for authorization.

Again you can compare this to how roles-based authorization works (role providers, IPrincipal & IIdentity and Thread.CurrentPrincipal). But a role is a very simple claim - it only allows a binary (yes/no) decision. If you want to base more complex authorization decisions on roles (e.g. allowed to transfer money up to 5000€) you usually have to make additional round trips to your authorization store from within your code (or you have to create a TransferMoney5000 role which of course is suboptimal because it couples business details with the role name - what do you do if the limit changes to 7000€ ?).

System.IdentityModel defines a more general purpose data structure in a class called Claim (simplified version):

[DataContract(
  Namespace = "http://schemas.xmlsoap.org/ws/2005/05/identity")]
public class Claim
{
  [DataMember(Name = "ClaimType")]
  public string ClaimType;
       
  [DataMember(Name = "Resource")]
  public object Resource;

       
  [DataMember(Name = "Right")]
  public string Right;
}

 

A claim consists of three pieces of data:

  • The claim type is just a string giving this claim a unique name. You should use http:// namespace URIs like http://www.leastprivilege.com/claims/transfermoney (though this format is not a must, I ran across problems with serialization in SAML tokens if you use a different format).
  • The resource is some additional arbitrary data that you can associate with the claim. This could be simple integer like 5000 (to stick with my above example) up to complete object models (like a customer history).
  • The right describes the type of the claim. There are two commonly used values here: Identity and PossesProperty. The right becomes more important when you group claims into claim sets (something I will talk about in the next post). You can get these values from the Rights class.

The ClaimTypes class defines a bunch of standard claim types (name, email, gender, webpage...) so you don't have to remember their namespace values. To create a simple name claim, you would write code like this:

Claim name = new Claim(ClaimTypes.Name, "Alice", Rights.Identity);

 

The Claim class also features several static methods that create standard claims, e.g.:

Claim name = Claim.CreateNameClaim("Alice");

 

The static methods always create PossesProperty claims. If you need to create an identity claim, you have to manually construct the claim object.

A custom TransferMoney claim could be created like this:

Claim transferMoneyClaim = new Claim(
    "http://www.leastprivilege.com/claims/transfermoney",
    5000,
    Rights.PossessProperty);

 

LeastPrivilege.IdentityModel defines three extension methods for the Claim class:

  • Get<T> and TryGet<T> to convert the resource to a specific type.
  • IsIdentity to check if the claim is an identity claim.

 

A single claim on its own is generally not very useful. Claims typically come as part of claim sets - the topic of the next post about IdentityModel. Stay tuned.


IdentityModel | ASP.NET | WCF | Work in Progress
Tuesday, February 19, 2008 3:45:28 PM UTC  #   

Using System.IdentityModel and LeastPrivilege.IdentityModel

Michael (a reader) recently wrote:

"You posted on your blog that System.IdentityModel is not tied to WCF...I understand how the claims, rights, and resources work. And I have created an Authorization Policy that implements IAuthorizationPolicy. But how do I make it all work together?"

I spent the last couple of months building some applications that use the classes found in System.IdentityModel for identity and access control. And I really like this model.

As with most of the other features introduced in .NET 3.0, the API is meant as a foundation (compare to WCF) and is a little raw. So while writing my code I also built a companion library boldly called LeastPrivilege.IdentityModel that adds functionality that makes it easier to build claims/identity based applications.

In the next couple of posts I will do a walkthrough of System.IdentityModel, how to use the classes found there (together), how they are used in WCF, how to use them outside of WCF and what my library adds to it.

If you have any specific questions - just post them here and I can try to incorporate them. Have fun.


Work in Progress | IdentityModel
Tuesday, February 19, 2008 7:41:51 AM UTC  #   
 Thursday, February 14, 2008

Be careful with ServiceAuthorizationManager.CheckAccess()

I wrote here about a new method to override in ServiceAuthorizationManager.

Yesterday suddenly a service began to behave very strangely after adding an authorization manager to it - and after some debugging I found out that my custom claims were missing and authorization broke all over the place.

After a little more digging it turned out that this was caused by implementing the new CheckAccess method without calling the base class implementation.

The call sequence in ServiceAuthorizationManager is a little strange (I guess that's the result of adding the new method without introducing a breaking change):

  1. WCF plumbing starts the authorization process
    (System.ServiceModel.Dispatcher.AuthorizationBehavior.Authorize (ref MessageRpc))
  2. CheckAccess(OperationContext, ref Message)
  3. CheckAccess(OperationContext)
  4. CheckAccessCore(OperationContext)

In step #3 the external authorization policies get added to the message which in turn means that the first access of AuthorizationContext afterwards triggers their evaluation. That means if you don't call the base class implementation, the external policies never get attached - hence my claims were missing.

This is working:

class TestAuthorizationManager : ServiceAuthorizationManager
{
    public override bool CheckAccess(OperationContext operationContext, ref Message message)
    {
        base.CheckAccess(operationContext, ref message);

        // authZ code
    }
}


WCF | Work in Progress
Thursday, February 14, 2008 9:57:01 PM UTC  #   
 Tuesday, February 12, 2008

Extending IIS 7 Configuration

If you want to add your own configuration sections (e.g. for this or this) to IIS 7 you have to write a schema file and dop it into the schema folder. From that point on it can be picked up by appcmd or ServerManager - all is good.

Thanks to Brock I found an article that shows how to extend existing configuration sections. I wanted to do that for my custom basic authentication module to live under the security element in IIS 7 but shied away from it because I didn't want to modify the built-in schema files. Now it turns out that IIS 7 can merge multiple schema files.

So if you want to extend an existing section - simply write a schema file for that section with your additional elements and attributes and drop it into the schema folder. Through the eyes of the configuration system the section will contain the built in and added information. Cool!

See also here.


ASP.NET | IIS | Work in Progress
Tuesday, February 12, 2008 8:33:01 AM UTC  #   
 Sunday, February 10, 2008

PowerTab

Fantastic tab completion add-on for Powershell.


For Your Favourites
Sunday, February 10, 2008 9:47:02 AM UTC  #   
 Saturday, February 09, 2008

My +1 List

I don't really like to just "+1" other blog posts I find - but Google Reader allows me to conveniently create a list of blog entries that I like to share with other people.

You can access this list here - and subscribe to it here.


For Your Favourites
Saturday, February 09, 2008 6:57:28 AM UTC  #   
 Friday, February 08, 2008

New HTTP.SYS Namespace Reservation in 3.5

A while ago I wrote about the "temporary listeners" wildcard namespace reservation that comes with .NET 3.0. I didn't like it - and my opinion hasn't changed here.

It turns out that .NET 3.5 adds a new reservation during installation:

c:\> netsh http show urlacl

Reserved URL            : http://+:8731/Design_Time_Addresses/
    User: NT AUTHORITY\INTERACTIVE
        Listen: Yes
        Delegate: No
        SDDL: D:(A;;GX;;;IU)

I believe this reservation is made to make the "WCF Service Library" and the accompanying generic WCF service host work.

Actually this reservation is much more sensible than the "temporary" one because:

  • it is not on a commonly used port which means that chances that the firewall is already open are relatively low. Remember that the "temporary" one points to port 80 which is also used by IIS, SQL Server WS-RemoteManagement etc...
  • It is using INTERACTIVE as opposed to WORLD (aka Everyone). This means that this reservation only applies to services started by the carbon unit operating the machine.

I had a discussion today with my friend Oliver who ran into the namespace reservation problem and thought that the best workaround would be to use the "temporary" namespace as it is already registered and doesn't require any command line fiddling. After I told him that I disagree, he asked me what would be the recommended way of handling this problem at development time (without elevating Visual Studio to admin all the time, of course ;).

I tackled this for me by reserving a dev time base address like http://+:9000/Services/ (ACLed only for myself) and key off all my services from that URI. But actually I think the new design time addresses make sense in design time ;) They are already there and have sensible settings for most standard development scenarios (but not for NT Services - since these are not interactive). Too bad the old "temporary" reservation didn't go away in 3.5...

Will try that in the future and see how it works out.


WCF | Work in Progress
Friday, February 08, 2008 2:19:00 PM UTC  #   
 Saturday, February 02, 2008

Information Cards and PPIDs (again)

Barry is a hard working guy. He implemented a complete STS for Information Cards - and believe me - this is no fun.

In this post he summarized some details about PPID generation and under which circumstances you can change parts of your application without getting new PPIDs (which is a major pain because you would basically lose card to user mappings in your application).

Barry and me are currently working on a more general paper about PPIDs and how to use them - in the meanwhile have a look at his post since it is a nice wrap up.


CardSpace | For Your Favourites
Saturday, February 02, 2008 10:43:23 PM UTC  #   
 Thursday, January 31, 2008

UserName SupportingToken in WCF

There has been some mentioning of supporting tokens recently (here and here). Supporting tokens allow adding additional tokens to a SOAP security header - at least one token (usually the primary token) must be capable of providing the necessary key material to secure the message - the other tokens are just additional information. This is quite interesting when you have typical multi-hop architectures.

The following scenario is quite common. How can this be accomplished using WCF?

  • Three participants: a client (using a browser), a web application and a back-end WCF service.
  • The web application uses forms authentication (or some other scheme where you end up with a username) to authenticate clients.
  • The web application uses a client certificate to talk to the back-end WCF service.
  • The WCF service needs the identity of the (browser) client for authorization.

Now there are several hand-rolled ways to accomplish this. You could add the client name as an operation parameter - or better as a header - and could parse this information in the WCF service. But if you already bought into the WCF security model  and especially claims and claims based authorization - the supporting token approach integrates more nicely.

WCF will create claim sets for all incoming tokens. In our case the back-end WCF service would end up with two claim sets - one for the web app client certificate and one for the browser client. Perfect.

How does this work?

First you have to configure a binding for supporting tokens. You can either create a new binding - or modify an existing one. This works by creating a token parameter description (in our case a UserNameTokenParameter) and setting the inclusion mode (derived key can be disabled here).

There are some decisions to make when adding a support token:

Optional or required
Supporting tokens can be either optional or required. You express this by using the xxxSupportingTokenParameters or the OptionalxxxSupportingTokenParameters collection on the SecurityBindingElement.

Scope
You can either add the token for some specific operations or the complete endpoint. Again there is a collection for Endpoint and Operation supporting tokens.

Token Usage
You can specify whether this token is used to generate key material (Endorsing and SignedEndorsing) or just as extra information (Signed or SignedEncrypted).

Modifying an existing binding involves calling the GetBindingElements method, finding the right binding element to modify and afterwards creating a new binding using the modifications. The code looks like this:

static Binding AddUserNameSupportingTokenToBinding(Binding binding)
{
    BindingElementCollection elements = binding.CreateBindingElements();

    SecurityBindingElement security =
      elements.Find<SecurityBindingElement>();
    if (security != null)
    {
        UserNameSecurityTokenParameters tokenParameters = new
          UserNameSecurityTokenParameters();
        tokenParameters.InclusionMode =
          SecurityTokenInclusionMode.AlwaysToRecipient;
        tokenParameters.RequireDerivedKeys = false;
        security.EndpointSupportingTokenParameters.SignedEncrypted.Add(
          tokenParameters);

        return new CustomBinding(elements.ToArray());
    }

    throw new ArgumentException(
      "Binding contains no SecurityBindingElement");
}

Of course you have to modify the binding both on the client and the service (see the sample download). You then set both credentials on the client side - the client certificate and the username part of the UserName credential.

Since this scenario is using a trusted subsystem approach, the back-end service trusts the web app that the client has already been properly authenticated. This means that you don't have to send the client's password to the WCF service - just leave it empty. This also means that you don't have to "remember" the client's password in the web app - which is desired.

To allow empty passwords on the service side you have to additionally provide a UserNamePasswordValidator which is happy with empty passwords. (again - the code is provided in the sample download).

When you got all this up and running - you have several places in your WCF service that are impacted by the additional token. First you get a new claim set - you will see an X509CertificateClaimSet for the direct caller's client certificate and a UserNameClaimSet that contains the name of the original client. You can inspect the claim sets like this:

private void ShowAllClaims()
{
    foreach (ClaimSet set in ServiceSecurityContext.Current.AuthorizationContext.ClaimSets)
    {
        Heading("IssuerClaims (" + set.Issuer.GetType().Name + ")");
        foreach (Claim claim in set.Issuer)
        {
            Console.WriteLine("{0}\n{1}\n{2}\n",
               claim.ClaimType,
               claim.Resource,
               claim.Right);
        }

        Heading("IssuedClaims (" + set.GetType().Name + ")");
        foreach (Claim claim in set)
        {
            Console.WriteLine("{0}\n{1}\n{2}\n",
               claim.ClaimType,
               claim.Resource,
               claim.Right);
        }
    }
}

You can also gain direct access to supporting tokens via the OperationContext:

if (OperationContext.Current.HasSupportingTokens)
{
    Heading("Supporting tokens:");
    foreach (SupportingTokenSpecification spec in OperationContext.Current.SupportingTokens)
    {
        Console.WriteLine(spec.SecurityToken.GetType().Name);
    }
}

Cool. Mission accomplished, right? Well - there is one gotcha.

 

The ServiceSecurityContext.PrimaryIdentity typically returns an IIdentity for the caller of the service. I was surprised to see that when you are adding a supporting token to a message the primary identity suddenly changes to Anonymous. Inspecting the code with Reflector shows that the logic of PrimaryIdentity is like this:

  • if there is a single identity, return this identity
  • if there is no identity or more than one, return anonymous.

Maybe I am misunderstanding the concept of primary identity - but I thought the primary identity should still point to my direct caller whereas the supporting token is just some secondary identity information. So the current behavior feels wrong to me.

If you are relying on PrimaryIdentity somewhere in your code - be aware that adding a supporting token will break that code.

 

Workarounds
Primarily, I guess you have to be aware of that behavior. If you need the direct caller on PrimaryIdentity you could write an IAuthorizationPolicy that sets a single identity on the Properties dictionary at the end of the evalution cycle. Haven't tested that - but should work.

I chose to write two extension methods for ServiceSecurityContext that give me more options when working with identities. If you are on 3.5 you can add these to your toolbox.

The first one returns all available identites:

public static IList<IIdentity> GetIdentities(this ServiceSecurityContext context)
{
    return GetIdentities(context.AuthorizationContext);
}

private static IList<IIdentity> GetIdentities(AuthorizationContext authorizationContext)
{
    object list;
    if ((authorizationContext != null) && authorizationContext.Properties.TryGetValue("Identities", out list))
    {
        return (list as IList<IIdentity>);
    }

    return new List<IIdentity>();
}

 

The second one returns the first identity (or anonymous if no identity exists):

public static IIdentity GetFirstIdentity(this ServiceSecurityContext context)
{
    IList<IIdentity> identities = GetIdentities(context.AuthorizationContext);
    if (identities.Count > 0)
    {
        return identities[0];
    }
    else
    {
        return new GenericIdentity(string.Empty);
    }
}

 

SupportingUserNameToken.zip (42.56 KB)

HTH


ASP.NET | WCF | Work in Progress
Thursday, January 31, 2008 8:26:51 AM UTC  #   
 Tuesday, January 29, 2008

WCF 3.5 and Partial Trust

This is a good summary of things that work and don't work when WCF is used in partially trusted AppDomains.

I especially like this sentence:

"The best way to discover that a piece of information or action is unavailable when running in a partial trust environment is to try to access the resource or do the action inside of a try block, and then catch the failure."

This pretty much describes the situation we currently have in CAS.


WCF
Tuesday, January 29, 2008 2:49:17 PM UTC  #   
 Saturday, January 26, 2008

Web Deployment Projects for Visual Studio 2008

(note to self)

download here.


For Your Favourites
Saturday, January 26, 2008 8:34:39 AM UTC  #   
 Thursday, January 24, 2008

Jörg Neumann in da House

I am very happy to announce that Jörg Neumann has joined thinktecture.

Jörg is the UI master, a SQL Server expert and an overall really great guy. Looking forward working with him!

http://www.thinktecture.com/staff/joerg


Misc
Thursday, January 24, 2008 8:47:28 AM UTC  #   

LeastPrivilege on RunAs Radio

A few weeks ago I did a recording with Richard and Greg for RunAs Radio. That was fun.

It is now online. Have fun and support the show!

http://www.runasradio.com/default.aspx?showNum=41


For Your Favourites
Thursday, January 24, 2008 8:13:29 AM UTC  #   
 Friday, January 18, 2008

ASP.NET Internals Spelunking II

Suppose you want to step through one of the built-in HttpModules in ASP.NET. This doesn't work by traversing the callstack. There is another little "trick" to get this working:

  • Go to Debug/New Breakpoint/Break at Function
  • Enter the function name, e.g. FormsAuthenticationModule.OnEnter and select "C#"
  • Ignore the error message
  • Hit F5

Unfortunately you'd first have to use Reflector to find the function name before you can set a breakpoint. Could be easier.

Have fun!


ASP.NET | Work in Progress
Friday, January 18, 2008 6:19:02 PM UTC  #   

ASP.NET Internals Spelunking

When I wrote the ASP.NET book I pretty much lived in Reflector 24/7 to figure out all the gory implementation details. Back then it would have been great to be able to simply set breakpoints in some of the low level classes like HttpRuntime or modules.

Fortunately this is now possible, here's a quick walkthrough:

  • Set up your Visual Studio to work with the new .NET symbols. Also have a look at the various symbol loading options you have.
  • Open an ASP.NET app
  • Set a breakpoint somewhere in your code (e.g. in a Page_Load)
  • Let the debugger hit the breakpoint
  • Open the call stack window and navigate up the stack, e.g. to HttpRuntime.ProcessRequest or Page.ProcessRequest
  • Set a breakpoint (use HttpRuntime.Init or the (c)ctor to step through the whole initialization process)
  • Right click the breakpoint, select location and check the "Allow the source code to be different from the original version" option.
  • Debug again. The debugger should now hit the breakpoint in the ASP.NET infrastructure class
  • Depending on how early in processing you set the breakpoint, you may have to recycle the AppDomain to start over. Simply make a change to web.config and save to trigger recycling.

 

Happy spelunking ;)


ASP.NET | Work in Progress
Friday, January 18, 2008 9:50:03 AM UTC  #   
 Wednesday, January 16, 2008

Simplified Impersonation Model

While playing with the new Named Pipe classes in 3.5 I noticed that a simpler model for impersonating the client is used here. Instead of putting the burden on the user to call Impersonate on a WindowsIdentity (and making sure that impersonation is undone correctly), you simply pass a delegate that should run impersonated to RunAsClient...interesting.

using (var server = new NamedPipeServerStream("pipe"))
{
    Console.WriteLine("waiting");
    server.WaitForConnection();
    Console.WriteLine("connected");

    using (var reader = new StreamReader(server))
    {
        string message = reader.ReadLine();

        string client = server.GetImpersonationUserName();
        Console.WriteLine("{0} says {1}", client, message);

        // runs under client identity
        server.RunAsClient(delegate
        {
            Console.WriteLine("impersonated identity: {0}",
                WindowsIdentity.GetCurrent().Name);
            
            // access some resource
        });
    }
}

FX Security | Samples | Work in Progress
Wednesday, January 16, 2008 6:15:28 AM UTC  #   
 Tuesday, January 15, 2008

Most popular Content in 2007

Just looked through my server logs...

Top Downloads:

  1. InfoCardSelector for ASP.NET (1508)
  2. HTTP.SYS config utility (819)
  3. GetCertKeyFile (732)
  4. IfConfig 2.11 (683)
  5. SslHelper (588)

 

Top Documents:

  1. PenTest Slides from WinDev 2004 (1682)
  2. Integration mit Windows Security from Basta 2006 (1125)
  3. Hackproofing IIS6 from ADC 2004 (924)
  4. Smart Client Deployment Security from Basta 2006 (643)
  5. New Security Features in .NET 2.0 from DevWeek 2005 (628)

 

Now back to serious work....;)


Misc
Tuesday, January 15, 2008 6:47:23 AM UTC  #   
 Saturday, January 12, 2008

HTTP Basic Authentication against Non-Windows Accounts in IIS7

The last posts (here, here, here and here) show how to build an HTTP Basic Authentication module using a membership provider (and including WCF support). Moving this code to IIS7 is technically not very difficult - but the mindset changes. In IIS6 a HTTP module is used to extend the managed ASP.NET pipeline. In IIS7 you are now building extensibility code for the web server itself. This has some implications:

  • The HTTP module is not registered in <system.web /> anymore. Since you extend IIS7, the module gets registered in <system.webServer /> now.
  • Same applies to the <customBasicAuthentication /> configuration section for the module itself.
  • The module now has easy access to the IIS security configuration and can behave diffently based on that
  • A proper IIS7 feature should also have a corresponding UI in the IIS Manager - including support for remote administration

I have already written a walkthrough on how to build IIS7 modules (here, here, here, here, here, here and here). I recommend reading them, since most of the code and structure is boilerplate.

The only real difference between the ServerHeader sample and the Basic Authentication UI integration is, that the authentication UI can register in the IIS7 Manager under the Authentication category:

From there you can open the custom Basic Authentication UI which controls the configuration settings in web.config for the currently selected application. Everything falls into place.

To register as a new authentication method you need to derive from a class called AuthenticationFeature. In this class you do some basic state management and are responsible for loading and showing the configurationUI. After that you register the feature in the Module derived class and call RegisterExtension on the extensibility manager.

// the module registers the custom UI with InetMgr
public class CustomBasicAuthenticationModule : Module
{
    protected override void Initialize(IServiceProvider serviceProvider, ModuleInfo moduleInfo)
    {
        base.Initialize(serviceProvider, moduleInfo);

        // add our module to the authentication section in the GUI
        IExtensibilityManager manager = 
            (IExtensibilityManager)serviceProvider.GetService(typeof(IExtensibilityManager));
        
        if (manager != null)
        {
            manager.RegisterExtension(
                typeof(AuthenticationFeature), 
                new CustomBasicAuthenticationFeature(this));
        }
    }
}

 

Download the full source here.

 


ASP.NET | IIS | WCF | Work in Progress
Saturday, January 12, 2008 8:49:51 PM UTC  #   

HTTP Basic Authentication against Non-Windows Accounts in IIS/ASP.NET (Part 3 - Adding WCF Support)

Taking this post as a starting point, we now have a working HTTP module that implements HTTP Basic Authentication against a membership provider. This is fine for ASP.NET applications and content, but to integrate a WCF service, there is some extra work necessary.

Even though WCF is technically also an HTTP module/handler participating in the ASP.NET pipeline, the WCF architects decided to keep a WCF service separate from the hosting environment. This means e.g. that you don't get access to the current HttpContext from within WCF by default.

Let's start with the service configuration itself. The service uses the basicHttpBinding and is configured for Transport security. Note that the clientCedentialType is set to None - this means that WCF itself is not doing any authentication. This is done by the HTTP module.

<system.serviceModel>
  <services>
    <service name="WcfService"
             behaviorConfiguration="Behavior">
      <endpoint address=""
                binding="basicHttpBinding"
                bindingConfiguration="CustomBasicAuth"
                contract="IWcfService" />
    </service>
  </services>

  <!-- turn off authentication in WCF, we get the client from the HttpContext -->
  <bindings>
    <basicHttpBinding>
      <binding name="CustomBasicAuth">
        <security mode="Transport">
          <transport clientCredentialType="None" />
        </security>
      </binding>
    </basicHttpBinding>
  </bindings>

  <!-- enable metadata -->
  <behaviors>
    <serviceBehaviors>
      <behavior name="Behavior">
        <serviceMetadata httpGetEnabled="true" />
      </behavior>
    </serviceBehaviors>
  </behaviors>
</system.serviceModel>

The next step is to allow WCF to access the ASP.NET HttpContext to be able to access HttpContext.User which holds the identity of the client as authenticated by the Basic Authentication HttpModule.

This is done by adding a configuration setting to <system.serviceModel /> in web.config:

<!-- to enable IIS authentication for WCF, we have to switch to compatibility mode -->
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />

Additionally you also need to decorate the service implementation to opt-in to ASP.NET compatibility mode:

[AspNetCompatibilityRequirements(RequirementsMode=AspNetCompatibilityRequirementsMode.Required)]
public class WcfService : IWcfService

Now you could access HttpContext.Current.User in a service operation. This is not very natural for a WCF service developer since in WCF the client identity is typically exposed via ServiceSecurityContext.Current.PrimaryIdentity. To propagate the user information properly from ASP.NET to WCF you can use an IAuthorizationPolicy. This code takes the value from Context.User.Identity and puts it into a special dictionary which is later picked up by WCF to construct the ServiceSecurityContext.In addition you could also add claims for the user here.

// syncs ServiceSecurityContext.PrimaryIdentity in WCF with whatever is set 
// by the HTTP pipeline on Context.User.Identity (optional)
public class HttpContextIdentityPolicy : IAuthorizationPolicy
{
    public bool Evaluate(EvaluationContext evaluationContext, ref object state)
    {
        HttpContext context = HttpContext.Current;

        if (context != null)
        {
            // set the identity (for PrimaryIdentity)
            evaluationContext.Properties["Identities"] = 
                new List<IIdentity>() { context.User.Identity };

            // add a claim set containing the client name
            Claim name = Claim.CreateNameClaim(context.User.Identity.Name);
            ClaimSet set = new DefaultClaimSet(name);
            evaluationContext.AddClaimSet(this, set);
        }

        return true;
    }

    public System.IdentityModel.Claims.ClaimSet Issuer
    {
        get { return ClaimSet.System; }
    }

    public string Id
    {
        get { return "LeastPrivilege HttpContextIdentityPolicy"; }
    }
}

You wire up this policy in the <serviceAuthorization /> behavior:

<serviceAuthorization>
  <authorizationPolicies>
    <!-- sync ServiceSecurityContext.PrimaryIdentity with Context.User.Identity -->
    <add policyType="LeastPrivilege.HttpContextIdentityPolicy, LeastPrivilege.HttpContextPolicies" />
  </authorizationPolicies>
</serviceAuthorization>

Another "slot" for security information in WCF is Thread.CurrentPrincipal. This is used to support classic role-based IsInRole calls and the [PrincipalPermission] attribute. T.CP gets populated according to another configuration attribute in the <serviceAuthorization /> behavior called principalPermissionMode.

By default WCF tries to create a WindowsPrincipal which wouldn't work here. Another option would be to associate the user with an ASP.NET role provider by setting the principalPermissionMode to UseAspNetRoles. In this case T.CP is populated with a RoleProviderPrincipal which takes the user name from ServiceSecurityContext.PrimaryIdentity to find associated roles from a configured role provider datastore. Also read this post if you plan to use a role provider with WCF.

The most flexible option is to create your own custom IPrincipal and put it on Thread.CurrentPrincipal. This way you can optimize the IsInRole implementation to your specific back-end credential store. I wrote about this in detail here.

The sample app sets a custom IPrincipal by copying the IPrincipal provided by the ASP.NET pipeline to the WCF world. This may be useful if you want to share an existing principal implementation between ASP.NET and WCF:

// syncs Thread.CurrentPrincipal in WCF with whatever is set 
// by the HTTP pipeline on Context.User (optional)
public class HttpContextPrincipalPolicy : IAuthorizationPolicy
{
    public bool Evaluate(EvaluationContext evaluationContext, ref object state)
    {
        HttpContext context = HttpContext.Current;

        if (context != null)
        {
            evaluationContext.Properties["Principal"] = context.User;
        }

        return true;
    }

    public System.IdentityModel.Claims.ClaimSet Issuer
    {
        get { return ClaimSet.System; }
    }

    public string Id
    {
        get { return "LeastPrivilege HttpContextPrincipalPolicy"; }
    }
}

 

So that's it. You can now write an IIS hosted service that uses HTTP Basic Authentication against custom accounts while preserving the usual WCF security programming model. As you've seen this involves some plumbing and configuration and the interaction between all these configurations and extensibility points is sometimes quite confusing. Would be nicer if this functionality would be provided out of the box...

Attached are the WCF specific parts of the sample project. Just insert them into the download from the last post.

 


ASP.NET | WCF | Work in Progress
Saturday, January 12, 2008 7:48:22 PM UTC  #   

HTTP Basic Authentication against Non-Windows Accounts in IIS/ASP.NET (Part 3 - Setting up IIS6/ASP.NET)

In the previous post I explained how the authentication module works. Now which steps are required to get the demo app included in the download to work?

IIS

  1. Create a vdir in IIS that points to the sample site.
  2. Mark it as an application.
  3. Disable all authentication methods besides "anonymous".
  4. If you want to protect static content (e.g. jpg or xml), you also have to add the aspnet ISAPI extension as a wildcard mapping. Go to the application properties -> Home Directory -> Configuration. Go to the tab with all the script mappings, pick e.g. the .aspx mapping and copy the path to the ISAPI dll to the clipboard. In the bottom part of the same dialog add a wildcard mapping and set the previously copied path. Now ASP.NET will get invoked for all incoming requests.

This should be it. Now navigate to default.aspx in the browser and use alice/secret as the credential. You can also try the included asmx client. Be aware that you may have to change the requireSSL config attribute and/or the URLs.


ASP.NET | WCF | Work in Progress
Saturday, January 12, 2008 5:29:31 PM UTC  #   

HTTP Basic Authentication against Non-Windows Accounts in IIS/ASP.NET (Part 2 - The HTTP Module)

An HTTP module is one of the main extensibility points in ASP.NET/IIS7. Modules subscribe to notifications of certain stages in the HTTP request/response processing. Inside of the event handlers you can then inject your custom code and logic.

To allow some flexibility in the module's logic there is also a configuration section where the following settings can be configured:

  • enabled yes/no
  • realm name
  • the membership provider that should be used to validate credentials (default means use the default provider, otherwise provide the name of the provider)
  • require SSL
  • should credential caching be enabled - if yes how long (I mimick here the IIS built-in token caching and default to 15 minutes)

The following configuration section reflects these options:

<!-- custom basic authentication configuration -->
<customBasicAuthentication enabled="true"
                           realm="leastprivilege"
                           providerName="default"
                           cachingEnabled="true"
                           cachingDuration="15"
                           requireSSL="true" />

I skip the details on how to create configuration sections. There are also some differences when it comes to support IIS 6 and 7. In IIS6 this section would go to <system.web /> whereas in IIS 7 <system.webServer /> would be used.

The next step is to write a class that implements the IHttpModule interface and its Init method. Inside of Init the module subscribes to two events in the pipeline: AuthenticateRequest and EndRequest:

// event registration
public void Init(HttpApplication context)
{
    context.AuthenticateRequest += OnEnter;
    context.EndRequest += OnLeave;
}

What's happening in AuthenticateRequest
Here the module hast to distinguish between two states:

  • No Authorization header present.
    This means the request is anonymous. The module now has to check if anoynmous access is allowed - if this is the case, the module does nothing and passes the request on. Right after the AuthenticateRequest stage runs the AuthorizeRequest stage. If one of the subscribed modules determines that authentication is needed for the requested resource, it will put a 401 on the status code and jump directly to EndRequest. Here our EndRequest logic kicks in (more on that in a second).
  • Authorization header is present.
    This means the user tries to authenticate. In this case the module extracts the credentials from the header and passes them on to the configured membership provider (you could of course plugin whatever credentials verification logic you want).

Checking if anonymous access is configured in IIS is only included in the IIS7 version of the module.

The corresponding code looks like this:

void OnEnter(object sender, EventArgs e)
{
    HttpContext context = HttpContext.Current;
    
    // check if module is enabled
    if (!Configuration.Enabled)
        return;

    // check if SSL is required and enabled
    if (Configuration.RequireSSL && !context.Request.IsSecureConnection)
    {
        throw new HttpException(403, "SSL required for Basic Authentication");
    }

    // try to authenticate user - otherwise set status code and end request
    if (IsHeaderPresent)
    {
        if (!AuthenticateUser())
        {
            DenyAccess();
        }
    }
    else
    {
        // if anonymous requests are not allowed - end the request
        if (!IsAnonymousAllowed)
        {
            DenyAccess();
        }
    }
}

The DenyAccess method sets the 401 status code and ends the current request. Ending a request means that processing jumps directly to EndRequest. This is were the OnLeave handler kicks in.

private static void DenyAccess()
{
    HttpContext context = HttpContext.Current;

    context.Response.StatusCode = 401;
    context.Response.End();
}

 

What's happening in EndRequest?
The logic in EndRequest is simple. It checks if a 401 status code is set (by the module itself or maybe by an authorization module or a page) and if that is the case, sends the necessary HTTP headers back to start the authentication handshake.

void OnLeave(object sender, EventArgs e)
{
    // check if module is enabled
    if (Configuration.Enabled)
    {
        if (HttpContext.Current.Response.StatusCode == 401)
        {
            SendAuthenticationHeader();
        }
    }
}

 

Authenticating the user
The authentication logic uses a membership provider under the covers and additionally implements caching of the client "token".

private bool AuthenticateUser()
{
    string username = "", password = "";
    string authHeader = HttpContext.Current.Request.Headers["Authorization"];

    if (authHeader != null && authHeader.StartsWith("Basic"))
    {
        // extract credentials from header
        string[] credentials = ExtractCredentials(authHeader);
        username = credentials[0];
        password = credentials[1];

        if (IsCredentialCached(username, password))
        {
            SetPrincipal(username);
            return true;
        }
        else if (Provider.ValidateUser(username, password))
        {
            if (Configuration.CachingEnabled)
            {
                CacheCredential(username, password);
            }

            SetPrincipal(username);
            new AuthenticationSuccessEvent(this, username).Raise();

            return true;
        }
    }

    new AuthenticationFailureEvent(this, username).Raise();
    return false;
}
The AuthenticationFailureEvent/AuthenticationSuccessEvent classes are Health Monitoring WebEvents (System.Web.Management). They push tracing information out to configured listeners. The nice thing is that in IIS7 you can forward these WebEvents to the FREB tracing infrastructure.

 

Caching
Once the Authorization header is initially set, it gets re-sent on every subsequent request. This means that the authentication logic kicks in every time. If you would do a (remote) database roundtrip to your credential store on every authentication request, this could impact performance.

To improve this situation, you can implement a simple caching scheme:

  • Once the use is authenticated, cache a unique identifier for that username/password pair
  • When re-authentication happens, re-create that unique identifier from the supplied credentials and check if this id is in the cache

The two methods IsCredentialCached and CacheCredential implement this logic:

// caches a credential identifier
private void CacheCredential(string username, string password)
{
    string identifier = GetIdentifier(username, password);

    try
    {
        HttpContext.Current.Cache.Add(
            identifier,
            "ok",
            null,
            DateTime.Now.AddMinutes(Configuration.CachingDuration),
            Cache.NoSlidingExpiration,
            CacheItemPriority.Normal,
            null);

        new CredentialCacheAddEvent(this, username).Raise();
    }
    catch (Exception ex)
    {
        new CredentialCacheAddErrorEvent(this, username, ex).Raise();
    }
}// checks if the credential has been cached already
private bool IsCredentialCached(string username, string password)
{
    if (!Configuration.CachingEnabled)
        return false;

    string identifier = GetIdentifier(username, password);
    bool cacheHit = (HttpContext.Current.Cache[identifier] != null);

    if (cacheHit)
    {
        new CredentialCacheHitEvent(this, username).Raise();
    }
    else
    {
        new CredentialCacheMissEvent(this, username).Raise();
    }

    return cacheHit;
}

Creating an identifier
To get rid of any clear text secrets in your cache, simply hash the username and password. If you are paranoid, add a salt to it - but if an attacker is so close to your RAM to read the cache, you are in trouble anyways ;)

// create a string identifier for the cache key
// format: prefix + Base64(Hash(username+password))
private string GetIdentifier(string username, string password)
{
    // use default hash algorithm configured on this machine via CryptoMappings
    // usually SHA1CryptoServiceProvider
    HashAlgorithm hash = HashAlgorithm.Create();

    string identifier = username + password;
    byte[] identifierBytes = Encoding.UTF8.GetBytes(identifier);
    byte[] identifierHash = hash.ComputeHash(identifierBytes);

    return _cachePrefix + Convert.ToBase64String(identifierHash);
}

Setting the authentication header
The last missing piece is the code that sets the Authenticate header and kicks off the authentication handshake.

// send header to start Basic Authentication handshake
private void SendAuthenticationHeader()
{
    HttpContext context = HttpContext.Current;

    context.Response.StatusCode = 401;
    context.Response.AddHeader(
        "WWW-Authenticate",
        String.Format("Basic realm=\"{0}\"", Configuration.Realm));
}

 

OK - that's it. You can download the full code here. There is also a test webapp included. In the next post I will detail how to configure IIS/ASP.NET to use the authentication module.


ASP.NET | WCF | Work in Progress
Saturday, January 12, 2008 10:42:42 AM UTC  #   
 Friday, January 11, 2008

HTTP Basic Authentication against Non-Windows Accounts in IIS/ASP.NET (Part 1 - Basic Authentication)

The first step in implementing an authentication module for IIS/ASP.NET is to understand the authentication protocol (doh ;)

It turns out the Basic Authentication is quite simple. Whenever the server wants to start the authentication handshake, he sends a 401 HTTP status code with a special HTTP header named WWW-Authenticate e.g.

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Basic realm="leastprivilege"

When a browser receives such an HTTP response, he typically opens a logon dialog box. The realm is some extra parameter that can be used by the client.

After the user has entered his credentials, these are sent back to the server as a base64 encoded HTTP header named Authorization:

GET /default.aspx HTTP/1.1
Authorization: Basic YWxpY2U6Z29vZHdvcmtub3d5b3VjYW5zZWVteXBhc3N3b3Jk

The server then grabs the authorization header, decodes it, and verifies the credentials against some back end store. Very simple. After that the server either grants access or returns an HTTP 403 (forbidden).

Some notes

  • the value of the Authorization header is using the iso-8859-1 character set.
  • the credentials are only base64 encoded. This means they are sent in clear text. You should only use Basic Authentication when you layer SSL on top of it.
  • IIS forces authentication when Basic Authentication is the only selected authentication method.
  • When anonymous access is allowed too, IIS only starts the authentication handshake when it sees a 401 status code coming back (e.g. from ASP.NET's URL authorization)
  • IIS uses the username/password values to call Win32 LogonUser. The result is a Windows token.
  • the Authorization header is sent to the server on every request after the user entered credentials. That means the server has to somehow verify the credentials on each request.
  • IIS caches the Windows token - by default for 15 minutes. This can be configured in HKLM\System\CurrentControlSet\Services\InetInfo\Parameters\UserTokenTTL

OK - now we understand how the protocol works. In the next post I will show you how to use the HTTP pipeline to implement the authentication module.


ASP.NET | IIS | WCF | Work in Progress
Friday, January 11, 2008 6:13:57 PM UTC  #   

HTTP Basic Authentication against Non-Windows Accounts in IIS/ASP.NET (Part 0 - Intro)

Imagine this (quite common) scenario: You have some web content/resources (e.g. static files, aspx pages, asmx or WCF services) in IIS that you want to protect using HTTP Basic Authentication. The problem is, that when you enable Basic Authentication in IIS - all authentication is done against Windows accounts. That means that IIS grabs the username/password pair from the HTTP header and tries to create a Windows token using these credentials. This is a problem when you don't have Windows accounts for your clients, but rather want to store the accounts in e.g. a database (which is much more common IMHO).

Unfortunately IIS (including IIS 7) does not support this out of the box. And to make things worse, WCF with the new support for usernames over transport can't help here either.

A while ago I wrote all the necessary code to enable this scenario for IIS 6/7 and since I get a lot of questions about this, I will post the bits here step-by-step.

In the next days I will post the following series of blog entries:

  • How does Basic Authentication work
  • The Basic Authentication HTTP module
  • Using the module in IIS 6
  • Adding WCF support
  • Adding IIS7 support (configuration and UI module)

Stay tuned!


ASP.NET | WCF | Work in Progress
Friday, January 11, 2008 8:24:39 AM UTC  #   

System.AddIn Pipeline Builder

I recently spent quite some time in the System.AddIn namespace. I really like the new extensibility model and once you take the initial hurdle of understanding the infrastructure (or pipepline in MAF-speak) it is a really sensible model.

To get you started more easily the CLR AddIn guys (Jesse, Jack, TQ) have published a tool that allows code-gen'ing the complete pipeline from the contract. Give it a try!

http://www.codeplex.com/clraddins


For Your Favourites | AddIns
Friday, January 11, 2008 6:57:09 AM UTC  #   
 Thursday, January 10, 2008

System.DirectoryServices.AccountManagement

Looking through some of the new 3.5 stuff I stumbled over a new assembly named "System.DirectoryServices.AccountManagement" - that caught my attention.

The whole namespace reminds a little bit of ADSI - an API tailored to create user, group and machine accounts (local and domain). There are some easy to use classes for common tasks. Have a look yourself.

Two examples of things that can be achieved with S.DS.AM are:

Verifying the password of a user:

public static bool ValidateCredentials(string username, string password, ContextType type)
{
    return new PrincipalContext(type).ValidateCredentials(username, password);
}

Checking if a given user is a member of some group:

public static bool IsUserInGroup(string username, string groupname, ContextType type)
{
    PrincipalContext context = new PrincipalContext(type);

    UserPrincipal user = UserPrincipal.FindByIdentity(
        context,
        IdentityType.SamAccountName,
        username);
    GroupPrincipal group = GroupPrincipal.FindByIdentity(
        context, groupname);

    return user.IsMemberOf(group);
}

In both cases the ContextType could be either Machine, Domain or a ADAM database...


Work in Progress
Thursday, January 10, 2008 7:55:00 PM UTC  #   
 Tuesday, January 08, 2008

Configuration Section Designer

Writing configuration sections is tedious - a perfect candidate for code-gen.

Try this. Very nice.


For Your Favourites
Tuesday, January 08, 2008 6:54:20 AM UTC  #   
 Friday, January 04, 2008

Walkthrough: Setting up a self hosted WCF Service with SSL

This is really just a compilation of already known bits of information. But since I had to do this recently and was struggling to get these pieces together, I thought I'll document all the steps here.

Scenario:
WCF service that uses transport security with an HTTP based binding. The service is self-hosted using a least privilege account. How does that work?

 

1. Create and install an SSL certificate

The first step is to install an SSL certificate on the target machine. SSL certs are just normal certs with an intended usage of "Server Authentication" (and Key Exchange). You can get them from public CAs like Verisign, an internal CA like Windows Server Certificate Services or generate them yourself using tools like makecert or selfssl (for testing purposes only of course).

The syntax for makecert would be (admin privs required):

makecert -r -pe -n CN="DNS_NAME_OF_SERVICE" -eku 1.3.6.1.5.5.7.3.1 -ss my -sr localmachine
-sky exchange -sp "Microsoft RSA SChannel Cryptographic Provider" -sy 12

SSL certificates must be installed into the "Personal" certificate store of the machine account (the above makecert command does this).

Afterwards - browse to the certificate using the MMC snap-in and make sure it is marked as valid and the details dialog says "You have a private key that corresponds to this certificate"

2. Map the SSL cert to the WCF listening port

For the next step you need several pieces of data beforehand.

  • the port on which your service will listen on
  • the thumbprint of the SSL certificate you want to use. Get this data e.g. from the MMC snap-in details dialog. Copy the "Thumbprint" value to a text editor and remove the whitespaces.
  • A GUID. Use e.g. the Visual Studio "Create GUID" tool (use the registry format).

On Vista+ you can use the netsh tool to do the mapping (admin privs required):

netsh http add sslcert ipport:0.0.0.0:port certhash=thumbprint appid=GUID

On pre-Vista you can use httpcfg.exe to accomplish the same thing:

httpcfg set ssl /i 0.0.0.0:port /h thumbprint /g "GUID"

 

You can also use the corresponding tools to view the mapping afterwards.

 

3. Set an URL ACL for the listening URI

The last step is to set an URI ACL on the listening URI for the service account (see also here). You should use the base address of the service as the entry URI.

Again there are two tools to accomplish this. On Vista use e.g.:

netsh http add urlacl url=https://+:port/Services user=authority\serviceaccount

For pre-Vista use e.g. this tool.

 

4. Configure the WCF service

The WCF service has to be configured for transport security (and an https protocol moniker) and for the base address you ACLed in step 4. My config looks like this:

<system.serviceModel>
  <services>
    <service name="Service"
             behaviorConfiguration="md">
      <endpoint address="SslService"
                binding="basicHttpBinding"
                bindingConfiguration="security"
                contract="IService" />

      <host>
        <baseAddresses>
          <add baseAddress="https://WcfSelfHost:4433/Services" />
        </baseAddresses>
      </host>
    </service>
  </services>

  <bindings>
    <basicHttpBinding>
      <binding name="security">
        <security mode="Transport">
          <transport clientCredentialType="Basic" />
        </security>
      </binding>
    </basicHttpBinding>
  </bindings>

  <behaviors>
    <serviceBehaviors>
      <behavior name="md">
        <serviceMetadata httpsGetEnabled="true" />
      </behavior>
    </serviceBehaviors>
  </behaviors>
</system.serviceModel>

 

HTH


WCF
Friday, January 04, 2008 10:57:24 AM UTC  #   
 Thursday, January 03, 2008
 Thursday, December 27, 2007

The Future of the ASP.NET InfoCardSelector Control

From now on I will make all updates to the control code available via this link.

The zip contains the control itself plus the code for the demo site you can find here. I will also include a changelog in the near future.

I thought about moving the control to Codeplex - would anyone be interested in contributing? What features would you like to see?


CardSpace
Thursday, December 27, 2007 9:52:24 AM UTC  #   
 Wednesday, December 19, 2007

Demo Site for InfoCardSelector ASP.NET Control

I have uploaded a mini site to test the InfoCardSelector ASP.NET control. The site features three options: self-issued cards with and without SSL and a managed card (using the Microsoft FederatedIdentity test STS).

Have fun!

http://www.leastprivilege.com/InfoCardSelector


CardSpace
Wednesday, December 19, 2007 6:23:13 PM UTC  #   
 Sunday, December 16, 2007

Updated InfoCardSelector ASP.NET Control for No-SSL Scenarios

Starting with .NET 3.5, CardSpace does not demand SSL connections anymore (see here). Kim shows how to get this scenario to work with "less than 30 lines of code" (link). This involves setting up the object tag, retrieving the clear text token from the post data and extracting the claims using XPATH. Easy. I think Keith uses the right term for that which is "Quick and Dirty".

Quoting from Kim's screencast: "If interception is not a concern, you can ignore the signature section in the token. The PPID becomes the shared secret between you and the web application."

While I agree with Kim that this (especially for the low security web app scenarios he is talking about) is much better than a password, I still have some comments:

  • using the PPID only brings us back into the world of shared secrets (and that is what we are trying to get away from with InfoCards).It is generally agreed that PPIDs only should not be used for authentication (see here). If someone can eavesdrop the connection he could simply create a new token, copy the PPID and impersonate the user (which is of course the same with clear text passwords or cookies over non protected transports).
  • InfoCard tokens include a public key with which the signature can be verified. This public key is a much better identifier for the user. The corresponding private key (which is the real secret) is never transmitted over the wire and much harder to spoof than a PPID.
  • removing SSL also removes all SSL provided security features like man-in-the-middle, spoofing and replay protection. So you can replay tokens anyway. Still - relying on the public key is better because tokens have a limited lifetime. This means you can only replay the complete token for a limited amount of time. The PPID alone can be basically replayed forever (much like a password besides that you cannot change PPIDs once they got compromised).
  • PPIDs are only guaranteed to be unique per site in self-issued cards scenarios. Depending on the STS implementation (e.g. non-auditing or just a bad one) the PPID may be always the same. But in managed card scenarios you should require SSL anyways.

Anyways, the Token class shipping with the ASP.NET InfoCard Toolkit cannot handle No-SSL scenarios out of the box - nor can my InfoCardSelector ASP.NET control (which is based on the Token class). I updated both so that they work fine now.

My control does verify the signature and thus gives you the choice of using the public key, PPID or whatever other combination you like. The programming model stays the same regardless of SSL or not.

Simply add a NoSSL="true" attribute to the control tag (see the DefaultNoSSL.aspx page as an example).

(download)

 


CardSpace
Sunday, December 16, 2007 9:29:37 AM UTC  #   
 Friday, December 07, 2007

Least Privilege, WCF and Named Pipes

Christian ran into an interesting problem with least privileged WCF service accounts and the NetNamedPipeBinding.

 

Bottom line: I can only say it again: develop and test your stuff using a standard user account. Don't do it for me - do it for yourself and make "deployment day" less painful ;)


For Your Favourites | WCF
Friday, December 07, 2007 11:15:45 AM UTC  #   

Extension Methods for AntiXss

Playing around with some C# 3.0 language features, I came up with something which is quite useful if you are doing a lot of web in/output encoding.

The following extension methods wrap the AntiXss library:

public static class Extensions

{

    public static string UrlEncode(this string input)

    {

        return AntiXss.UrlEncode(input);

    }

 

    public static string HtmlEncode(this string input)

    {

        return AntiXss.HtmlEncode(input);

    }

 

    // rest omitted

}

This allows doing something like this:

string input = "<h1>leastprivilege rocks</h1>";

Response.Write(input.HtmlEncode());

AntiXssExtensions.zip (17.19 KB)

 


ASP.NET | Work in Progress
Friday, December 07, 2007 8:36:48 AM UTC  #   
 Sunday, November 25, 2007

Poor Man's File Replication using Robocopy

Robocopy has an option to monitor a directory tree for changes - and if a change occurs robocopy will re-rerun the last copy operation. This way you can have something like a "realtime" backup of data.

Example:

robocopy c:\etc\source e:\source /MIR /MON:1


Work in Progress
Sunday, November 25, 2007 11:30:23 AM UTC  #   
 Friday, November 23, 2007

WCF Usernames over Transport and IIS Hosting

I wrote here that WCF 3.5 now supports usernames over transport security. This does not work for IIS hosted services though.

The problem is this:

When you set the clientCredentialType on the binding to Basic, you also have to enable Basic Authentication in IIS for the .svc file. Otherwise you will get this error:

"Security settings for his service require 'Basic' Authentication but it is not enabled for the IIS application that hosts this service."

But when you enable Basic Auth in IIS, the request will bounce already at the IIS level because the custom credentials don't map to a Windows account. bummer.

That said - you can get Basic Authentication for custom accounts to work in IIS by plugging into the ASP.NET pipeline. I have a proof of concept implementation of that in my book and a complete implementation (including IIS7 UI integration) somewhere on my hard drive. Will post that when I find time.


WCF | Work in Progress
Friday, November 23, 2007 3:30:37 PM UTC  #   

Small change to WCF ServiceAuthorizationManager in 3.5

An often wanted feature for the WCF ServiceAuthorizationManager in 3.0 was to get easy access to the incoming message.

This is now possible in 3.5 - there is a new overload for CheckAccess which passes in the Message object as a ref parameter. done.


WCF | Work in Progress
Friday, November 23, 2007 3:15:55 PM UTC  #   

Authorizing Access to WCF Metadata

This post may be interesting for you because you eiter

  • want to control who has access to a service's metadata
  • retrieving metadata suddenly stops working when adding a ServiceAuthorizationManager to your service

Metadata retrieval requests pass just like normal requests the ServiceAuthorizationManager (if registered). Such requests will have a an action of http://schemas.xmlsoap.org/ws/2004/09/transfer/Get. You can retrieve this value from operationContext.IncomingMessageHeaders.Action.

So if you want to secure your MEX endpoint as described in this post, you can use a ServiceAuthorizationManager to distinguish between metadata requests and "normal" requests to make authorization decisions. If you don't care about MEX security but use an authorization manager, make sure you watch out for the metadata retrieval action, otherwise your clients probably can't update their client proxies.

UPDATE: DrNick has some more alternatives for detecting metadata requests.


WCF | Work in Progress
Friday, November 23, 2007 1:22:48 PM UTC  #   

Securing WCF Metadata

This question came up in a recent consulting gig - so I thought I summarize the options and provide some links.

WCF Metadata Architecture Overview

 

Base Address

The easiest way to expose metadata is to provide a base address and enable metadata publishing in the serviceMetadata behavior. You enable publishing by setting the http(s)GetEnabled attribute to true. When it comes to security, your options are limited. You won't get any authentication this way - but you can enforce SSL.  You can also specify the publishing URL which might solve the security problem at an infrastructure level.

This option only allows to retrieve metadata via HTTP.

How to: Secure WCF Metadata Endpoints

 

MEX endpoint

A more general pupose approach is to expose a Metadata Exchange (MEX) endpoint. This endpoint has to implement the (pre-defined) IMetadataExchange interface and is typically used with a mex* binding (for HTTP(S), TCP and NamedPipes) e.g.

<endpoint address="mex"

          binding="mexHttpBinding"            
           contract
="IMetadataExchange" />

There are also situations where MEX endpoints are required, e.g. WCF needs them to query policy when issued tokens and STSes are used.

The mex* bindings don't support security but you can expose metadata over arbitrary bindings as long as you use the IMetadataExchange contract. This gives you the full WCF security feature set.

The only problem is that svcutil.exe will not work out of the box with other bindings besides mex*. To make this work you would have to modify the svcutil.exe.config file in the framework directory. If you want to programmatically retrieve the metadata you have to configure the client channel accordingly.

If you want to secure a MEX endpoint, either use a secure binding or put the endpoint on some uri/port that is not generally reachable.

Secure MEX Endpoints

Retrieving Metadata

 

Metadata via WMI

Another way to retrieve metadata from a WCF service is via WMI. This is totally indepent from the base address, the serviceMetadata behavior or any MEX endpoints. By ACLing the WMI namespace in the computer management MMC snap-in you can control who has access to the WMI objects.

To enable WMI publishing you have to add this fragment to your serviceModel config:

<diagnostics wmiProviderEnabled="true" />

To retrieve the metadata, this code snippet should get you started:

private static void DumpAllMetadata()

{

    ManagementClass mc =

      new ManagementClass(@"\root\ServiceModel:Service");

 

    ManagementObjectCollection mos = mc.GetInstances();

 

    foreach (ManagementObject mo in mos)

    {

        Console.WriteLine("\n"+ mo["Name"] + "\n");

 

        foreach (string s in (string[])mo["Metadata"])

        {

            Console.WriteLine("more...");

            Console.ReadLine();

            Console.WriteLine(s);

        }

    }

}

 


WCF | Work in Progress
Friday, November 23, 2007 12:54:43 PM UTC  #   
 Tuesday, November 20, 2007

STS? Coming soon!

Vittorio has a sneak peek of the upcoming identity framework up on his blog.

Like many others I wrote my own STS (or tried to) - and I am happy that in the future this tedious work will be the job of some base class to derive from.

I have also seen Vittorio's talk at TechEd and have seen some more good upcoming stuff. In the meanwhile you can still download my InfoCard ASP.NET control to make things easier ;)


CardSpace | For Your Favourites | WCF
Tuesday, November 20, 2007 12:04:28 PM UTC  #   
 Thursday, November 01, 2007

WCF and SecurityAccessDeniedException

When you return false from the ServiceAuthorizationManager's CheckAccessCore method, WCF sends a special fault message back to the client. The logic looks more or less like this:

private Exception CreateAccessDeniedFault()
{
  FaultCode code = FaultCode.CreateSenderFaultCode(
    "FailedAuthentication",
    "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");

    FaultReasonText faultText = 
new FaultReasonText("Access is Denied.", CultureInfo.CurrentCulture);
return new FaultException(new FaultReason(faultText), code); }

This fault message gets turned into a SecurityAccessDeniedException in the (WCF) client that you can catch.

Now maybe you are also doing authorization from within your service operation and wanna return the same fault in case access is denied (and you don't want to come up with some custom fault which would mean you have to look for either the access denied exception *or* some fault exception). How does that work?

Well - first I used the above code to handcraft the fault but it turns out that it is much simpler.

The only thing you have to do is to throw a SecurityException from your operation. doh.


WCF | Work in Progress
Thursday, November 01, 2007 12:56:49 PM UTC  #   
 Wednesday, October 31, 2007

Finally! Usernames over Transport Authentication in WCF

Sometimes you have to wonder why the most basic features are missing in a v1 product...

Imagine this scenario: You have a public facing web service. You want the widest possible reach and compatibility - so the perfect technologies for that are HTTP, SSL and basic authentication (usernames and passwords) - and your customer accounts are no Windows accounts.

There was no sane way to pull this off in WCF v1 (besides fiddling around with the HTTP pipeline and simulating the basic auth handshake - but that tied you to IIS/ASP.NET).

You may say now - isn't that exactly what TransportWithMessageCredential is supposed to do? Not exactly - because this involves sending a basic WS-Security SOAP header inside of the message. I want simple HTTP basic auth....

(Christian and me had this very scenario at a customer just a few weeks ago...)

The good news is that WCF in .NET 3.5 will finally support this!

How does that work?

a) use transport security (e.g. basicHttpBinding)

<bindings>
  <basicHttpBinding>
    <binding name="secureBasic">
      <security mode="Transport">
        <transport clientCredentialType="Basic" />
      </security>
    </binding>
  </basicHttpBinding>
</bindings>

b) write a username/password validator

public class CustomUsernamePasswordValidator : UserNamePasswordValidator
{
    public override void Validate(string userName, string password)
    {
        if (!AuthenticateUser(userName, password))
            throw new SecurityTokenValidationException("...");
    }
}

c) hook up the validator in a serviceCredentials behavior

<behaviors>
  <serviceBehaviors>
    <behavior name="userName">
      <serviceCredentials>
        <userNameAuthentication customUserNamePasswordValidatorType="..."
                                userNamePasswordValidationMode="Custom" />
      </serviceCredentials>
    </behavior>
  </serviceBehaviors>
</behaviors>

d) send a username/password using the client proxy

ChannelFactory<IService> cf = new ChannelFactory<IService>("Service");
cf.Credentials.UserName.UserName = "bob";
cf.Credentials.UserName.Password = "foo";

IService proxy = cf.CreateChannel();
Console.WriteLine(proxy.Ping());

((IClientChannel)proxy).Close();

WCF | Work in Progress
Wednesday, October 31, 2007 1:51:19 PM UTC  #   

Does Microsoft regret the Security Push?

Well - at least parts of it - but this did get your attention, right?

Not sure what to think about that.

Remember the ILoveYou virus? It replicated itself to file shares to spread across intranets.

I think the argument that unmanaged code is not affected anyways doesn't count - we have a chance to make it better in the future (see also here).

I'd rather like to see a poll on how people would like the idea to finally be able to deploy CAS policies in a sane way...

What are your feelings about that?


Work in Progress
Wednesday, October 31, 2007 7:42:49 AM UTC  #   
 Thursday, October 25, 2007

Try XSSDetect

Exciting things are happening over at the ACE team at Microsoft.

One is XSSDetect - a Visual Studio plugin that analyzes your code to find potential XSS vulnerabilities. Mark also gives a sneak preview of other upcoming tools. Interesting!

 


For Your Favourites
Thursday, October 25, 2007 6:04:41 AM UTC  #   
 Tuesday, October 23, 2007

ValidateRequest does not mitigate XSS completely

I often mentioned that to customers and students. Here is the "official" word.

 


ASP.NET | For Your Favourites
Tuesday, October 23, 2007 9:02:29 AM UTC  #   
 Wednesday, October 10, 2007

.net@movies Episode 1 - Web Security

Am 17.Dezember findet die erste Veranstaltung der brandneuen .net@movies Serie des ProDev Colleges statt.

Bei diesem ersten Event geht es um ein Thema, das mir schon lange sehr am Herzen liegt: Web Security.

Mein geschätzer Kollege Christian Wenz und ich werden an diesem abwechslungsreichen Tag über Angriffe, Verteidigungsmaßnahmen, ASP.NET Sicherheitsfeatures und Web 2.0 Security reden.

Das ganze wird durch ein nettes Abendprogramm abgerundet ;)

Die Details findet Ihr hier.

Vielleicht sieht man sich ja!

 


Microsoft Deutschland Security Portal
Wednesday, October 10, 2007 7:09:23 AM UTC  #   
 Thursday, October 04, 2007

Sichere Software mit Microsoft .NET entwickeln

...ist der Titel eines neuen Buches vom Entwickler.Press Verlag.

Darin könnt Ihr eine Artikelsammlung von Autoren wie Michael Howard, Steve Lippner, Christian Wenz, Darius Parys und mir finden.

Behandelt werden Themen wie Windows, ASP.NET, .NET und WCF Security. Das ganze ist abgerundet mit ein paar interessanten Interviews...

Zuschlagen!

 


Microsoft Deutschland Security Portal
Thursday, October 04, 2007 7:20:23 AM UTC  #   

TechEd:Developer 2007 Security Track

TechEd:Developer in Barcelona this year will be the first TechEd ever that has a "physical" security track. That's great.

There are great speakers and interesting sessions on this track, e.g. Michael Howard (SDL, Threat Modeling), Keith Brown (identity, claims and ADFS), Mike Downen (Silverlight and 3.5 security), Rafal Lukawiecki (crypto and Vista security), Chris Chorio (UAC and WIM for developers), Vittorio Bertocci (CardSpace) and Alik Levin (web security).

I'll add some stuff about my favourite topics: WCF security and CardSpace.

Also join Keith and me for the panel discussion on CardSpace and the Identity Metasystem if you are around. Should be fun.

I am excited. Thanks to Sebastian for making this happen!!!

 


Conferences
Thursday, October 04, 2007 7:13:31 AM UTC  #   
 Thursday, September 27, 2007

Details of CardSpace RP Identity Generation

Ever wondered how exactly the RP identity is "calculated" for EV and non EV certs? Get the details here.

Just search for "OrgIdBytes".

 


For Your Favourites
Thursday, September 27, 2007 9:01:45 AM UTC  #   
 Tuesday, September 25, 2007

CardSpace in 3.5 doesn't require SSL

Important change to CardSpace in .NET 3.5 - read the details here.

 


For Your Favourites
Tuesday, September 25, 2007 1:29:39 PM UTC  #   
 Sunday, August 26, 2007

Certificate based Authentication and WCF (Mode independent)

My third approach for restricting trust when using client certificates works for transport and message security. Furthermore it does not involve any OS level configuration.

WCF has a piece of plumbing called the Service Authorization Manager. The SAM gets called on every incoming request and at this point the claim sets are already populated. In the issuer claim set of the client, you can find the thumbprint of the issuing CA (you can also find the thumbprint of the end certificate of course).

With that information you can apply the same logic as in the certificate validator I showed earlier.

Writing a SAM involves deriving from a class called ServiceAuthorizationManager and implementing the CheckAccessCore method. The logic looks like this:

protected override bool CheckAccessCore(OperationContext operationContext)
{
    string[] trustedThumbprints = GetTrustedThumbprints();
    string[] thumbprints;

    if (ValidationMode == ValidationMode.EndCertificate)
    {
        thumbprints = new string[] { 
            GetEndCertificateThumbprint(operationContext) };
    }
    else
    {
        thumbprints = GetIssuerThumbprints(operationContext);
    }

    return IsTrusted(thumbprints, trustedThumbprints);
}

To get the thumbprint from the claim set, you simply search for a thumbprint identity claim. One thing to note here is that the thumbprint gets stored as a byte[]. You can use the BitConverter class to convert that into a string, e.g.

private string GetEndCertificateThumbprint(OperationContext operationContext)
{
    foreach (ClaimSet set in 
operationContext.ServiceSecurityContext.AuthorizationContext.ClaimSets) { foreach (Claim claim in set.FindClaims(ClaimTypes.Thumbprint, Rights.Identity)) { string tb = BitConverter.ToString((byte[])claim.Resource); tb = tb.Replace("-", ""); return tb; } } throw new Exception("No thumbprint claim found"); }
The last step is to register the SAM in the serviceAuthorization behavior:
<behaviors>
  <serviceBehaviors>
    <behavior name="behavior">
      <serviceAuthorization serviceAuthorizationManagerType="type" />
    </behavior>
  </serviceBehaviors>
</behaviors>

Be aware that the SAM gets called on every request. Make sure the logic is as inexpensive as possible.

In the download you can find a SAM base class which follows the same design pattern as the certificate validator. Just override the GetTrustedThumbprints method.

CertAM.zip (51.86 KB)

 


WCF | Work in Progress
Sunday, August 26, 2007 1:52:54 PM UTC  #   

Certificate based Authentication and WCF (Transport Security)

When using SSL you need to set up a Certificate Trust List (CTL) for the listener port. In the CTL you can define which issuers to trust. CTLs are very low level and are used during the inital SSL handshake. If the incoming certificate is not trusted, the connection gets refused already at the protocol level.

The whole process is straightforward if you are hosting in IIS. Simply edit the "Secure Communication" settings for the site. Since SSL (and thus CTLs) is TCP port based, you can only edit theses settings at the site level.

When self hosting, you have to do the following:

  1. Define the CTL. In the SDK you can find a tool called MakeCTL.exe which helps you with that. At the end of this process MakeCTL allows to store the CTL in the Windows certificate store. You will get a GUID as the identifier for the stored CTL.
  2. Use httpcfg.exe or netsh.exe (when on Vista/Server 2008) to assign the CTL from the store to the SSL endpoint (you need that GUID here again to make the link)

While this is the "right" way of restricting trust with transport security, you have some additional deployment steps. In the next post I show you a way that is security mode independent - but also slightly less efficient.


WCF | Work in Progress
Sunday, August 26, 2007 1:38:42 PM UTC  #   

Good Article on System.IdentityModel

Keith wrote a good intro to the identity model APIs in .NET 3.0 here. Recommended.

A lot of people think that System.IdentityModel is somehow tied to WCF - this is not the case - it is a general API which happens to be nicely integrated into WCF - but you can use it in arbitrary applications - try this to get you started:

WindowsClaimSet windows =
  new WindowsClaimSet (WindowsIdentity.GetCurrent());

Have fun!

 


For Your Favourites | WCF
Sunday, August 26, 2007 9:38:37 AM UTC  #   
 Saturday, August 25, 2007

Certificate based Authentication and WCF (Message Security)

When using message security, the intended way to validate an incoming credential (== token) is a token validator. You can find several internal validators in the System.IdentityModel.Selectors namespace (e.g. for UserName, X.509 or Windows tokens). The X509 token validators gets called whenever an incoming certificate has to be validated - when you have secure conversation enabled, this happens only on the first request which makes this approach very efficient.

WCF has three builtin validators for X.509 certificates and you can choose which one to use via a service/endpoint behavior. I will show the service side settings here, but the same switches also exist on the client side.

<behaviors>
  <serviceBehaviors>
    <behavior name="behavior">
      <serviceCredentials>
        <clientCertificate>
          <authentication certificateValidationMode="ChainTrust"
                          revocationMode="Online" />
        </clientCertificate>
      </serviceCredentials>
    </behavior>
  </serviceBehaviors>
</behaviors>

The certificateValidationMode specifies how incoming certificates are validated and how trust is determined:

  • None. No validation is performed. Not recommended.
  • ChainTrust. The certificate has to chain up to one of the CAs in your trusted CA certificate folder.
  • PeerTrust. The incoming certificate has to be in the Trusted People certificate folder.
  • PeerOrChainTrust. A combination of Chain and Peer trust.
  • Custom. For all other cases.

The trustedStoreLocation attribute determines whether the current user or local machine store is used for peer or chain checks - defaults to local machine. Furthermore you specify how revocation lists should get checked via the revocationMode attribute (no check, offline or online).

See this post for a more detailed description of the validation modes. Also, as mentioned in that post, the standard validation modes are mostly useful in niche situations.

This is where the Custom mode comes into play. With this mode it is your responsibility to validate the certificate following your own guidelines. You then make decisions if you want to accept the certificate.

A custom certificate validator involves deriving from X509CertificateValidator and implementing the Validate() method. The WCF plumbing passes the incoming certificate into this method. If you want to reject the certificate you throw a SecurityTokenValidationException inside Validate().

So far so good - now, which steps are involved to validate a certificate? Before you can rely on any information in the cert, you have to make sure it's valid - typcially by checking it is not expired or revoked and is issued by a trusted CA. Afterwards you can check certain properties of the certificate or its issuer to further restrict the allowed certs. Another approach would be - like PeerTrust - to check the certificate against a list of allowed certificates.

For checking the trust chain, you use the X509Chain class - the general logic of Validate() looks like this:

public override void Validate(X509Certificate2 certificate)
{
    // create chain and set validation options
    X509Chain chain = new X509Chain();
    SetValidationSettings(chain);

    // check if cert is valid and chains up to a trusted CA
    if (!chain.Build(certificate))
    {
        throw new SecurityTokenValidationException(
"Client certificate is not valid"); } // check if cert is from our trusted list if (!IsTrusted(chain, GetTrustedThumbprints())) { throw new SecurityTokenValidationException(
"Client certificate is not trusted"); } }

How the certificate should be exactly validated can be specified on the ChainPolicy property of X509Chain. Besides the configurable revocation mode, WCF uses the default settings for the chain policy, which are:

protected override void SetValidationSettings(X509Chain chain)
{
    chain.ChainPolicy.RevocationMode = X509RevocationMode.Online;
    chain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot;
    chain.ChainPolicy.VerificationFlags = X509VerificationFlags.NoFlag;
    chain.ChainPolicy.VerificationTime = DateTime.Now;
    chain.ChainPolicy.UrlRetrievalTimeout = new TimeSpan(0, 0, 0);
}

My implementation of IsTrusted then checks if either an issuer or a the end certificate itself (specified by the ValidationMode property) is in a trust list. The check is done by comparing the thumbprint of the certificate in question against a list.

protected virtual bool IsTrusted(X509Chain chain, string[] trustedThumbprints)
{
    int depth = 0;

    if (ValidationMode == ValidationMode.EndCertificate)
    {
        // only check the end certificate
        return CheckThumbprint(chain.ChainElements[0].Certificate, trustedThumbprints);
    }
    else
    {
        // check the rest of the chain
        foreach (X509ChainElement element in chain.ChainElements)
        {
            if (++depth == 1)
            {
                continue;
            }

            if (CheckThumbprint(element.Certificate, trustedThumbprints))
            {
                return true;
            }
        }
    }

    return false;
}
The last step is to register the validator in a serviceCredentials behavior.
<serviceCredentials>
  <serviceCertificate findValue="Service"
                      x509FindType="FindBySubjectName"
                      storeLocation="CurrentUser"
                      storeName="My" />

  <clientCertificate>
    <authentication certificateValidationMode="Custom"
                    customCertificateValidatorType="type" />
  </clientCertificate>
</serviceCredentials>

In the download you can find a ready to use validator base class from which you can derive from. You just have to implement the GetTrustedThumbprints method and return a string[] of thumbprints. You can get the thumbprints from the certificate UI (just remove the blanks). Have fun!

CertValidator.zip (16.98 KB)

 


WCF | Work in Progress
Saturday, August 25, 2007 3:31:47 PM UTC  #   

Live ID and Information Cards - just good friends...

Read more here and here. finally.

 


For Your Favourites
Saturday, August 25, 2007 10:45:10 AM UTC  #   

Certificate based Authentication and WCF

Certificate based authentication with WCF has two components - configuring credentials and determining trust.

The first part is easy - you simply set the clientCredentialType in the binding's security configuration to Certificate. This means that WCF will demand that the client sends a certificate along with the (first) request - either as a WS-Security X509 token or using SSL client certificates (depending on the security mode this also means that this requirement becomes part of the WSDL/Policy).

Furthermore the "plumbing" (either SSL or WCF message security) will make sure that the incoming certs are technically valid. This includes making sure that the certificate is not malformed and that the signature matches the public key. If this is not the case, the request gets rejected at a very low level and usually your service code would never get invoked.

At this point you have a technically valid certificate - but this does not necessarily mean that you also trust that certificate. The default validation strategy for certificates (regardless of message vs. transport) is called Chain Trust, this means:

  • the certificate must be issued from a CA in your trusted CA list (in the machine certificate store)
  • this intended purpose of that CA must include "Client Authentication"
  • the current date/time must be within the certificate's validitiy period

With message security you also get a mode called Peer Trust. In this mode WCF simply checks if the incoming certificate is installed in the Trusted People folder in the certificate store (the expiration time is checked too).

When to use which mode?

Peer Trust
Since the Trusted People folder must hold all allowed certificates, this means that the service (or the client) has to know every peer a priori. This is OK if you have a limited amount of certificates that you want to allow - but does not scale very well. If you have more than one service on a machine and want to use peer trust, you have to run them under different accounts and use the current user certificate store to separate the "allowed" list.

Chain Trust
Chain trust is for scenarios where you don't know every certificate explicitly, but want to establish trust based on the issuer. The problem you have here is, that by default the trusted CA list is quite, errm, extensive. It includes popular CAs like VeriSign and not so popular ones (which I never heard before). So using chain trust it is quite easy to get a certificate that would be trusted by your service.

So in a lot of cases peer trust is not enough and chain trust is too much. What you typically want to have is first validating the trust chain and afterwards restrict to a specific CA or specific properties in the client cert. How do you accomplish that? Well - it depends ;)

In classic SSL transport security this is accomplished by using a Certificate Trust List (CTL). The moral equivalent in message based security is a X509 Certificate Validator. A third approach (which works in both modes) would be a WCF Service Authorization Manager.

In the next posts I will have a look at these technique and show you the up/downsides of each. Stay tuned.


Work in Progress
Saturday, August 25, 2007 7:17:12 AM UTC  #   
 Monday, August 20, 2007

Guidance on User and Password Management

The ACE blog has a good checklist on the above topic. Something to have around when implementing the next password based system.

 


For Your Favourites
Monday, August 20, 2007 8:07:56 AM UTC  #   
 Thursday, August 09, 2007

Extend Your WCF Services Beyond HTTP With WAS

..is the title of an article by Christian, Steve and me. Online and in the September issue of MSDN Magazine. Enjoy.

 


For Your Favourites
Thursday, August 09, 2007 7:17:24 AM UTC  #   
 Wednesday, August 08, 2007

Custom Principals and WCF

The question how to setup a custom principal in WCF services comes up every once in a while. Since it is not obvious how this works, I knocked up a little walkthrough and a boilerplate sample.

Principal Permission Mode
WCF has two authorization systems - roles-based and claims-based. Roles-based security is centered around an IPrincipal implementation placed on Thread.CurrentPrincipal. What WCF puts on T.CP depends on the principalPermissionMode attribute in the serviceAuthorization behavior. There are four options:

  • None. T.CP will contain a GenericPrincipal with no roles set.
  • UseWindowsGroups. T.CP will contain a WindowsPrincipal. IsInRole queries the Windows token for group membership. Only supported for Windows authentication.
  • UseAspNetRoles. T.CP will contain a RoleProviderPrincipal. The IsInRole method calls the role provider's IsUserInRole implementation.
  • Custom. You have to set your own principal implementation.

Authorization Policies
The place to set a custom principal implementation is a class implementing the IAuthorizationPolicy interface. Generally, authorization policies are used to add claim sets to the authorization context, but in this scenario the policy has the special purpose of setting the principal. You will also get a runtime exception when the principalPermissionMode is set to custom and you don't set a principal.

The IAuthorizationPolicy interface has three member that you have to implement:

  • Id. You typically return a unique ID here, e.g. a GUID.
  • Issuer. This returns a claim set describing who has added the policy information.
  • Evaluate. You create and set the custom principal here.

The way to get to the authenticated identity and the principal in the Evaluate method is a little "hidden". The evaluation context that gets passed into Evaluate contains a collection named Properties. This collection has two well known keys called Identities and Principal (see also here).

To get to the client identity you have to extract an IList<IIdentity> from this collection:

private IIdentity GetClientIdentity(EvaluationContext evaluationContext)
{
    object obj;
    if (!evaluationContext.Properties.TryGetValue("Identities", out obj))
        throw new Exception("No Identity found");

    IList<IIdentity> identities = obj as IList<IIdentity>;
    if (identities == null || identities.Count <= 0)
        throw new Exception("No Identity found");

    return identities[0];
}

Based on that identity you typically create your custom principal and set it back to the properties collection. The WCF plumbing takes the principal from there and sets it on T.CP.

The following authorization policy combines the groups of a Windows user with application defined roles and creates a new principal that can be queried in the service implementation using Thread.CurrentPrincipal.IsInRole() or PrincipalPermission[Attribute]. The custom principal that is used here is omitted but can be found in the download (it simply mimics the GenericPrincipal, but allows to get back the roles using the Roles property - which can be handy and is a limitation of GenericPrincipal IMO).

class CombinedRolesPolicy : IAuthorizationPolicy
{
    Guid id = Guid.NewGuid();

    public bool Evaluate(EvaluationContext evaluationContext, ref object state)
    {
        // will hold the combined roles
        List<string> roles = new List<string>();

        // get the authenticated client identity
        IIdentity client = GetClientIdentity(evaluationContext);

        // this policy is intended for Windows accounts only
        WindowsIdentity windowsClient = client as WindowsIdentity;
        if (windowsClient == null)
        {
            throw new SecurityTokenValidationException(
"Only Windows accounts supported"); } // add Windows groups roles.AddRange(GetWindowsRoles(windowsClient)); // add application defined roles roles.AddRange(GetAppRoles(windowsClient)); // set a new principal holding the combined roles // this could be your own IPrincipal implementation evaluationContext.Properties["Principal"] = new CustomPrincipal(windowsClient, roles.ToArray()); return true; } private IEnumerable<string> GetWindowsRoles(WindowsIdentity windowsClient) { List<string> roles = new List<string>(); IdentityReferenceCollection groups =
windowsClient.Groups.Translate(typeof(NTAccount)); foreach (IdentityReference group in groups) { roles.Add(group.Value); } return roles; } private IEnumerable<string> GetAppRoles(WindowsIdentity windowsClient) { List<string> roles = new List<string>(); // simulated database access roles.Add("MyApplicationRoleA"); roles.Add("MyApplicationRoleB"); return roles; } public System.IdentityModel.Claims.ClaimSet Issuer { get { return ClaimSet.System; } } public string Id { get { return id.ToString(); } } }

The last step is to register the authorization policy with your service:

<behaviors>
  <serviceBehaviors>
      <serviceAuthorization principalPermissionMode="Custom">
        <authorizationPolicies>
          <add policyType="LeastPrivilege.CombinedRolesPolicy, CustomPrincipalService" />
        </authorizationPolicies>
      </serviceAuthorization>
    </behavior>
  </serviceBehaviors>
</behaviors>

In you service implementation, you can use the usual techniques to make role based checks, or you cast the principal to its concrete type to use some specialized features you have implemented (see the sample code). Have fun.

WcfCustomPrincipal.zip (24.83 KB)

 


Work in Progress
Wednesday, August 08, 2007 1:20:59 PM UTC  #   
 Tuesday, July 24, 2007

InfoCardSelector for ASP.NET V1.0

OK - so I settled for a final name (since the control also works for other identity selectors than CardSpace this is more appropriate) and resetted the version.

There are only minor (but breaking) changes to the last version I posted:

  • Changed the name to <lp:InfoCardSelector />
  • Incorporated the official token decryption class from the ASP.NET Toolkit
  • Added an AutoDecryptToken property. If set to true the control will decrypt the token an pass the Token instance back to the page via the event handler
  • Added a BSD license

I also made some minor changes to the Token class to retrieve the public key hash of the issuer without combining it with the PPID.

InfoCardSelectorV1.zip (100.47 KB)

 


Tools | Tools for Thinktecture | Work in Progress
Tuesday, July 24, 2007 7:06:57 AM UTC  #   
 Saturday, July 21, 2007

Excellent ASP.NET Information

Daniel currently publishes an internal Microsoft feed on his blog. Excellent information about ASP.NET internals like process models, dynamic compilation and the pipeline (more to come).

Recommended!

 


For Your Favourites
Saturday, July 21, 2007 4:42:48 AM UTC  #   
 Thursday, July 12, 2007

Windows Integrity Mechanism

Another very interesting security feature (even revolutionary for Windows) in Vista/2008 is mandatory access control (as opposed to discretionary access control).

Michael just posted the link to the new technical reference. Enjoy.


For Your Favourites
Thursday, July 12, 2007 7:49:50 AM UTC  #   

Windows Service Hardening

I am pretty impressed with the changes and new features for good old NT Services in Vista/Server 2008.

My favourite feature is, that every service can now have a distinct SID that can be used to ACL kernel objects. Imagine you have to deploy a bunch of services that have to be isolated from each other. Pre-Vista you would have to create an account for each service. Now you can simply use the new NT Service\ServiceName SID to control authorization. Very neat.

Even better the SIDs for services are deterministic across machines (S-1-5-80-SHA1(Servicename) to be exact), and you can also pre-calculate the SID even when the service doesn't exist yet:

sc showsid FooService

This makes it very easy in server farms to copy data *and* ACLs (e.g. using robocopy) between nodes...

Additionally you can write-restrict the service tokens - that means that the service won't have write access to any kernel object unless the service SID is explicitly part of the ACL.

Other nice features are that services no longer share the same desktop with interactive users and that you can bundle network access rules with services. These rules are independent from the Windows firewall and would even work if the firewall is disabled.

Nice least privilege work, guys!

Voy has written a series of blog posts about the new features (recommended reading):


Work in Progress
Thursday, July 12, 2007 7:00:12 AM UTC  #   
 Wednesday, July 11, 2007

New WCF Security Samples

Justin wrote me an email this morning:

"Dominick,

I hope you are well.

There are several new WCF-security samples posted at http://wcf.netfx3.com/files/53/default.aspx (today)

I thought you might be interested, especially if you are planning on writing that WCF security book…"

That's great news! They all look very interesting!

Thanks for getting me into trouble, Justin ;)

 


For Your Favourites
Wednesday, July 11, 2007 6:38:04 AM UTC  #   
 Friday, July 06, 2007

WCF Security Webcast

Im Rahmen der WCF/WF Webcast Serie auf MSDN Deutschland haben Christian Weyer und ich einen Webcast über WCF Security gemacht.

Der ist jetzt downloadbar. Viel Spass!

 


Microsoft Deutschland Security Portal
Friday, July 06, 2007 7:52:03 AM UTC  #   
 Thursday, July 05, 2007

Hit by Identity 1.0

I am moving soon to another city - that means I will have a new address, new telephone number etc. I realized at how many sites I have to change my details (e.g. for delivery).

Wouldn't life be much better if all these sites would just update their details from my updated Information Card?

Identity 2.0, where art thou?


Misc
Thursday, July 05, 2007 9:28:54 AM UTC  #   
 Monday, July 02, 2007

Re:MVP

Just got mail from Microsoft - my MVP status in the small but fine Developer Security category has been renewed.

Thanks Microsoft!

 


Misc
Monday, July 02, 2007 8:30:53 AM UTC  #   
 Thursday, June 28, 2007

Information Card Kits for ASP.NET and HTML

Microsoft just released two kits to make it easier to integrate information cards into HTML and ASP.NET based applications. woohoo.

A quick look reveals a new class for decrypting tokens and a helper to bridge the gap to membership. Looks like a perfect companion for my ASP.NET control ;) Stay tuned...

 


For Your Favourites
Thursday, June 28, 2007 3:55:55 AM UTC  #   
 Monday, June 25, 2007

Feature Complete Version of the ASP.NET CardSpace Control

UPDATE: new version available here.

After I made some incremental changes and releases of my CardSpace control (found some bugs, got some feedback), I wanted to consolidate all the information along with a new version and some new features here. It now contains all the features I need and will be the last release for some time i guess.

If you have any feedback or suggestions feel free to write me or leave me a comment. If you want to add support for XHTML, contact me too ;)

Download here. Have fun!

PS. This was only possible with a little help of my friends...thanks Brock and JasonD!

Features

Easy to use syntax
One of my main goals was to have an easy to use markup syntax and intellisense support. I don't want to type in all those namespace URIs...

<lp:CardSpaceSelector runat="server" ID="_selector_sic" AutoPostback="true"
    IssuerType="SelfIssued">

    <lp:ClaimType Name="givenname" />
    <lp:ClaimType Name="surname" />
    <lp:ClaimType Name="email" />

</lp:CardSpaceSelector>

Clean markup and independence of the server form
The emitted markup works with Firefox and IE. I also made sure that the <object> tag is placed outside of the postback form. This allows you to have multiple postback controls on the form without triggering the identity selector.

Support for standard InfoCard image
You can choose between all standard sizes of the official InfoCard image. You can also supply your own image and dimensions

Designer integration
I never use the designer - but I acknowledge the fact that some people do ;) The control renders correctly in the designer and has an editor to setup the required/optional claims (including intellisense support).

Event driven
The control fires an event when a token is submitted.

protected void _selector_sic_TokenSubmitted(object sender, TokenSubmittedEventArgs e)
{
    string xmlToken = e.Token;
}

Conditional rendering
You can choose to render the control only if the client browser supports InfoCards. You can specify an alternative <div /> that would render in that case (e.g. to tell the user how to get CardSpace).

Decoupling
I intentionally didn't couple the control with any user management semantics (like membership) or decryption clases (like the TokenProcessor). It is totally up to you how to proceed after you received the encrypted token. This is considered a feature ;)

 

Properties

InfoCard setup

IssuerType
This enum has two values ‘SelfIssued’ and ‘Managed’. If you select ‘SelfIssued’ then the issuer URI for self-issued cards will be emitted. If you select ‘Managed’ you have to set the issuer URI yourself. Defaults to 'SelfIssued'

Issuer and IssuerPolicy
Specifies the URIs for the issuer and the issuer policy.

TokenType
Specifies the token type. Defaults to SAML 1.0.

PrivacyUrl and PrivacyVersion
Specifies to the URL and version of the associated privacy policy (if any).

Image

ImageUrl
Specifies a custom image to display. Defaults to the official InfoCard logo. 

StandardImageSize
Selects one of the standard images sizes for the official InfoCard logo. Defaults to 114x80.

Width & Height
Specifies the size of the image in pixels. Only relevant when a custom image is used.

Rendering

RenderOnlyIfSupported
When set to true, the control will only render if the client browser supports CardSpace. You have to embed the control into a <div /> and specify the name in the DivToRender attribute. Defaults to false.

DivToRender
Specifies which <div /> to render/make invisible based on client support.

UnsupportedDiv
Optionally specifies a <div /> to render when CardSpace is not supported on the client.

RenderMode
Choose between static and dynamic rendering. Static preserves the space for the control on the client. Defaults to Static.

Misc

HiddenFieldName
Name of the hidden field used to transmit the token back to the page. Defaults to __XMLTOKEN.

AutoPostBack
Specifies if the control posts back after a card has been selected. Defaults to false.

TriggerOnLoad
Specifies if the identity selector should be invoked directly after the page has finished loading. Defaults to false.

XmlToken
Holds the encrypted token after the user has selected a card.

 

CardSpaceSelectorV2.5.zip (393.33 KB)
Work in Progress
Monday, June 25, 2007 6:40:10 PM UTC  #   
 Saturday, June 23, 2007

Recorded Sessions from IMTC

The Irish folks are great - not only they organized a fantastic community conference - they also recorded all the sessions...

http://www.askasqlguru.com/category/imtc/

Have fun!

 


Conferences
Saturday, June 23, 2007 3:43:00 PM UTC  #   

MSDN Security Code Clips

In Zusammarbeit mit MSDN Deutschland habe ich einen ganzen Schwung Screencasts rund um sichere Softwareentwicklung erstellt (unterteilt in die Bereiche Allgemein, Client Entwicklung, ASP.NET, WCF und CardSpace).

Die Startseite findet Ihr hier:
http://www.microsoft.com/germany/msdn/solve/knowhow/sicherheit/default.mspx

 


Microsoft Deutschland Security Portal
Saturday, June 23, 2007 2:49:02 AM UTC  #   

MSDN UK CardSpace Nuggets

I recently did four short screencasts for MSDN UK on various aspects of CardSpace. The focus more on self issued cards and are divided into the following topics:

Also find the sample code I used here.

 


CardSpace | Work in Progress
Saturday, June 23, 2007 2:40:34 AM UTC  #   
 Thursday, June 21, 2007

Another update to the CardSpace Control for ASP.NET (2)

Sorry about the confusion - but I felt like adding some more "nice to have" features to the control.

In this new version you can tell the control to render only if CardSpace is supported in the client browser. For this I added three new properties:

  • RenderOnlyIfSupported (self explanatory)
  • DivToRender (specifies the <div /> to render if CardSpace is supported
  • RenderMode (either Static or Dynamic - render either a hidden or a none div. Like the display mode in validation controls)

The tag could look like this:

<div id="selectorDiv">
    <h2>CardSpace Login</h2>
    <lp:CardSpaceSelector runat="server" ID="_selector" 
AutoPostback="true" IssuerType="SelfIssued" OnTokenSubmitted="_selector_TokenSubmitted" RenderOnlyIfSupported="true" DivToRender="selectorDiv" RenderMode="Dynamic"> <lp:ClaimType Name="Email" /> <lp:ClaimType Name="Surname" /> <lp:ClaimType Name="PPID" /> <lp:ClaimType Type="Custom" CustomName="http://foo" IsOptional="true" /> </lp:CardSpaceSelector> </div> 

CardSpaceSelectorV2.2.zip (51.76 KB)

 


Work in Progress
Thursday, June 21, 2007 6:16:00 AM UTC  #   
 Tuesday, June 19, 2007

Another Update to the CardSpace Control for ASP.NET

Update: updated version here.

I did another slight modification to my CardSpace control (see here and here) - it now fires a TokenSubmitted event where it passes the encrypted token back to the page. This makes for a nicer programming model.

The tag could look like this:

<lp:CardSpaceSelector runat="server" ID="_selector" AutoPostback="true" 
IssuerType="SelfIssued" OnTokenSubmitted="_selector_TokenSubmitted"> <lp:ClaimType Name="Email" /> <lp:ClaimType Name="Surname" /> <lp:ClaimType Name="PPID" /> <lp:ClaimType Type="Custom" CustomName="http://foo" IsOptional="true" /> </lp:CardSpaceSelector>

And the corresponding code:

protected void _selector_TokenSubmitted(object sender, TokenSubmittedEventArgs e)
{
    try
    {
        // parse and decrypt the token
        Token token = new Token(e.Token);
        ...
    }
    catch (Exception ex)
    {
        ...
    }
}

Have fun!

CardSpaceSelectorV2.1.zip (48.28 KB)

 


Work in Progress
Tuesday, June 19, 2007 3:47:08 AM UTC  #   
 Monday, June 18, 2007

Getting CardSpace Tokens Programmatically

Last week I did a talk at Software Architect about Federation and CardSpace. I got almost the same question three times: "Can I use CardSpace in my own applications - without having to use WCF or a browser?"

The scenarios where interesting - Andy had a CardSpace enabled VPN/Radius access in mind and Tim was wondering how to CardSpace enable an http/xml based system.

So I decided to have another look at the APIs in the System.IdentityModel.Selectors assembly (specifically the CardSpaceSelector.GetToken() method).

Well - this API is quite unpleasant to use as it requires a lot of handcrafted XML to generate the policy that gets passed to the CardSpace selector service. So I wrote a little wrapper to simplify things. You basically pass in the required details (issuer and target URI, the target identity as well as required/optional claims) and get back the encrypted XML token. From this point on it is up to you to use this token in whatever way you want -  it is as easy a shipping a string to your relying party. handy.

This code snippet shows how to use the wrapper to get a token for a self issued card:

IdentitySelector selector = new IdentitySelector();

selector.IssuerUri = 
new Uri("http://schemas.xmlsoap.org/ws/2005/05/identity/issuer/self"); selector.TargetUri = new Uri("http://relyingParty"); selector.SetTargetCertificate("RelyingParty", X509FindType.FindBySubjectName, StoreLocation.CurrentUser, StoreName.AddressBook); selector.RequiredClaims.Add(ClaimTypes.GivenName); selector.RequiredClaims.Add(ClaimTypes.Surname); selector.RequiredClaims.Add(ClaimTypes.Email); string tokenString = selector.GetTokenString();
 

IdentitySelectorWrapper.zip (13.29 KB)

 

 


Work in Progress
Monday, June 18, 2007 6:34:44 AM UTC  #   
 Saturday, June 09, 2007

Updated CardSpace Control for ASP.NET

Update: updated version here.

I did some updates to the CardSpace control I posted last year. It now produces cleaner HTML which plays nicer with the Firefox plugin and has improved server side markup.

A typical tag could look like this:

<lp:CardSpaceSelector runat="server" ID="_selector" AutoPostback="true"
    IssuerType="SelfIssued">

  <lp:ClaimType name="Email" />
  <lp:ClaimType name="Surname" />
  <lp:ClaimType name="PPID" />
  <lp:ClaimType type="Custom" CustomName="http://foo" isOptional="true" />
    
</lp:CardSpaceSelector>

The name attribute now supports intellisense so you don't have to remember all those claim type namespace URIs.

I intentionally did not couple the control with any token decryption logic or user management. There are simply too many options how to handle claims in your application and I didn't want to dictate any style or pattern.

After the postback you can retrieve the encrypted token from the XmlToken property and use the token decryption logic of your choice to get to the claims (a tweaked version of the TokenProcessor SDK sample is included - but you may wanna consider using the TokenDecryptionService).

protected void Page_Load(object sender, EventArgs e)
{
  if (IsPostBack)
  {
    // retrieve encrypted XML
    string xmlToken = _selector.XmlToken;
    if (!string.IsNullOrEmpty(xmlToken))
    {
      // parse and decrypt the token
      Token token = new Token(xmlToken);
      string email = token.Claims[ClaimTypes.Email];
  }
}

CardSpaceSelectorV2.zip (51.13 KB)

 


Work in Progress
Saturday, June 09, 2007 7:12:11 PM UTC  #   
 Friday, May 25, 2007

Online Vorlesungen zu Betriebssystem Architektur

Wow - 60h online Material zu OS Internals. hier und hier.

Mehr Infos bei Sebastian.

 


Microsoft Deutschland Security Portal
Friday, May 25, 2007 6:59:08 AM UTC  #   
 Wednesday, May 23, 2007

"Hello {0}: {1}", "Dublin", "Irish Microsoft Technology Conference"

Clare, Microsoft and the Irish community gang have set up a fantastic conference in june! Lots of interesting talks! Looking forward to it!

If you are around say Hi!

 


Conferences
Wednesday, May 23, 2007 9:43:50 AM UTC  #   
 Thursday, May 17, 2007

Identity Providers, Authentication, Self Issued Cards and (again) Keys

I just assume (= hope) that it will be a common scenario in the future that identity providers set up a web page where you somehow register and in turn get a managed card to import into CardSpace.

After you imported the card you can use it to login to applications and services that accept cards from that particular identity provider. Under the covers, CardSpace will then contact the STS to obtain a token for the relying party. To get this token you have to somehow authenticate with the STS.

A CardSpace STS currently supports four credential types

  • Username/password
  • Certificates
  • Kerberos
  • Self issued cards

Each authentication type has its use.

  • Username/password supports any key/value pair type of authentication, e.g. also CC# and security code.
  • Kerberos enables single-sign on in Kerberos realms
  • Self issued cards enable single sign on with self asserted information (but probably verified by the identity provider)
  • Certificates (my favourite) enable very strong authentication and especially when combined with smart cards multiple factors

You can also strengthen the protection of the cards with an additional pin that you have to enter, every time you use them.

When you want to build your own STS for CardSpace you have to make a choice which authentication type(s) you want to support. While reading the The Identity Selector Interop Profile about using self issued cards, I came across this:

<quote>

5.4. Self-issued Token Credential

When the identity provider requires a self-issued token as the credential type, the following credential descriptor format MUST be used in the information card to specify the required credential.

Syntax:

<ic:UserCredential>
  <ic:SelfIssuedCredential>
    <ic:PrivatePersonalIdentifier> xs:base64Binary </ic:PrivatePersonalIdentifier>
  </ic:SelfIssuedCredential>
</ic:UserCredential>

The following describes the attributes and elements listed in the schema outlined above:

.../ic:SelfIssuedCredential

This element indicates that a self-issued token credential is needed.

.../ic:SelfIssuedCredential/ic:PrivatePersonalIdentifier

This required element provides the value of the PPID claim asserted in the self-issued token used previously to register with the IP/STS (see Section 8.5.14). Furthermore, the actual security policy of the IP/STS (expressed in its WSDL) MUST include the sp:IssuedToken assertion requiring a self-issued token with exactly one claim, namely, the PPID.

</quote>

This means that you have to embed the PPID of the self issued card inside a managed card to make the connection between these two cards. In a recent post I told you that PPIDs are generated dynamically based on the application identity.

Now this implies that from a CardSpace point of view the web site issuing the managed card and the STS (usually a separate web service) need to have the same identity. Otherwise the PPIDs would get calculated differently and CardSpace could not associate the SIC with the managed card.

This means that essentially the web site and the STS have to use the same server certificate. And both of them need read access to the private key.

Now to me it sounds a little bit dangerous when a flaw in one application can compromise the identity of another. And especially an identity provider should have rock solid security.

Just another reason to factor out at least the token decryption code in the web interface. I wrote about that here and provided a service that allows doing that here.

Another approach could be to use EV certificates. That would allow having separate certs for the web app and the STS and still the PPID would get calculated correctly (given the O, L, S, C fields in the certificate match). I have not tried that but will once I can afford them ;)

Sometimes it is really funny how such a small detail can completely change your overall architecture…and sometime you find out the hard way…


Work in Progress
Thursday, May 17, 2007 10:11:40 PM UTC  #   
 Wednesday, May 16, 2007

Live Alerts

Trying to be as "2.0" as Clemens - I added Live Alerts support to this blog.

Click here to subscribe:

Windows Live Alerts

Spass beiseite: I think the Alerts service is interesting (for more than just pinging you when I wrote a new post) - so I thought I give it a try...more to come...

 


Work in Progress
Wednesday, May 16, 2007 7:00:15 AM UTC  #   
 Saturday, May 12, 2007

Primary Keys in Identity

Keith followed up on my recent post about unique values and IDs in InfoCards. He proposes a model of simply using the hash of the public key as an identifier. This is totally fine and, frankly, I simply forgot to talk about that option in my post.

Basing the ID only on the public key gives you the most flexibility, but you also need a strategy for dealing with the case when certain "key" values in the card change. Combining the public key with certain claims will automatically invalidate the card when these values change. Both behaviors are totally valid, and it depends on your application scenario which way you wanna go. Just be sure to know what you get.

In this post Keith talks about another piece of the puzzle – the ClaimType.Right. Recommended.


Work in Progress
Saturday, May 12, 2007 7:37:43 AM UTC  #   
 Thursday, May 03, 2007

Orcas, WF, WCF and CardSpace

Vittorio posted a nice walkthrough on how to combine the above mentioned technologies. Sweet.


For Your Favourites
Thursday, May 03, 2007 9:47:50 AM UTC  #   
 Saturday, April 21, 2007

UAC Manifest Support in "Orcas"

Adding UAC manifests to apps built with VS2005 is kind of a hassle (and does not really work in all situations).

I was pleased to spot this new properties dialog in VS "Orcas":

 

 


Work in Progress
Saturday, April 21, 2007 11:29:02 AM UTC  #   
 Wednesday, April 18, 2007

Token Decryption Service for CardSpace

Web Applications that want to decrypt CardSpace tokens need read access to the SSL private key. But you would increase your attack surface tremendously if you directly grant this access to the worker process account of your application. I wrote about this in more detail here and Richard Turner followed up here.

Together with my colleagues at Thinktecture (thanks Christian and Buddhike for code reviewing and QA) I wrote an out-of-proc token decryption service that allows decrypting tokens without having to have direct access to the private key in the application, the idea is as follows:

Your web application runs under its normal least privilege account with no read access to the private key. The token decryption service runs as an NT service on the same machine under an account that has read access. Whenever the application has to decrypt a token, it hands the encrypted token to the token decryption service which (in this version) simply uses the TokenProcessor to return a list of claims, a unique ID and the issuer key.

The token decryption service is implemented as a WCF service that uses named pipes to communicate with the applications. To make sure that only authorized applications can call into the service, the application(s) have to be member of a special Windows group called "TokenDecryptionUsers" (can be changed in configuration to support multiple decryption services on the same machine). I also wrote a shim for the WCF client proxy that allows using this service from partially trusted web applications.

The download contains binaries, installation instructions and the full source code. I hope this helps CardSpace adopters to improve the security of their applications and servers. If you have any comments or questions – feel free to contact me.

TDSv1.zip (167.91 KB)

 


Tools | Tools for Thinktecture | Work in Progress
Wednesday, April 18, 2007 5:22:26 AM UTC  #   
 Tuesday, April 17, 2007

UAC Demo and Helpers

As a follow up to the UAC article I wrote some weeks ago, I compiled some demos and a helper library.

The demos show how applications behave given different manifest settings and the helper includes some useful methods like:

  • Find out if the application is currently running elevated
  • Start a process elevated
  • Restart the current process elevated
  • Start a COM DLL elevated
  • Add the shield icon to a button

Btw - excellent information about UAC internals can be found in Mark Russinovich's talk from TechEd '06

UAC_Demo.zip (151.69 KB)

 


Tools | Tools for Thinktecture | Work in Progress
Tuesday, April 17, 2007 8:36:29 AM UTC  #   
 Monday, April 16, 2007

TechDays IIS7 Talk

Danke an alle Teilnehmer der TechDays. Es war ein schöne Konferenz und traumhaftes Wetter.

Ein paar der Demos aus meinem IIS7 Talk findet Ihr hier.

 


Conferences
Monday, April 16, 2007 6:17:56 PM UTC  #   
 Friday, April 13, 2007

CardSpace, PPIDs and UniqueIDs – my Conclusion

In the last post I described the unique values you can find in a card, how they are (roughly) generated and how the certificate of the application (relying party) influences this generation.

Now which value do you use to uniquely identify your users?

In a lot of samples the PPID is used. The PPID is unique for each card/relying party pair - but it is not really a good secret. If you would solely base user identification on the PPID, anybody who knows a valid PPID could craft up a card with that claim and use it to log in to your application.

The CardSpace UI does a good job of not disclosing the complete PPID to the user which helps against phishing. But you still enter the realm of shared secrets – and that's exactly what we try to get away from (think passwords). The secrecy of the PPID would depend on how secure applications store them in their databases. The same techniques as with passwords could be applied here (e.g. iterated salted hashing), but there are much better approaches.

What's really secret in a card is the private key –this key is never transmitted to the relying party. Instead the card is signed with the private key, and the public key is sent alongside to verify this signature. The WCF plumbing and the TokenProcessor sample for ASP.NET do all the heavy lifting to verify the incoming cards using the public key.

That means that you could store the public key at registration time with your user record – and when the user logs in, you would compare the public key with the one you stored before. Keith was the first who wrote about this technique, Garret and Vittorio followed up here and here.

Now the key size can be quite big and could also change with new versions of CardSpace over time. This could become a problem with your database design. For storage it would be better to have a fixed key length – enter hashing.

Hashing produces fixed length outputs of variable length inputs, and in addition, you can combine multiple values of a card in the hash. This is often referred to as the "unique id" of a card. The TokenProcessor uses the PPID as a second value for the hash by default (but this can be changed using the IdentityClaimType appSetting in web.config).

Now which value(s) should you use for the unique id? Well it depends... (I know this is lame ;). But it really depends on your application. In classic applications you stored all the user details in your database (first name, last name, email etc..) and provided a UI for the user to change some of these values. In CardSpace these values come from the claims inside the card and in theory there is no reason to store them again – also the UI to change the values is provided by CardSpace itself. Some applications need the user details for offline processing, some don't. Some applications don't need private user details at all and just need some way to distinguish between users. And some applications don't allow the user to change certain values after registration.

So the rule of thumb is, if changing certain values in a card would invalidate the account, combine these values together with the public key to the unique id. So e.g. if you absolutely wanna make sure that changing the email address in the card would render the card useless – and the user has to re-register, do a hash over the email claim and the public key. The next time the user would try to login with that card – he would not get recognized as a registered user in your system.

If you want to allow users to change all values in the card (or you don't care about other values) do the hash over the PPID and the public key. In this case you either don't store any user data and just re-read it from the card every time. Another approach would be to "cache" the data and update your cache on every login based on the incoming claims.

The attached sample is a bare-bones implementation of a register/login system using unique IDs. With that code you can play around with different ways to generate the unique ID. The code is also compatible with the TokenProcessor for ASP.NET which allows sharing users between ASP.NET and WCF.

CardSpaceUniqueIdDemo.zip (25.08 KB)

 


Samples | Work in Progress
Friday, April 13, 2007 5:57:44 PM UTC  #   

InfoCards and Identity Stability

InfoCards don't have the notion of a username in the classic sense. That means you need to find some other unique value to "recognize" your users.

Two unique values are created whenever you send a card to an application for the first time. These values are a public/private key pair and the PPID. The private key is used to sign the card, and the public key is sent along the card so that the relying party is able to verify the signature (and thus authenticate the card – but not the user). The PPID is a claim which is a long number that should make it easy to distinguish between cards. When you send the same card to five different applications, each application will see a different PPID and key pair. This prevents phishing and that one application can try to use your data to log into a different application.

CardSpace generates these values based on the identity of the relying party and a locally stored salt. The exact algorithm that is used to create these values is not public, but the general approach depends on the type of certificate that the relying party uses (e.g. a SSL certificate for a web application).

Since you have to base your user management on one of these values (or even both in combination), you should be aware of the implications when you select or change the certificate of your application.

For EV (extended validation) certificates the generation is based on the Organisation, State, Country and Location fields in the certificate. This means that you can change you certificate over the times as long as it is still an EV cert and these values don't change. You can even have multiple applications/servers with different certificates and share the users between them as long as the above mentioned fields match.
The term EV is kind of a joke IMO. It is the job of a Certification Authority to validate the identity of the person/company that requests the certificate. Obviously most CAs do a questionable job here. So EV really means that they now try to do it properly – and this of course is also more expensive…

For "normal" certificates the approach is totally different. Here the subject fields from all the certificates in the chain, all the way to the root certificate are used. This means that you have to be very careful when you change a certificate. If one of the subjects change, the unique values will be calculated differently. This in turn means that your stored user records are now out of sync with the incoming cards, and you basically lost your users.

You can read more details about that here.

I came across a lot of companies which see certificates just as a necessary evil to get SSL up and running and they choose whatever CA is the cheapest on a yearly basis (basically after a customer reports that their browser gives them a security warning when trying to access the web application).

Be aware that you have to be more careful with your certificates when you plan to use CardSpace as an authentication system.


Work in Progress
Friday, April 13, 2007 10:05:51 AM UTC  #   
 Wednesday, April 11, 2007

Wrap-up of HTTP.SYS Discussion

After my post about punching holes in HTTP.SYS – there was some discussion going on (on this blog, on Keith's blog and in a number of private email conversations). Let me wrap this up.

Kenny commented on my post:

"I think that the main confusion here is around the purpose of http.sys reservations. Reservations are used to prevent squatting. For example, SQL doesn't want other apps, admin or otherwise, to listen on http://+:80/sql/, so they make a reservation with their creds. Reservations are not for preventing malware from listening on your machine. If you have malware on your machine it can just open a socket if it wants to receive data."

Keith then posted this:

"If Kenny is correct, then it would seem to me that things would work the other way around: that is, by default you'd be allowed to register with HTTP.SYS unless some other application set up a reservation on that port/prefix. But by default, HTTP.SYS is locked down (except for the holes that Dominick is discussing)."

and:

"Squatting on a port is one attack. But it's only one of a myriad of attacks that this reservation system prevents. I cannot imagine that squatting was the only consideration. The port sharing ability of HTTP.SYS makes it even more vulnerable to malware than simply opening a socket. If my exploit can port share with an existing, legitimate service, you might not notice it there if you're using a traditional tool such as netstat. Plus, firewalls are configured to control access to ports - sharing a legitimate port might allow my malware to communicate with the cloud."

I'm sure malware authors worldwide rejoiced when HTTP.SYS port sharing was introduced. I'd say that concerns like Dominick's are well founded."

In the meantime Kenny posted this (shocking IMO) comment:

"To followup, there is a confluence of unfortunate events that actually turn this into a larger concern:
Because of the absence of proper Http.sys – Firewall interaction (you cannot do per-app or per-service firewall exceptions with Http.sys), you have to use reservations to prevent firewall bypass.

It also turns out that WS-Man conveniently installs a firewall exception on port 80 (scoped to system). So with the WCF reservation for world, on every Vista client machine the port 80 is opened for anybody at the WCF temporary addresses prefix."

It turns out that this is not as catastrophic as it first looked because the WS-Man firewall exception is not enabled by default (which is good news). So be aware that when you should ever open port 80 on a machine you might be sharing ports…

Kenny also asked:

"I understand the concerns here. I do have to wonder though, if you have malware running on your machine, can't it just open a client socket to the cloud (which actually is worse since it will traverse your client-side NAT)? Is the difference Reputability since you can check what the client is connecting to?"

and Keith replied:

    "Yep. It could. But it'd be noisy.

Windows Firewall, for example, would prompt the user whether she wanted to "unblock" the application. Any other sort of monitoring tool worth its salt would notice this as well.

Compare this to simply registering as a listener on an existing port via HTTP.SYS. The port is already open as far as the TCP/IP network stack is concerned, so would monitoring software notice this? I don't know.

You have to ask yourself, why go to the bother of locking something down with an ACL if you're then going to configure it loosely so that the ACL doesn't have any effect. I believe that's what Dominick may have been feeling when he wrote his post."

In some private conversation I heard the comparison of the temporary listen URIs to a temp directory on the hard disk: "It is just as opening up a small part of the hard disk as opposed to the whole system." I personally think this analogy is OK for squatting, but does not work very well for the malware opening a listener case.

A listener is a listener, regardless of where he is listening. If some widespread malware establishes a listener on some open URI, this becomes a well known URI.

Remember Back Orifice? This back door was distributed via various means. But BO was not necessarily also exploited by the same people that distributed it. Instead there was also scanning software available that could scan complete address ranges for BO. So a lot of people just used an already installed BO to exploit a machine…

So to sum up:

  • The HTTP ACL system seems to target squatting only – in that case I wonder why it is not implemented as described by Keith above
  • There are several mitigating factors like the Windows firewall and potential NATs
  • You may not be always behind a NAT
  • With the reservation open for world, some malware could potentially open a listener on a system without a single prompt – no UAC, no firewall.
  • Malware establishing outgoing connections (as opposed to listeners) is also a big concern

Make up your mind yourself.


Work in Progress
Wednesday, April 11, 2007 6:56:19 AM UTC  #   
 Wednesday, April 04, 2007

UAC is for Developers

I recently wrote this short article for the latest DevelopMentor newsletter. It is not as detailed as it could be – but I only had 1000 words. Maybe it is still interesting for you.

 

Windows Vista finally starts to address one of the biggest security problems we have in software development today – the administrator problem. Most applications don't need administrative rights, most services don't need them either – but strangely a lot of applications simply don't work when run as a normal user thus forcing your clients (e.g. in corporate environments) to run as administrators. Why is that?

This is kind of a chicken and egg problem. The developers writing these applications also mostly run as administrators and often just don't properly debug and test their code under non-administrative conditions. The changes in Vista security try to break this vicious circle.

The UAC (User Account Control) feature in Vista makes administrators have kind of a split personality. Even if a user is in the administrators group, all administrative privileges are stripped out of their default security token. So whenever an application requests these powerful privileges, Vista puts up a consent dialog to inform the user about this fact. After the user confirms, Vista will put the full token containing all groups and privileges on the newly created process and thus giving it all rights a "normal" administrator would have. This only applies to administrators – not standard users.

This serves two purposes. First of all, people that insist on being administrators on their machines run with reduced privileges by default which makes it much harder for malware to cause harm to your system (but not your user data of course). For developers this makes it much easier to find out if their applications work with a normal privileged user account. If your apps work elevated but not with the default security context – you most probably do something that requires elevated privileges and you should think about if this is really needed.

The most common problems why applications break when run as normal users are very easy to fix. Check e.g. if you are trying to write to a location where you don't have write access to. Popular locations are e.g. "Program Files", the Windows directory or HKLM in the registry – which is just forbidden. You should always put user data into the profile – and it is incredibly easy to do that. The following line of code returns the per-application directory in the profile – similar enum values exist for "My Documents", the desktop and so on:

string path = Environment.GetFolderPath(
  Environment.SpecialFolder.ApplicationData);

Now – another new feature of Vista makes this a little bit harder to test – it is called Virtualization. By default Vista redirects write attempts to some forbidden locations to a per user store. This includes parts of "Program Files" and parts of the registry. This feature is enabled by default to not break legacy applications running in this new and tighter security context on Vista. But what makes an application "legacy"?

Applications express their "Vista readiness" by embedding a so called manifest into the binary. The existence of this manifest makes Vista turn off the compatibility features like Virtualization and an attempt to write to the above mentioned locations will result in an access denied error. Manifests further express the security needs of an application, e.g. if they are designed to work with normal privileges as opposed to administrative applications. Embedding a manifest into applications is by the way a Vista logo requirement.

A manifest is a resource file that looks like this:

#include <winuser.h>
#define IDR_MANIFEST 1 // 2 for a DLL

IDR_MANIFEST RT_MANIFEST MOVEABLE PURE
{
  "<assembly xmlns=""urn:schemas-microsoft-com:asm.v1"" manifestVersion=""1.0"">
    <asmv3:trustInfo xmlns:asmv3=""urn:schemas-microsoft-com:asm.v3"">
      <asmv3:security>
        <asmv3:requestedPrivileges>
          <asmv3:requestedExecutionLevel
            level=""asInvoker""
            uiAccess=""false"" />
        </asmv3:requestedPrivileges>
      </asmv3:security>
    </asmv3:trustInfo>
  </assembly>"
}

The level attribute tells Vista about the security needs of the application. "asInvoker" starts the application with the current security token of the user – which is by default non-administrative. There is another value called "requireAdministrator" which tells Vista that this application is designed to be used by administrators only and the user will see the elevation prompt at application startup.

Before Visual Studio "Orcas" there is no automated way of embedding this resource files into your assemblies – you have to do the following steps:

Compile this file (which should have an extension of ".rc") to a binary resource file using the resource compiler:

rc.exe manifest.rc

This will result in a ".res" file that you can embed using the project properties dialog in Visual Studio. Use the "Resource File" option in the "Application" tab for that. That's it – you are done.

Now the application will behave "Vista compatible" – which is exactly the same behavior as running it on pre-Vista operating systems under a non-administrative account. You are now at a point where you can easily test the application under normal user conditions. And when you start Visual Studio non-elevated (which you should do contrary to what most blog entries tell you), you can also debug it using the standard-user security context.

In the case you plan to write applications that expose both user and administrator functionality you can also programmatically find out in which security context the application was started and modify the application based on that. The following line of code tells you about your state:

bool isElevated = newWindowsPrincipal(
  WindowsIdentity.GetCurrent()).IsInRole(
    WindowsBuiltInRole.Administrator);

By talking to the shell (and thus the consent dialog) you can also programmatically elevate your application programmatically. You can use the standard .NET process API for that:

public static void StartElevated(
  string filename, string workingDirectory, string arguments)
{
  ProcessStartInfo startInfo = newProcessStartInfo();
  startInfo.UseShellExecute = true;
  startInfo.Verb = "runas";
  startInfo.WorkingDirectory = workingDirectory;
  startInfo.FileName = filename;
  startInfo.Arguments = arguments;

  Process.Start(startInfo);
}

You should always make a conscious decision about who the target audience of your application is. If it is meant to be used by administrators then you need administrative privileges. If it is meant for normal users, you should be very careful to not introduce unneeded dependencies on administrative privileges. This is nothing new in Vista – UAC just makes it easier now for developers to switch between contexts and to find out if your apps work in both "worlds". Happy testing!

 


Work in Progress
Wednesday, April 04, 2007 5:41:48 AM UTC  #   
 Tuesday, April 03, 2007

Making my Visual Studio better

James, a student on this week's WCF course has found solutions to two things that bugged me in Visual Studio:

  • Removing the #region tags around interface implementations when using the smart tag "Implement Interface"
  • Setting the default startup mode to "Current Selection"

James has the details for the #region tags here – for the startup mode, change the "DefaultBehaviorForStartupProject" from 0 to 2.

Thanks James! And indeed a good reason to start a blog ;)


For Your Favourites
Tuesday, April 03, 2007 9:40:50 PM UTC  #   
 Sunday, April 01, 2007

WCF Performance Comparison

Microsoft has published a paper that compares WCF to other communication stacks (WSE, ASMX, ES…). Of course WCF is the fastest of them all (in most situations). It would have been nice to give us the test code to verify this against our own networks…

The following section made me wonder (quoting):

"3.3.2 Self-Hosted Secure Request/Reply TCP Application

In this section, the performance of WCF and ES are compared for the same message loads as the previous section (Section 3.3.1) with security enabled. Specifically, transport-level SSL security is employed and ASP.NET Role principle is used for the authorization. Figure 9 shows that the performance of ES on a uni processor is faster than WCF by 24% for the primitive message type while for the order message type, WCF is faster than ES by 69%."

Anyone ever seen Enterprise Services running over SSL (and what does ASP.NET has to do with this)?

Check it out yourself:
http://msdn2.microsoft.com/en-us/library/bb310550.aspx

 


Work in Progress
Sunday, April 01, 2007 1:12:29 PM UTC  #   

WCF Faults and Information Disclosure

I am sure you all have seen these kind of exceptions when working with WCF:

System.ServiceModel.FaultException: The server was unable to process the request
due to an internal error. For more information about the error, either turn on
IncludeExceptionDetailInFaults (either from ServiceBehaviorAttribute or from the <serviceDebug> configuration behavior) on the server in order to send the exception information back to the client, or turn on tracing as per the Microsoft .NET Framework 3.0 SDK documentation and inspect the server trace logs.

Server stack trace:

at System.ServiceModel.Channels.ServiceChannel.ThrowIfFaultUnderstood(Message reply, MessageFault fault, String action, MessageVersion version, FaultConverter faultConverter)
at System.ServiceModel.Channels.ServiceChannel.HandleReply(ProxyOperationRuntime operation, proxyRpc& rpc)
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs)
at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)

Why do the default exceptions have to be so verbose, telling everyone the exact toolkit running on the server – and even a stack trace…??

This is (unnecessary) information disclosure.


Work in Progress
Sunday, April 01, 2007 12:49:14 PM UTC  #   
 Tuesday, March 27, 2007

Decrypting CardSpace Tokens in Partial Trust

One way to overcome the problem I described in this post would be to run in partial trust. This way you could factor out the code that does the encryption while the rest of your application doesn't even have file IO access to the private key file.

The problem is that the TokenProcessor code from the SDK has a lot of dependencies on System.ServiceModel/IdentityModel – and they require full trust (no APTCA). After massaging the decryption code a little bit, I was able call it from a partially trusted web app. The necessary steps were:

  • Put the code into a separate assembly
  • Assert full trust
  • Add APTCA
  • Re-implement the ClaimTypes class (so that the web app doesn't need to use the full-trust only WCF class)
  • Put it in the GAC / write a custom policy

Works fine. (download)

 


Work in Progress
Tuesday, March 27, 2007 7:02:56 PM UTC  #   
 Friday, March 23, 2007

Punching Holes into HTTP.SYS

If you want to open a listen URI with HTTP.SYS you either need administrative privileges or an administrator that reserves the URI for normal users. I wrote about this here and here – and even wrote a tool to make that step easier (and given the download numbers, this seems to be helpful for a lot of people).

The whole reason for ACLs on listen URIs is that malware can't easily open a new listening port – or even trickier – hide behind an already opened port (at least that was my understanding).

You can view all existing reservations on your machine using either the httpcfg.exe tool:

httpcfg.exe query urlacl

or netsh on Vista/LHS:

netsh http show urlacl

Now on a typical machine you will find quite a lot of reservations – and even worse some of them are not ACLed for specific accounts (like service or machine accounts), but generally for 'users' or even 'everyone'.

One of those "wildcard" reservations is this one:

URL: http://+:80/Temporary_Listen_Addresses/
ACL: D:(A;;GX;;;WD)

This reservation comes with .NET 3.0/WCF to make duplex communication over HTTP easily possible (and the WD stands for 'everyone').

So if some malware wants to open a highly sophisticated listening port on your machine (e.g. using HttpListener, WCF or the C++ HTTP API) – they can just use one that Microsoft already opened for them….

Doesn't this totally defeat the purpose???


Work in Progress
Friday, March 23, 2007 5:46:01 PM UTC  #   

SQL Server 2005 Security

One of my favorite database guys wrote a whitepaper about my favorite topic: security.

Check out "SQL Server 2005 Security Best Practices" – interesting read!


For Your Favourites
Friday, March 23, 2007 5:42:29 PM UTC  #