Wednesday, September 03, 2008

Zermatt: Source Code for FormsAuth STS

I got several requests to publish the full source of the FormsAuth STS I described here. Find it here. The setup is just like the passive STS/RP samples in the SDK.

HTH

 


IdentityModel
Wednesday, September 03, 2008 10:55:48 PM UTC  #   
 Sunday, August 17, 2008

P2P and WCF: Some Troubleshooting Resources

These are helpful resource when troubleshooting P2P:

Checking cloud status:
netsh p2p pnrp cloud show list

Checking Teredo status:
netsh int teredo show state

Enabling ping over P2P

P2P Traceroute

PNRP Debugging Guide

PNRP and WICN on Server 2008


WCF
Sunday, August 17, 2008 5:47:17 AM UTC  #   
 Friday, August 15, 2008

Zermatt: Using Forms Authentication in a Passive STS

Since all Zermatt samples use Windows authentication to auth against an STS - the question how to use forms authentication instead popped up several times. It is easy to do that.

Basically a passive STS endpoint is an ASP.NET handler - could be a plain IHttpHandler, an .ashx or a page. All samples simply hook the Page_PreRender event to render the redirect logic. But you could also show a UI before doing that. This means you can put e.g. a login control on your issuing page and manually verify username/password credentials before issuing the token.

My sample issuing page looks like this:

<html xmlns="http://www.w3.org/1999/xhtml">
<
head runat="server">
    <title>Forms Authentication Sign In</title>
</
head>
<
body>
    <form id="form1" runat="server">
    <div>
        <asp:Login runat="server" ID="_login" OnAuthenticate="_login_Authenticate" />
    </div>
    </form>
</
body>
</
html>

In the Authenticate event you verify credentials somehow, and if successful, create a ClaimsIdentity and  issue the token. In my sample I additionally issue a forms auth ticket for the STS domain so that returning users don't have to re-authenticate using the form.

Another approach to make the federation token apply to more than one RP is to modify the cookie domain (if the RPs are in the same domain).

protected void _login_Authenticate(object sender, AuthenticateEventArgs e)
{
    if (ValidateUser(_login.UserName, _login.Password))
    {
        FormsAuthentication.SetAuthCookie(_login.UserName, false);

        ClaimsIdentity identity = new ClaimsIdentity(
            new Claim(System.IdentityModel.Claims.ClaimTypes.Name, _login.UserName),
            "UserName");
        ClaimsPrincipal principal = new ClaimsPrincipal(
            new ClaimsIdentityCollection(identity));

        ProcessFederationMessage(principal);
    }
}

HTH


IdentityModel
Friday, August 15, 2008 6:58:18 AM UTC  #   
 Thursday, August 14, 2008

How to build a Development/Test/Demo CA

I often need X509 certificates - but I never really became friendly with makecert. So I ended up running Windows Cerificate Services which proved to be an easy to use, robust solution. You can have one at home or carry it around in a VM. Perfect.

There are some gotchas you can run into and I refined my configuration over the years. I just had to rebuild my Test CA (on Hyper-V) so I thought I'll document the important steps (for self-reference and whoever might be interested)

 

Basic Installation

  1. Install Windows Server (2003 or 2008) - either phyiscally or virtually.
  2. Install Certificate Services.
  3. Select Stand-alone CA.
  4. Choose a reasonable lifetime for the CA cert (like 10 years)
  5. Backup the CA cert. You need this when rebuilding the machine or having several installations act like the same CA.

 

Certificate Lifetimes
By default certificates issued with the CA have a lifetime of one year. They usually expire on the very day where you have to do an important demo. You can configure the lifetime in the registry. See here for details.

 

Revocation Lists
Another common reason why you run into problems with certificates are revocation lists. The location of the revocation list(s) is embedded in certificates in the CDP (CRL Distribution Point) extension. Now by default there is an entry pointing to the CA's Netbios name. Accessing that network location works while you are in your home network and the CA is up and running. If this network location cannot be accessed, many applications and frameworks will fail certificate validation (the default WCF binding security settings e.g.).

Simply removing the complete CDP extension would be one workaround - but some apps don't like that at all. So I wouldn't recommend doing that. A better solution is to create a revocation list once, and store that at a publicly available location. You can also set the lifetime of that revocation list to some high value (like 5 years), so Windows will cache the list. This way you have access to your CRL while on the road and due to the high lifetime value, this will even work when having no network access at all.

To change the CDP settings, open the Certificate Services MMC snap in. Right click on your CA and then select properties. On the extensions tab you can see the list of CRL locations. The first entry specifies the CA local location where CRLs are published. The last three locations get embedded in the certificate. You can delete them and add you publicly available location. Make sure you check the "Include in the CDP extensions of issued certificates" box for your new location.

Next you have to publish a CRL. First you have to set the CRL lifetime. This is configured in the properties of the 'Revoked Certificates' folder in the snap in. Set it to e.g. five years. Next you publish via right-Click -> All Tasks -> Publish. You can find the CRL at the previously configured local location.

The last step is to copy the CRL to your public location and that's it.

Now you can browse to the CA's web interface (http://server/certsrv) and request certificates. After requesting a certificate you have to go to the CA's MMC console and issue the cert (in 'Pending Requests'). Then you can download the certificate by returning to the web interface.

HTH


Work in Progress
Thursday, August 14, 2008 2:05:12 PM UTC  #   

Most important bug fix in 3.5 SP1

BradA says:

"We have brought managed executables in line with native code executables in how they behave when run off a network share.  Yea!"

VanceN says:

"Hurray, its finally fixed!  manage code 'just works' from network file share!"

Grats for fixing this bug!

 

(hint: some sarcasm is hidden in this post ;)


Misc
Thursday, August 14, 2008 9:06:21 AM UTC  #   
 Thursday, July 24, 2008

WCF Claims to "Zermatt" Claims Migration Story

Hey - that's a short post - there is none. thanks. bye...

OK hold on - some background info.

Microsoft introduced their new claims API with .NET 3.0 as part of the System.IdentityModel assembly (with super tight integration into WCF). Needless to say that I really like the claims approach - and I have written quite a lot about it here.

Also since that day we were all waiting for that super secret identity framework that was supposed to extend the claims API and which will finally give us easier support for security token services and Information Card related technologies. Now it is in beta and is called "Zermatt". Cool. Life is good.

After some experiments and proof of concepts - it turns out life is not so good.

To enable the "Zermatt" model in WCF you basically have to call the ConfigureServiceHost method on the ExtensibleServiceCredentials class (at least that's how it works with the current bits). As soon as you enable Zermatt the usual places where security happens in WCF don't work anymore - namely ServiceSecurityContext and AuthorizationContext. Interested readers of my blog know that these classes are the entry point into the claims based world in WCF. Also if you have made any investments already into the claims model, your code relies on these classes. If they are "gone", your code is broken.

All the claims functionality found in WCF is now replaced by the Zermatt model and there is no connection whatsoever between them. You'll also find that Zermatt has its own version of a Claim class (and other structural classes too). This basically means that if you have already invested in the WCF claims model but wanna move on to Zermatt, you have to throw away all your code and start from scratch. There is no migration or co-existence story between WCF claims and Zermatt claims.

OK - the next thing I tried is how existing WCF authorization policies work together with Zermatt. The results are quite mixed. First of all external policies only work with certain credential types - and Right.Idenity claims are not supported at all.

Fortunately, there is a forum for Zermatt so I can ask some MSFT people about their take on that. Here's what I got back (I'd love to provide links to the forum posts - but unfortunately this forum is sooo cool and ajaxy that one of the most important features of the web - called URLs - don't work):

"Hi Dominick,
Deeper investigation on my part into what I suspected was a bug turned
out to be by design. The scenario you are attempting (upgrading a
legacy app that depends on the WCF claims model) is not supported in
Zermatt.

Once you opt into Zermatt's claims model, the WCF claims API does not
work anymore. The reason for this behavior is the new claims model in
Zermatt is not backwards compatible with the WCF claims API and it is
not feasible to support both the old and new models at the same time
for WCF applications.

As you have called out earlier in a separate discussion, you have to
choose between staying with the WCF API and not use Zermatt, or move
your application to Zermatt. If you do choose to migrate, any code
that depends on the WCF claims API will need to be rewritten."

Have you also spotted the word "legacy" wrt to WCF??? They must be kidding me!

Here's my answer:

"I don't understand the problem - why can't you take existing authZ
policies and transform them into a ClaimsIdentity - this would be an
easy solution for the migration problem.

Frankly - this sucks.

What do I tell my customers? Sorry for leading you into the
System.IdentityModel direction? Your code will not move forward?

You have a brand new communication framework with a brand new claims
based authZ model - now you are releasing a brand new identity
framework that just disables the technologies used before???? This is
not right.

I don't ask for a full compatibility story between S.IM and Zermat -
but i don't want to throw away all my code (or my customers code)."

Am I asking for too much? Is this a non-issue? I filed a bug for that here. If this is also important for you - feel free to vote.

 

Disclaimer: Don't get me wrong. I don't want to bash Microsoft or the Zermatt team, nor the people on the forum which are really helpful. I just think this is a wrong design decision. And I read somewhere that Microsoft is looking for feedback. So here it is.


IdentityModel
Thursday, July 24, 2008 10:06:31 AM UTC  #   
 Friday, July 18, 2008

Try "Zermatt" and give Feedback

The last week I spent some time exploring parts of the "Zermatt" framework - some things are really cool - some I have mixed feelings about.

The team is still in a quite early stage where design decisions and directions are (re-) considered. Since "Zermatt" is the main Microsoft identity framework we have to live (and work) with for the next years, take you chance to shape it!

I have already started some discussions, so feel free to contribute.


IdentityModel
Friday, July 18, 2008 7:35:37 AM UTC  #   
 Friday, July 11, 2008

CLR Security Site on Codeplex

The CLR security team has a site now on Codeplex - Shawn has all the details here.

Good stuff!


For Your Favourites | FX Security
Friday, July 11, 2008 6:15:18 AM UTC  #   
 Wednesday, July 09, 2008

Skiing in "Zermatt"

Today, Microsoft finally announced the first public version of their .NET identity framework code named "Zermatt". The most important things you get from this framework are:

  • APIs for the token/claims related heavy crypto lifting
  • Supporting classes for claims aware applications (including an IIdentity/IPrincipal implementation to give you a common programming model and smooth migration path)
  • ASP.NET plumbing for accepting tokens in web applications
  • ASP.NET controls for adding Information Card support to web applications
  • OM for creating Information Cards
  • Framework and base classes to write security token services (for active and passive scenarios)

If you have already started looking into claims based security, this framework extends the concepts found in System.IdentityModel and makes it much easier to exploit the full power of token/claims based security systems. See here for the official announcement.

You can download the bits and a good whitepaper (written by Keith) here:

https://connect.microsoft.com/site/sitehome.aspx?SiteID=642

Stay tuned ;)


IdentityModel
Wednesday, July 09, 2008 10:38:23 PM UTC  #   
 Wednesday, July 02, 2008

Using IdentityModel: Useful Extension Methods for Serializing Claim Sets

As a follow up to my last post - the following extension methods make it easy to manually serialize claim sets:

public static XElement Serialize(
  this ClaimSet set, IEnumerable<Type> knownTypes)
{
    DataContractSerializer dcs = new DataContractSerializer(
        set.GetType(),
        knownTypes,
        int.MaxValue,
        false,
        true,
        null);
   
    MemoryStream ms = new MemoryStream();
    dcs.WriteObject(ms, set);
    ms.Seek(0, SeekOrigin.Begin);
   
    return XElement.Load(new XmlTextReader(ms));
}

 

public static XElement Serialize(
  this IEnumerable<ClaimSet> claimSets, string rootName,
  string rootNamespace, IEnumerable<Type> knownTypes)
{
    XNamespace ns = XNamespace.Get(rootNamespace);

    return new XElement(ns + rootName,
                    from cs in claimSets
                    select cs.Serialize(knownTypes));
}


IdentityModel
Wednesday, July 02, 2008 9:13:54 PM UTC  #   

Re:MVP

Quoting Brian:

"Microsoft has decided I didn't cause too much trouble over the last 12 months so I get to continue being a {0} MVP. Thanks!", "Developer Security"


Misc
Wednesday, July 02, 2008 8:53:58 PM UTC  #   

Using IdentityModel: Serializing Claim Sets

Both Claim and ClaimSet are decorated with DataContract/DataMember attributes. This means they are made for serialization. And this makes sense - maybe you want to forward a claim set (server to server) or send a claim set from server to client (UI authorization).

But you will most likely run into problems when trying to serialize a claim set using the DataContractSerializer.

Known Types
DCS needs to 'know' all types that are involved in the serialization process. This involves every type in the inheritance chain down to ClaimSet (e.g. DefaultClaimSet or my DeferredLoadClaimSet) as well as all possible resource types. You either supply the known types via attributes/config (KnownType and ServiceKnownType).

Or you supply the types when newing up the DCS manually:

DataContractSerializer dcs = new DataContractSerializer(
    typeof(ClaimSet),
    new List<Type> { typeof(DefaultClaimSet), typeof(UIClaimResource) });

 

Circular References
Typical claim sets will have circular references - e.g. when the last issuer in the chain points to himself. DCS is not made for cyclic reference - but rather object trees (at least with the default settings). When you are trying to serialize objects with cyclic references you will get the following exception : "type contains cycles and cannot be serialized if reference tracking is disabled.". In WCF traces you will see something like "message not logged because its size exceeds configured quota".

When newing up a DCS you can opt for "preserving object references". This will create ID/IDREF pairs in the serialized XML and allows for type references and thus cycles. (Aaron has an explanation of how that works).

DataContractSerializer dcs = new DataContractSerializer(
    typeof(ClaimSet),
    new List<Type> { typeof(DefaultClaimSet), typeof(UIClaimResource) },
    int.MaxValue,
    true,
    true,  // preserveObjectReferences
    null);

 

This is fine when you can control the DCS parameters. But you can't easily do that in WCF. Sowmy has a sample on how to enable reference preserving in WCF. This will solve the problem!

[OperationContract]
[ReferencePreservingDataContractFormat]
ClaimSet GetClaims();

 

3.5 SP1 to the Rescue!?
Starting with 3.5 SP1 you can enable reference preserving on a DataContract like this:

[DataContract(Namespace = "...", IsReference = true)]
public abstract class DeferredLoadClaimSet : ClaimSet

 

But there are two problems with this approach:

  • You actually need access to the DataContract to change the attribute. In the claims case - you would need to change the framework's DefaultClaimSet or your own ClaimSet-derived class.
  • Every DataContract in the inheritance chain needs the IsReference attribute - otherwise you will get the following error: "Derived types must have the same value for IsReference as the base type". Since all custom claim sets ultimately derive from ClaimSet - but this DataContract has no IsReference set, we are back to square one.

 

Conclusion
Keep these things in mind when serializing claim sets:

  • supply all involved types as known types
  • Set preserveObjectReferences to true on the DCS. The new attribute on DataContract in 3.5 SP1 is nice - but does not help with claim sets. Use the [ReferencePreservingDataContractFormat] attribute instead (find the code here).
  • Reference preserving adds ID/IDREF attributes to the resulting XML. These attributes come from a Microsoft namespace. This may be a problem for interop scenarios. If you need full control over the XML, either use the DCS extensibility points for manual serialization, or don't use the DCS at all (and use one of the alternative message generation mechanisms). Another option would be to use a more standardized serialization format for claims like a SAML token.
  • WindowsClaimSet and X509CertificateClaimSet are not marked with [DataContract] at all - they are not intended for serialization.

HTH


IdentityModel
Wednesday, July 02, 2008 8:00:28 AM UTC  #   
 Sunday, June 15, 2008

PowerShell Profile

Putting these three things (and a little bit of this) together - you can build a very nice profile script for PowerShell ;)


Work in Progress
Sunday, June 15, 2008 2:44:38 PM UTC  #   
 Thursday, June 12, 2008

Advanced Extensions to IIS 7 Configuration

Great article about IIS 7 configuration extensibility:

http://learn.iis.net/page.aspx/241/configuration-extensibility/

Especially infos about the COM backed extensions are hard to find elsewhere...


IIS
Thursday, June 12, 2008 10:55:59 PM UTC  #   
 Saturday, June 07, 2008

Software Architect 2008

Thanks to everyone who attended my IdentityModel talk at Software Architect.

You can have all the code I showed you during my talk - just send me a private message or leave a comment. Most of the demos are online anyways - have a look at my IdentityModel micro-site.

Questions and feedback are more than welcome. Happy identity-ing.


Conferences | IdentityModel
Saturday, June 07, 2008 4:44:43 AM UTC  #   
 Thursday, June 05, 2008

Writing IIS 7 Manager Extensions

Good walkthrough here:

http://learn.iis.net/page.aspx/441/understanding-ui-extension-authoring/


IIS
Thursday, June 05, 2008 9:16:30 AM UTC  #   
 Wednesday, May 28, 2008

SQL Server Security Best Practices

Bob wrote me an email as a response to this post. He also directed me to this whitepaper he wrote about SQL Server Security. Interesting read!


For Your Favourites
Wednesday, May 28, 2008 8:39:37 AM UTC  #   
 Monday, May 26, 2008

OpenID Phishing Demo

Funny and educational:

http://idtheft.fun.de/


For Your Favourites | IdentityModel
Monday, May 26, 2008 8:02:32 PM UTC  #   

System Accounts and SQL Server 2005

I recently ran into a strange situation - I was expecting an "access denied" but it didn't happen (yes - security guys are strange people ;). Here's the long story:

I was writing some test code for LINQ to SQL (see here) in ASP.NET. Since this was on a freshly installed box I was expecting an access denied since I hadn't created a SQL login for Network Service yet. But it worked - I could successfully query (and update) data in all databases. Shock.

After some investigation I found the reason for this behavior. Since I was using SQL Express, the SQL instance was running as Network Service (the default). Furthermore setup creates a Windows group for SQL Server service accounts (e.g. MACHINE\SQLServer2005MSSQLUser$...) and puts Network Service in there. It turns out that this Windows group is mapped to a SQL login with a server role of sysadmin...

This means (on my machine) that all SQL clients running as Network Service (or can get an impersonation token for that account) have sysadmin privileges in the SQL Server installation. Or more generally - when a client can use the same Windows account as SQL Server itself - it will get sysadmin privileges

I thought I might point this out, since running SQL Server and ASP.NET as Network Service seems to be a pretty common configuration.

The moral of the story: Always create dedicated service accounts for SQL Server (or every service you install).

btw - the full blown SQL Server installation specifically asks you for the account to use (but also gives Network Service as a choice).


Work in Progress
Monday, May 26, 2008 11:26:37 AM UTC  #   
 Friday, May 23, 2008

Avoid unhandled Exceptions in WCF Error Handlers

The IErrorHandler interface in WCF allows to write some central error handling code that gets invoked whenever an unhandled exception bubbles up from your service. There are two methods to implement:

  • ProvideFault - called on the request thread to turn the exception into a fault message
  • HandleError - called on a separate thread for error logging and the like

While WCF tries its best to shield the service host from all kinds of error conditions, there are some situations where unhandled exceptions can hurt your hosting process. One of them is the HandleError method on IErrorHandler.

HandleError is called on a background thread to allow doing (kind of) lenghty operations without impacting the request where the error originally occurred. If you have an unhandled exception in HandleError the normal CLR rules for excpetions in background threads apply - which means shutting down the process. Be careful here.


WCF
Friday, May 23, 2008 8:26:30 AM UTC  #   
 Sunday, May 18, 2008
 Thursday, May 15, 2008

Two important Security changes in .NET 3.5 SP1

Shawn details the two big security changes in .NET 3.5 SP1 on his blog:

We have discussed both changes internally - and I have mixed feelings about them. I guess the most important thing to be aware of is, that they are not opt-in changes. By installing SP1 - the behavior will change automatically - if you like it or not.


FX Security
Thursday, May 15, 2008 6:53:45 AM UTC  #   
 Wednesday, May 14, 2008

Improved IisRegMgmt

Thanks to CarlosAg from the IIS team, I was able to improve my tool for registering IIS 7 management modules.

IisRegMgmt01.zip


IIS
Wednesday, May 14, 2008 6:34:10 AM UTC  #   

P2P and WCF: The PeerName Tool

To play around with peer name registration and resolution, I wrote a little tool that makes this easy (yes - I know all this functionality is also available via netsh - but I wanted something more specialized).

Registering

Resolving

PeerName.zip (27.31 KB)

 


WCF
Wednesday, May 14, 2008 6:03:31 AM UTC  #   
 Monday, May 12, 2008

Using IdentityModel: Tracing

While reading through some of the code of System.IdentityModel, I noticed that there is some diagnostics tracing going on. Just add a trace listener for the source 'System.IdentityModel' to your config file.

HTH


IdentityModel | WCF
Monday, May 12, 2008 5:28:09 PM UTC  #   
 Friday, May 02, 2008

P2P and WCF: Some Resources

If you want to know more about P2P and its related protocols and components, here's a list of online resource I found useful while researching:

Have fun!


For Your Favourites | WCF
Friday, May 02, 2008 4:09:01 AM UTC  #   
 Thursday, May 01, 2008

P2P and WCF: Exposing a Service

The last post explained how to find a PNRP registered service. What else do you have to do for e.g. exposing a WCF service over the P2P infrastructure?

Code-wise nothing. If the WCF service listens on all NICs (the default), a client can do a resolution via the peer DNS name and connect to it. Easy.

Well - hold on - does that mean that arbitrary clients can now traverse my NAT and connect to my intranet machine? Kind of - yes...

For the service to be accessible you also have to adjust firewall rules:

  • the port the service is listening on must be openend (this will allow normal TCP/IP traffic to the endpoint)
  • to allow Teredo traffic to the service, additionally the "allow edge traversal" option must be checked. This option is only available via the advanced firewall (available via Administrative Tools or MMC). See screenshot:

So to recap - these are the prereqs for a globally reachable service:

  • P2P (PNRP and Teredo) must work properly
  • the service must be registered
  • the client (or peer) must know the peer name
  • the endpoint port must be opened in the firewall
  • Teredo traffic must be allowed for this port

But one thing is very true, you now allow (internet) inbound traffic to an intranet hosted service, which has some implications:

  • there is probably no security around that intranet machine (like a DMZ).
  • intranet machines are typically not hardened for exposing internet services.
  • this means that if the service has some security problem (e.g. directory traversal etc), there are no safe-nets that will stop an attacker e.g. accessing other machines or system resources.
  • your administrators may not like this!

Typical P2P scenarios don't necessarily involve publicly known peer names, so you maybe only have a limited exposure. But still - the traffic bypasses perimeter security and goes directly into the intranet. So be careful.

 


WCF
Thursday, May 01, 2008 9:26:52 AM UTC  #   

P2P and WCF: Finding a Service

After you have registered a service, the next step is to find it again. The System.Net.PeerToPeer API includes a PeerNameResolver class which does that.

You input the peer name and get back the registration details (IP addresses, port, comment and data):

static void Resolve(string name)
{
    PeerNameResolver resolver = new PeerNameResolver();
    PeerName peerName = new PeerName(name);

    Console.WriteLine("Resolving {0}...", peerName);
    Console.WriteLine();

    PeerNameRecordCollection results = resolver.Resolve(peerName);

    if (results.Count == 0)
    {
        Console.WriteLine("No records found.");
        return;
    }

    int count = 1;
    foreach (PeerNameRecord record in results)
    {
        Console.WriteLine("Record #{0}\n", count);
        Console.WriteLine("DNS Name: {0}", record.PeerName.PeerHostName);

        Console.WriteLine("Endpoints:");
        foreach (IPEndPoint endpoint in record.EndPointCollection)
        {
            Console.WriteLine("\t Endpoint:{0}", endpoint);
        }

        count++;
    }
}

The way you will resolve peer names more commonly is via the DNS format (the PeerHostName property in the above code). A peer name also has DNS name representation (e.g. foo.pnrp.net for an unsecured service named foo). Whenever you use this format (e.g. with ping or any other application that does DNS name resolution), Windows will use the P2P APIs internally to return the corresponding IP address). See here for the details.

This e.g. means that you could make a registration on a web server for port 80 and can use the browser to directly connect to the web server using the DNS format name.


WCF
Thursday, May 01, 2008 6:43:10 AM UTC  #   

P2P and WCF: Registering a Service

To make a service discoverable using the P2P infrastructure, you first have to do a so called peer name registration.

A peer name registration has the following properties:

  • a name (there are two different flavours: secured and unsecured - more on that later)
  • one or more IP addresses and scope
  • a port number
  • a comment (optional)
  • up to 4KB of binary data (optional)

Peer Name
The name of the service you want to register. Names have the following format: 'authorityId.Name'. Unsecured Names use a '0' as the authorityId and are easy to spoof/squat. When using a secured name, a key/pair is generated on the fly (the first time only) to sign the registration request. The public key hash becomes the authorityId in this case.

IP addresses and scope
That's the most fascinating (and complicated) part. The peer name registration can have local and/or a global scope. A global scope means that the service can be discovered and contacted - well - globally. How can that work, given the service is behind a NAT device? IPv6 is the answer.

Now you may ask yourself: "but my network/router hardware is not IPv6 enabled, can this still work?". Yes it does - making the transition between IPv4 and IPv6 is the job of so called transition or tunneling protocols. Teredo is the name of the protocol that is typically used here. Teredo has several jobs - one is to provide a globally unique IPv6 address, the other is to enable NAT traversal. I won't go into the Teredo details here, but this document describes how it works.

When you do a 'ipconfig' on the command line you may already see a bunch of IPv6 addresses. The one that is directly associated with your NIC is the local address. You may also see a "Tunnel Adapter" interface - that would be the global Teredo provided address.

You can check the status/health of the Teredo protocol by using this command: 'netsh int teredo show state'. This article helps you with troubleshooting if Teredo should not be enabled on your machine.

You can also have a look at the scope of your registration by checking the clouds to which your machine has access. This is done by doing a 'netsh p2p pnrp cloud show list'. You should see one or more LinkLocal_ clouds and a Global_ cloud.

Again this article has all the details on clouds and their background.

So to wrap it up - by default a peer name registration will use all available NICs/IP addresses. If you have a global IPv6 address (which means that Teredo is working properly and you can 'see' the global cloud) this one is used also. This in turn means that the service can be used by every client that also has a global address.

The remaining properties are self explaining I think.

The following code snippet would register a secured peer name in all available clouds (you can find the APIs in the System.Net assembly (v3.5):

private void Register(string name, int port, string comment)
{
    PeerName peerName = new PeerName(name, PeerNameType.Secured);
    PeerNameRegistration reg = new PeerNameRegistration();
   
    reg.PeerName = peerName;
    reg.Port = port;
    reg.Cloud = Cloud.Available;
    reg.Comment = _cl.Comment;

    reg.Start();
}

The next posts will deal with peer name resolution and how to host a WCF service over this infrastructure.


WCF
Thursday, May 01, 2008 5:42:58 AM UTC  #   
 Wednesday, April 30, 2008

P2P, PNRP, Teredo...the Motivation

Since I started playing around with computers, communication of machines over a "wire" has been fascinating to me. This is probably why I ended up in the distributed applications space.

Typically clients talk to servers and servers to servers - but less common clients directly to clients. But applications like MSN Messenger or Skype show useful use cases of client to client communication. With Vista and Server 2008 (and XP SP2 + some components) peer to peer networking has become part of the operating system. There is also a peer channel in WCF that sits on top of these core components. Time to have a closer look.

So what features is the P2P infrastructure supposed to give you?

  • Global/local registration of services
  • Global/local connectivity between peers which includes the capability to traverse NAT devices
  • Global/local virtual broadcasting networks
  • Peer/Service Discovery
  • Invitation/activation of P2P enabled applications

All of these capabilities are as fascinating as they are scary. More importantly I really think that P2P communication patterns will be a "big" thing and will also change the way we have to think about network/perimeter security.

In the next posts I will write about some of my findings. Stay tuned.


WCF
Wednesday, April 30, 2008 6:18:56 AM UTC  #   
 Monday, April 28, 2008

Using IdentityModel: Converting ADFS Security Properties to Claims

This little helper might be useful when you are working with ADFS, but want to use the IdentityModel types in your app:

public static ClaimSet ToClaimSet(this SingleSignOnIdentity identity)
{
    List<Claim> claims = new List<Claim>();

    claims.Add(new Claim(identity.NameType, identity.Name, Rights.Identity));

    foreach (SecurityProperty property in identity.SecurityPropertyCollection)
    {
        string claimType = property.Uri;
        if (claimType.EndsWith("NameValue"))
        {
            claimType = property.Name;
        }

        claims.Add(new Claim(claimType, property.Value, Rights.PossessProperty));
    }

    return new DefaultClaimSet(ClaimSet.System, claims);
}


IdentityModel
Monday, April 28, 2008 5:39:21 AM UTC  #   
 Tuesday, April 22, 2008

Ein Session Abstract ganz genau nach meinem Geschmack

Gesehen auf der JAX2008 Webseite:

Security Last – Sicherheitsentscheidungen spät treffen
Sicherheitsanforderungen wie Logins und Berechtigung sind wichtig – aber müssen diese wirklich gleich am Anfang umgesetzt werden? Das nachträgliche Hinzufügen dieser Anforderungen mit reinem Java und OOP ist sehr schwierig, weshalb dies meistens mit „Ja“ beantwortet wird. Erfahren Sie hier, wie mithilfe von Tools wie Spring Security, AspectJ und CAS auch spät in Anwendungen integriert werden kann.

Ohne Worte...

 


Microsoft Deutschland Security Portal
Tuesday, April 22, 2008 11:49:20 AM UTC  #   
 Sunday, April 20, 2008

Token Kidnapping

Interesting...and shocking.

Read more here:
http://www.argeniss.com/research/TokenKidnapping.pdf


For Your Favourites
Sunday, April 20, 2008 7:15:28 AM UTC  #   

Installing an IIS 7 Extension

Related to cleaning up my authentication module for Codeplex, I needed a way to (semi) automatically install a complete IIS extension (including schema, config sections and management extensions). I came up with a batch file that does the necessary steps (anybody out there that wants to write a real installer?).

1. Register all assemblies in the GAC
Usually an IIS 7 extension consists of at least three assemblies (module/handler, server extensions, client extensions). Gacutil.exe is your friend here (use the /if option). Also take into account, that IIS loads GACed assemblies domain neutral. That means that you have to recycle the worker process when you update e.g. your GACed module.

2. Register schema and config section
This involves copying your schema to the IIS' schema directory and add a <configSection> registration to applicationHost.config. Mike Volodarsky from the IIS team has written a nice tool call IisSchema that automates this step.

3. Registering the management extension
This involves adding the module to administration.config (in two different places). I haven't found an automated way of doing this, so I wrote a little tool to accomplish this task.

IisRegMgmt [install/uninstall] [assembly_to_register]

This will find all Microsoft.Web.Management.Server.ConfigurationModuleProvider derived classes in the specified assembly and register them in administration.config.

The code to do the registration is as follows:

private static void RegisterAdministration(string name, string type, string assembly)
{
    // get access to administration.config
    Configuration administration = _manager.GetAdministrationConfiguration();

    // get access to <moduleProviders>
    ConfigurationSection moduleProvidersSection =
        administration.GetSection("moduleProviders");
    ConfigurationElementCollection moduleProviders =
        moduleProvidersSection.GetCollection();

    // check for existing elements first
    Clean(name, moduleProviders);

    // create new element
    ConfigurationElement newModuleProvider =
        moduleProviders.CreateElement();

    // set attributes
    newModuleProvider.SetAttributeValue("name", name);
    newModuleProvider.SetAttributeValue("type", type + ", " + assembly);

    // add element
    moduleProviders.Add(newModuleProvider);


    // get access to <modules>
    ConfigurationSection modulesSection =
        administration.GetSection("modules");
    ConfigurationElementCollection modules = modulesSection.GetCollection();

    // check for existing element first
    Clean(name, modules);

    // create new element
    ConfigurationElement newModule = modules.CreateElement();

    // set attributes
    newModule.SetAttributeValue("name", name);

    // add element
    modules.Add(newModule);

    // save changes
    _manager.CommitChanges();
}

Don't forget to clean up the sections before you add the new module, otherwise you might end up with double entries:

private static void Clean(string name, ConfigurationElementCollection elements)
{
    var hits = from e in elements
               where (string)e.GetAttributeValue("name") == name
               select e;

    hits.ToList().ForEach(e => elements.Remove(e));
}

For completeness sake, here's how you can find the right management classes in the assembly (and because I was amused about 'Linq to Reflection' ;)

private static IEnumerable<string> GetManagementTypes(Assembly a)
{
    string baseType = "Microsoft.Web.Management.Server.ConfigurationModuleProvider";
   
    var types = from t in a.GetExportedTypes()
                where t.BaseType.FullName == baseType
                select t.FullName;

    return types;
}

 

IisRegMgmt01.zip (18.66 KB)

 


IIS
Sunday, April 20, 2008 6:50:57 AM UTC  #   
 Saturday, April 19, 2008

Custom Basic Authentication is now on Codeplex

I also moved my "Basic Authentication against non-Windows accounts for IIS" (phew) project to Codeplex

http://www.codeplex.com/CustomBasicAuth

Again, if you have feature ideas or want to contribute, contact me.

 


ASP.NET | IIS | WCF
Saturday, April 19, 2008 7:44:05 AM UTC  #   
 Wednesday, April 16, 2008

InfoCardSelector is now on Codeplex

I finally found a new home for my ASP.NET InfoCard control:

http://www.codeplex.com/InfoCardSelector

If you feel like contributing or suggesting new features, you can contact me via this page.


ASP.NET | CardSpace | IdentityModel | WCF
Wednesday, April 16, 2008 4:02:36 PM UTC  #   
 Saturday, April 12, 2008

Developing ASP.NET Applications in Medium Trust

Alex Smolen did a good write up of the various approaches for sandboxing ASP.NET applications. Recommended!


ASP.NET
Saturday, April 12, 2008 5:56:02 AM UTC  #   
 Thursday, April 03, 2008

Beware of Whitespaces in WAS Configuration

This took me a while to solve...

In the WAS configuration dialog there must be NO whitespaces in the enabled protocols configuration. Otherwise you will get this error:

"Could not find a base address that matches scheme net.tcp for the endpoint with binding NetTcpBinding."

 

HTH

 


IIS | WCF | Work in Progress
Thursday, April 03, 2008 8:04:24 AM UTC  #   
 Sunday, March 30, 2008

Using Information Cards in ASMX Web Services

As I wrote here - an Information Card token is just a string. This means that (with the help of some extra plumbing) you can seamlessly integrate cards into "legacy" technologies. Here's a sample walkthrough for ASMX web services.

To transmit the token to the service, I will use a SOAP header. So the first step is to define the header:

[XmlRoot(ElementName = "InformationCard",
    Namespace = "http://schemas.xmlsoap.org/ws/2005/05/identity")]
public class InfoCardTokenHeader : SoapHeader
{
    public string Token;
}

For metadata support, we can now annotate a web method with this header information:

[WebService(Namespace = "urn:leastprivilege")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class AsmxService : WebService
{
    public InfoCardTokenHeader InfoCardToken;

    [WebMethod]
    [SoapHeader("InfoCardToken", Direction = SoapHeaderDirection.In)]
    public string Ping()
    {
       …
    }
}

The client can now use the CardSpaceSelector API (or my wrapper) to get a token manually. Afterwards the token gets transmitted using the header:

static void Main(string[] args)
{
    AsmxService proxy = new AsmxService();

    InfoCardTokenHeader token = new InfoCardTokenHeader();
    token.Token = GetInfoCardToken(proxy.Url);
    proxy.InformationCard = token;

    Console.WriteLine(proxy.Ping());
}

static string GetInfoCardToken(string targetUri)
{
    IdentitySelector selector = new IdentitySelector();
    selector.TargetUri = new Uri(targetUri);
    selector.SetTargetCertificate(targetUri);

    selector.RequiredClaims.Add(ClaimTypes.GivenName);
    selector.RequiredClaims.Add(ClaimTypes.Surname);
    selector.RequiredClaims.Add(ClaimTypes.Email);

    GenericXmlSecurityToken token = selector.GetToken();
    return token.TokenXml.OuterXml;
}

On the server side you could now retrieve the token from the header and use your favourite token decryption class to extract the claims. If you want to put in a little bit more work, you can improve the integration of that information using a SoapExtension.

The extension will check the incoming headers, extract the token and set Thread.CurrentPrincipal and Context.User to an instance of IdentityPrincipal that wraps the token generated authorization context. A corresponding extension attribute connects this logic with the web method:

[WebMethod]
[InfoCardSoapExtension(TokenRequired = true)]   
[SoapHeader("InfoCardToken", Direction = SoapHeaderDirection.In)]
public string Ping()
{
    return IdentityPrincipal.Current.ClaimSets.FindClaim(
        ClaimTypes.GivenName).Get<string>();
}

This gives the web service method seamless access to incoming claims.

 

The code for the SOAP extension is quite simple (the configuration code is omitted):

public class InfoCardSoapExtension : SoapExtension
{
  public override void ProcessMessage(SoapMessage message)
  {
    if (message.Stage == SoapMessageStage.AfterDeserialize)
    {
      foreach (SoapHeader header in message.Headers)
      {
        InfoCardTokenHeader tokenHeader =
          header as InfoCardTokenHeader;

        if (tokenHeader != null)
        {
 
        IdentityPrincipal principal;

          try
          {
            var token = new Token(tokenHeader.Token, true);
            principal = new IdentityPrincipal(token.AuthorizationContext);
          }
          catch
          {
            throw new HttpException(500, "Token validation failed");
          }

          HttpContext.Current.User = Thread.CurrentPrincipal = principal;
          return;
        }
      }

      if (_tokenRequired)
      {
        throw new HttpException(401, "Authentication required");
      }
    }
  }
}

Disclaimer: I know that this code could be written far more generic. Consider this as a proof of concept.

 

The LeastPrivilege.IdentityModel download contains the complete sample. Have fun.


ASP.NET | CardSpace | IdentityModel
Sunday, March 30, 2008 8:49:07 AM UTC  #   
 Saturday, March 22, 2008

Using IdentityModel: Some Samples

Here are some typical usage scenario of IdentityPrincipal in ASP.NET.

Simple IsInRole calls (checks for a status claim with a value of 'Gold'):

HttpContext.Current.User.IsInRole("Gold");

 

Retrieving the OrderHistory claim:

IdentityPrincipal ip = IdentityPrincipal.Current;
Claim orderHistory = ip.ClaimSets.FindClaim(
  Constants.OrderHistoryClaimType,
  Constants.ApplicationIssuerIdentityClaim);

var orders = orderHistory.Get<List<OrderDetail>>();

 

..or some authorization code from my CardSpace sample app - thanks to the unified authorization model, I can share this method across ASP.NET, ASMX and WCF:

public static IEnumerable<MessageBoard> GetBoardsForUser(
  AuthorizationContext context, bool includePublic)
{
    List<MessageBoard> boards = new List<MessageBoard>();

    foreach (Claim typeClaim in context.ClaimSets.FindClaims(
      AppClaims.UserTypeClaim,
      new ApplicationIssuerClaimSet()))
    {
        string type = typeClaim.Get<string>();
        if ("Public".Equals(type) && includePublic == false) continue;

        boards.AddRange(GetBoards(type));
    }

    return boards;
}

I have updated the source download here to include a console, WCF and ASP.NET test app that share the same authorization model.


ASP.NET | CardSpace | IdentityModel | WCF
Saturday, March 22, 2008 1:54:39 PM UTC  #   
 Friday, March 21, 2008

LINQ to SQL and Security

I don't know how many SQL injection demos I did in my life - and it is still surprising (or shocking rather) how many people don't know about this. It is even more surprising how many people fight for their string concat ad hoc queries and come up with all kind of dodgy excuses why it makes sense to use them.

What is wrong with SQL parameters? ;)

Recently I played around with LINQ to SQL and think it is a compelling (and time saving) way to do database interactions. The thing I really like about LINQ to SQL is that it does the right thing by default (security wise) when it comes to parameters.

Consider this query:

var products = from p in context.products
               where p.description.StartsWith(_txtSearch.Text)
               select new
               {
                   p.description,
                   p.price,
                   p.stock
               };

This gets turned into the following SQL (for a search value of 'sony'):

exec sp_executesql N'SELECT [t0].[description], [t0].[price], [t0].[stock]
FROM [dbo].[products] AS [t0]
WHERE [t0].[description] LIKE @p0',N'@p0 varchar(5)',@p0='sony%'

A (malicious) search value with an appended or 1=1 where clause would look like this:

exec sp_executesql N'SELECT [t0].[description], [t0].[price], [t0].[stock]
FROM [dbo].[products] AS [t0]
WHERE [t0].[description] LIKE @p0',N'@p0 varchar(16)',@p0='sony'' or 1=1 --%'

Which will not yield any results.

So if you really like ad hoc SQL - but for some reason are too lazy to work with parameters, LINQ is a compelling alternative (again purely from a security view).

Usually the databases I design (and how I recommend it to my customers), don't allow direct table access. All access is done via stored procedures. This is where LINQ to SQL comes in really handy for me - basically as a nice Sproc to C# code generator. Calling a GetData stored procedure and subsequent binding to a grid looks like this:

protected void _btnSearch_Click(object sender, EventArgs e)
{
    using (ComputerstoreDataContext context = new ComputerstoreDataContext())
    {
        var products = context.GetProducts(_txtSearch.Text);

        _gridProducts.DataSource = products;
        _gridProducts.DataBind();
    }
}

Which will result in this SQL:

declare @p4 int
set @p4=0
exec sp_executesql N'EXEC @RETURN_VALUE = [dbo].[GetProducts] @Search = @p0',N'@p0 nvarchar(4),@RETURN_VALUE int output',@p0=N'sony',@RETURN_VALUE=@p4 output
select @p4

This works for me.

Disclaimer: There's quite some controversy about LINQ to SQL and I am not a database expert enough to say if LINQ is good or bad. They way I use it (for Sprocs only) it seems to be fine. Again from a security point of view it does the right thing by default. And that's what I mostly care about ;)


Work in Progress
Friday, March 21, 2008 1:16:11 PM UTC  #   
 Thursday, March 20, 2008

Using IdentityModel: Adding ASP.NET Support Part 2 (Claims Manager)

The last step for integrating claims into ASP.NET is to write a module that loads authorization policies, creates an AuthorizationContext and persists that on Context.User/Thread.CurrentPrincipal.

My module has this simple configuration section:

<claimsManager enabled="true"
               addAuthenticationClaims="true"
               roleClaimType="urn:leastprivilege/claims/customers/status">
  <authorizationPolicies>
    <policy type="LeastPrivilege.CustomerIdAuthorizationPolicy, App_Code" />
    <policy type="LeastPrivilege.CustomerAuthorizationPolicy, App_Code" />
  </authorizationPolicies>
</claimsManager>

If addAuthenticationClaims is set to true, the policy that transforms authentication details to claims (see my last post) will be loaded before all the external policies. The roleClaimType attribute specifies the claim type that should be used for the IsInRole implementation of IdentityPrincipal. The authorizationPolicies collection specifies the claims transformation policies that should run.

The module itself subscribes to PostAuthenticateRequest, loads the policies and populates Context.User/Thread.CurrentPrincipal with the IdentityPrincipal (which in turn wraps the AuthorizationContext).

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

    public void Init(HttpApplication context)
    {
        context.PostAuthenticateRequest += OnPostAuthenticateRequest;
    }

    private void OnPostAuthenticateRequest(object sender, EventArgs e)
    {
        // this code makes only sense when the user is authenticated
        if (!(HttpContext.Current.Request.IsAuthenticated))
        {
            return;
        }

        // make sure the module is enabled
        if (!Configuration.Enabled)
        {
            return;
        }

        HttpApplication app = sender as HttpApplication;
        HttpContext context = app.Context;

        IList<IAuthorizationPolicy> policies = ClaimsManagerModule.Policies;

        if (policies.Count != 0)
        {
            AuthorizationContext authContext =
              CreateAuthorizationContext(policies);

            IdentityPrincipal ip = new IdentityPrincipal(
              context.User.Identity, authContext);

            context.User = Thread.CurrentPrincipal = ip;
        }
    }

    private static AuthorizationContext CreateAuthorizationContext(
      IList<IAuthorizationPolicy> policies)
    {
        AuthorizationContext authContext =
            AuthorizationContext.CreateDefaultAuthorizationContext(policies);

        return authContext;
    }

    public static IList<IAuthorizationPolicy> Policies
    {
        get
        {
            List<IAuthorizationPolicy> policies = new
              List<IAuthorizationPolicy>();

            if (Configuration.AddAuthenticationClaims)
            {
                policies.Add(new AspNetAuthenticationPolicy());
            }

            foreach (PolicyType policy in
              Configuration.AuthorizationPolicies)
            {
                policies.Add(CreatePolicy(policy.Type));
            }

            return policies;
        }
    }

    private static IAuthorizationPolicy CreatePolicy(string typename)
    {
        Type type = Type.GetType(typename, true);

        if (!typeof(IAuthorizationPolicy).IsAssignableFrom(type))
        {
            throw new ConfigurationErrorsException(
              "Policy does not implement IAuthorizationPolicy");
        }

        return (IAuthorizationPolicy)Activator.CreateInstance(type);

    }

    public static ClaimsManagerSection Configuration
    {
        get
        {
            return (ClaimsManagerSection)
              WebConfigurationManager.GetSection("claimsManager");
        }
    }
}

 

Don't consider this as production ready code. There are things missing like error handling and caching. But this should get you started. With the next post I will show some usage scenarios and will also update the source download.

HTH


ASP.NET | IdentityModel
Thursday, March 20, 2008 6:54:32 AM UTC  #   
 Wednesday, March 19, 2008

Using IdentityModel: Adding ASP.NET Support Part 1 (Authentication based Claims)

Adding claims support to ASP.NET is a perfect candidate for an HTTP module. As a reminiscence to RoleManager, I called mine ClaimsManager. The job of the claims manager is this:

  • Creating claims based on the technical authentication details (Windows, Forms, client certificates etc.)
  • Invoking external claims transformation policies which then build the app specific claims based on the technical ones
  • Making the AuthorizationContext available to pages (via Thread.CurrentPrincipal/Context.User - see my previous post)

The first step is to write an IAuthorizationPolicy to map the ASP.NET authentication details to claims. This is done by inspecting Context.User.Identity and client certificates - if you use a custom identity, you would amend that code (check my previous posts about authorization policies):

// policy that adds ASP.NET authentication type specific claims
public class AspNetAuthenticationPolicy : IAuthorizationPolicy
{
    public bool Evaluate(
      EvaluationContext evaluationContext, ref object state)
    {
        HttpContext context = HttpContext.Current;
        List<ClaimSet> claimSets = new List<ClaimSet>();

        // Windows or Forms authentication
        if (context.User.Identity is WindowsIdentity)
        {
            claimSets.Add(
              new WindowsClaimSet(context.User.Identity as WindowsIdentity));
        }
        else if (context.User.Identity is FormsIdentity ||
                 context.User.Identity is GenericIdentity)
        {
            claimSets.Add(new UserNameClaimSet(context.User.Identity.Name))
        }

        // client certificate
        if (context.Request.ClientCertificate.IsPresent)
        {
            X509Certificate2 certificate = new X509Certificate2(
                context.Request.ClientCertificate.Certificate);

            claimSets.Add(new X509CertificateClaimSet(certificate));
        }

        claimSets.ForEach(set => evaluationContext.AddClaimSet(this, set));
        return true;
    }

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

    public string Id
    {
        get
        {
          return
            "LeastPrivilege.IdentityModel.Web.AspNetAuthorizationPolicy";
        }  
    }
}

 

In the next post I will show the HTTP module, how it calls the above policy and how it chains in external policies.


ASP.NET | IdentityModel
Wednesday, March 19, 2008 7:23:49 AM UTC  #   

Using IdentityModel: IdentityPrincipal

Since V1 of .NET there is a "slot" to store authorization information about the current user: Thread.CurrentPrincipal. This data gets propagated to newly created threads and is deeply integrated into other application frameworks like ASP.NET.

To integrate claims into ASP.NET it makes sense to re-use this infrastructure.

To accomplish this the AuthorizationContext has to be wrapped by an IPrincipal implementation. I called mine IdentityPrincipal (which is part of LeastPrivilege.IdentityModel). I won't show you the source code here because this is not very exciting - but there are some things to note:

  • You can either new up the class by supplying a concrete AuthorizationContext or a list of claim sets.
  • If none of the above are specified, the class will try to use ServiceSecurityContext (for WCF support).
  • You can pass in one of the built-in IIdentity implementations (like WindowsIdentity, FormsIdentity etc). If you don't, the identity will be anoynmous (and it is assumed that you don't care about it).
  • I also created a ClaimsIdentity which simply wraps a claim and uses the resource as the Name property.
  • It features a static IdentityPrincipal.Current property which grabs the IPrincipal from Thread.CurrentPrincipal and tries to cast it to IdentityPrincipal.
  • The IsInRole implementation searches for a configurable claim type in the claim sets. Then the resource gets checked against the supplied "role name". This gives you compatibility for existing role based authorization code like ASP.NET UrlAuthorization (for simple scenarios).

The typical pattern would be:

  • Create an AuthorizationContext (or just one or more claim sets).
  • New up an IdentityPrincipal and put it on Thread.CurrentPrincipal (or Context.User for ASP.NET)
  • Call Thread.CurrentPrincipal.IsInRole (or Context.User.IsInRole) for simple checks
  • Call IdentityPrincipal.Current to get access to the claim sets and use the previously described extension methods for more advanced claims checking.

I updated the source download here to include the IdentityPrincipal as well as a console and WCF test app.

Have fun.


ASP.NET | IdentityModel | WCF
Wednesday, March 19, 2008 12:22:10 AM UTC  #   
 Tuesday, March 18, 2008

General Failure when pinging the local Machine Name

I have this strange problem that when I ping my local machine name, I get a General Failure. Forcing the ping to IPv4 with the /4 option works. I could live with that, but another side effect is that opening a http connection to my local IIS takes around 5 seconds to resolve the name. This is not acceptable.

It is even more strange that this problems stops as soon as I am on cabled network.

So this has something to do with IPv6 and Teredo and due to lack of time for finding the real reason, disabling Teredo worked for me.

netsh int teredo set state disabled

HTH


Misc
Tuesday, March 18, 2008 7:46:15 PM UTC  #   

Using IdentityModel: Adding Claims Support to ASP.NET (Spoiler)

Many people asked me how to use claims based authorization in ASP.NET. While I have it working here on my machine (hey - it works on my machine!), I still need to polish the bits before I can release them. For those who want to get their hands dirty immediately - here's what you'd have to do:

  • Write an IPrincipal implementation that wraps an AuthorizationContext
  • Write an HTTP module that creates the AuthorizationContext, wraps it in the IPrincipal and puts it on Thread.CurrentPrincipal and Context.User.
  • If you need compatibility with the existing URL authorization infrastructure, define a role claim type that should be checked by IPrincipal.IsInRole
  • For more functionality, pages would grab the principal from Context.User and retrieve the AuthorizationContext. From there you can get to the claim sets to use the previously presented APIs to do authorization.

There are some additional nice to have features to implement like a permission and a corresponding attribute as well as a configurable claim type for IsInRole.

Bear with me.


ASP.NET | IdentityModel
Tuesday, March 18, 2008 7:23:35 AM UTC  #   
 Sunday, March 16, 2008

Troopers08

My good friends at ERNW are organizing a fantastic security conference to take place in Munich in April. There are two tracks appropriately named "attack" and "defense" and interesting speakers like Dan Bernstein, Andrew Cushman, Enno Rey, Michael Thumann and more guarantee for high profile content. Check it out!

http://www.troopers08.org


Conferences | For Your Favourites
Sunday, March 16, 2008 8:56:42 AM UTC  #   
 Tuesday, March 11, 2008

STS? Available!

In my Post STS? Coming Soon! I linked to information about the upcoming framework for writing STSes (and more) from Microsoft. Unfortunately this is not yet available.

Along with Barry and David I am very happy to announce SharpSTS - a C# library for developing Information Card security token services.

You can find the source code on Codeplex and a demo website here. Don't expect a "click setup.exe and everything is fine" installation experience. There is still a lot of work to do. But you can get the complete source code along with installation instructions to play around. If you want to contribute or have feature ideas, leave me a comment.

Have fun!

 


CardSpace | IdentityModel | WCF
Tuesday, March 11, 2008 6:32:45 AM UTC  #   
 Tuesday, March 04, 2008

Using IdentityModel: Authorization Context and Claims Transformation outside of WCF

Here I showed you how to transform authentication specific claims to general application claims. The same model can be also used outside of WCF. I will use a client application here as an example and save ASP.NET for a later post (as this requires some extra work).

Typically, in client applications you don't need all these layers of indirection and flexibility since you will have (most likely) a single authentication type. But to prove the point that you can reuse your existing policies - here's what you would do:

  • Create a claim set for your "technical" user, e.g. from a username/password dialog, the Windows token or an inserted SmartCard
  • Map the "technical" identifier to an application identifier
  • Create the customer claims

It turns out that the only thing that is different to the WCF scenario is the initial "technical" claim set. The rest of the code stays the same.

You can create the authorization context by calling CreateDefaultAuthorizationContext on the AuthorizationContext class (and passing in the three policies described above). From that point on you can use the same authorization code as you would write in WCF.

The context creation code could look like this:

private static AuthorizationContext CreateContext()
{
    WindowsIdentity identity = WindowsIdentity.GetCurrent();

    AuthorizationContext context =
      AuthorizationContext.CreateDefaultAuthorizationContext(
        new List<IAuthorizationPolicy>()
        {
            // claim set based on Windows authentication
            new WindowsUserAuthorizationPolicy(identity),

            // mapping of authentication to application id
            new CustomerIdAuthorizationPolicy(),
           
            // application claim set
            new CustomerAuthorizationPolicy()
        });

    return context;
}

The only new code here is the policy for Windows authentication. This one is very simple:

class WindowsUserAuthorizationPolicy : IAuthorizationPolicy
{
    WindowsIdentity _identity;

    public WindowsUserAuthorizationPolicy(WindowsIdentity identity)
    {
        _identity = identity;
    }

    public bool Evaluate(EvaluationContext evaluationContext, ref object state)
    {
        evaluationContext.AddClaimSet(this, new WindowsClaimSet(_identity));

        return true;
    }

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

    public string Id
    {
        get { return "WindowsUserAuthorizationPolicy"; }
    }
}

Now you have an AuthorizationContext. But there are some small things left to do. How do you make that context available to the rest of your app? How can you propagate this information across across thread switches. This brings us back to an old friend called Thead.CurrentPrincipal. Stay tuned.


IdentityModel
Tuesday, March 04, 2008 9:43:49 AM UTC  #   

Using IdentityModel: Simplifying Calculation of Information Card Unique IDs in WCF

The key to information card backed systems is calculating a stable unique identifier for your users based on the card claims. Typically this is done by hashing the issuer public key (plus some other information like a PPID for managed cards).

This involves the following steps:

  • Find the issuer claim set containing the issuer RSA key
  • Optionally find some other claims (e.g. a PPID)
  • Create a hash from that pieces of information

In LeastPrivilege.IdentityModel I added some helper extension methods to simplify this process. The first one extends the RSACryptoServiceProvider class and allows calculating the hash the from the key (plus optional extra information):

public static class RSAExtensions
{
    public static byte[] GetKeyHash(this RSACryptoServiceProvider rsa)
    {
        return rsa.GetKeyHash(string.Empty);
    }

    public static byte[] GetKeyHash(this RSACryptoServiceProvider rsa, string entropy)
    {
        int entropyLength = Encoding.UTF8.GetByteCount(entropy);
        RSAParameters rsaParams = rsa.ExportParameters(false);
        byte[] shaInput;
        byte[] shaOutput;

        int i = 0;
        shaInput = new byte[rsaParams.Modulus.Length +
          rsaParams.Exponent.Length + entropyLength];
        rsaParams.Modulus.CopyTo(shaInput, i);
        i += rsaParams.Modulus.Length;
        rsaParams.Exponent.CopyTo(shaInput, i);
        i += rsaParams.Exponent.Length;
        i += Encoding.UTF8.GetBytes(entropy, 0, entropy.Length, shaInput, i);

        using (SHA256 sha = SHA256.Create())
        {
            shaOutput = sha.ComputeHash(shaInput);
        }

        return shaOutput;
    }
}

 

I use the same algorithm here as in Microsoft's Token class for ASP.NET. This makes it easy to share the IDs between ASP.NET and other application types.

Another helper extends IEnumerable<ClaimSet> and simplifies retrieving the RSA claim:

public static RSACryptoServiceProvider FindIssuerRsaClaim(
  this IEnumerable<ClaimSet> claimSets)
{
    return claimSets.FindClaim(
        ClaimTypes.Rsa,
        ClaimSearchMode.Issuer).Get<RSACryptoServiceProvider>();
}

 

With these helpers you can retrieve the unique ID by using this code:

AuthorizationContext context =
    ServiceSecurityContext.Current.AuthorizationContext;
byte[] uniqueId = context.ClaimSets.FindIssuerRsaClaim().GetKeyHash();


IdentityModel | WCF
Tuesday, March 04, 2008 12:26:35 AM UTC  #   

Using IdentityModel: Claims Transformation in WCF

In the previous post I talked about claims transformation. Two authorization policies are necessary for the scenario I described. The first one maps the "technical" identity to an application identity and the second one creates application specific claims based on the application identity.

Mapping the identity
The first authorization policy grabs the first identity claim from the evaluation context and passes that to some mapping logic (omitted):

class CustomerIdAuthorizationPolicy : IAuthorizationPolicy
{
    public bool Evaluate(EvaluationContext evaluationContext, ref object state)
    {
        string userId;
        Claim id = evaluationContext.ClaimSets.FindIdentityClaim();

        userId = Map(id);
        evaluationContext.AddClaimSet(this, new CustomerIdClaimSet(userId));
       
        return true;
    }

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

    public string Id
    {
        get { return "CustomerIdAuthorizationPolicy"; }
    }
}

The mapping logic then checks the claim type and returns the user id from some data store.

Adding the customer claims
The second policy then checks for the customer id claim and adds the necessary information to the evaluation context:

class CustomerAuthorizationPolicy : IAuthorizationPolicy
{
    public bool Evaluate(EvaluationContext evaluationContext, ref object state)
    {
        string userId = evaluationContext.ClaimSets.FindClaim(
            Constants.CustomerIdClaimType,
            Constants.ApplicationIssuerIdentityClaim).Get<string>();

        evaluationContext.AddClaimSet(this, new CustomerClaimSet(userId));
        return true;
    }

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

    public string Id
    {
        get { return "CustomerAuthorizationPolicy"; }
    }
}

 

The last step is to to add the policies to the serviceAuthorization behavior in the right order:

<serviceAuthorization>
  <
authorizationPolicies>
    <
add policyType="LeastPrivilege.CustomerIdAuthorizationPolicy, Service" />
    <
add policyType="LeastPrivilege.CustomerAuthorizationPolicy, Service" />
  </
authorizationPolicies>
</
serviceAuthorization>

 

In the next post I will show you how to accomplish the same thing outside of WCF.


IdentityModel | WCF
Tuesday, March 04, 2008 12:04:18 AM UTC  #   
 Friday, February 29, 2008

Using IdentityModel: Authorization Policies, Context and Claims Transformation

In the previous posts I talked about claims and claim sets - but where do claim sets come from? The answer is easy - from authorization policies ;) OK - let's have a closer look.

The "container" for claim sets is a called the authorization context (defined in System.IdentityModel.Policy).

The authorization context gets created by calling CreateDefaultAuthorizationContext on the AuthorizationContext class. The input for this method is a list of objects that implement the IAuthorizationPolicy interface. This interface defines a method called Evaluate. Inside of Evaluate you create the claim sets you need and then call the AddClaimSet method on the EvaluationContext that gets passed into Evaluate. So much for the facts. How do we bring that all together?

Typically you want to distinguish between two kinds of authorization policies (and thus claim sets) in your system - I like to call them internal and external authorization policies.

An internal authorization policy is very close to the implementation details of the authentication process. WCF e.g. creates them based on the configured client credential type. These policies in turn create claim sets based on the incoming credential (Windows, certificates, usernames, SAML tokens).

External authorization policies do the application specific transformation of claims. They should not rely on any environment specific properties like ServiceSecurityContext (WCF), Context.User (ASP.NET) or Thread.CurrentPrincipal (whatever) but instead use solely the claims created by the internal policies to figure out all necessary information about the entity. This way external authorization policies can be used environment independent and can be shared between services or applications.

Let's do an example. Imagine a WCF service with multiple credential types. Regardless of the authentication type, you want to end up with a customer claim set that holds the necessary authorization information for your service. That could be done like this:

  1. WCF creates a claim set based on the incoming credential type, e.g. a WindowsClaimSet, a X509CertificateClaimSet or a claim set that holds assertions from an incoming SAML token.
  2. You create a policy that parses the WCF created claim set and transform the "technical" user identifier (e.g. Windows account name, X509 thumbprint, username) to an identifier that makes sense in your application. This identifier goes into a new claim set that gets added to the evaluation context.
  3. You create another policy that parses the application identifier from the previously added claim set and retrieves all necessary authorization information. Based on that you create a customer claim set and add it to the evaluation context.
  4. The business logic retrieves the information from the customer claim set and does authorization decisions.

If you follow this pattern you would only have to adjust the logic of the user id generation to your specific environment. The customer claim set creation policy is now easily re-usable.

In the next post I will show you some code ;)


IdentityModel | WCF
Friday, February 29, 2008 10:40:39 AM UTC  #   

New IIS7 Resources

HTH


IIS
Friday, February 29, 2008 8:35:41 AM UTC  #   
 Tuesday, February 26, 2008

Using IdentityModel: Creating Custom Claim Sets

As part of LeastPrivilege.IdentityModel I wrote a claim set derived class that you can use to create your own custom claim sets. The DeferredLoadClaimSet lazily loads the claims (which potentially involves roundtrips to data stores) on demand when the claims are accessed for the first time.

You simply derive from DeferredLoadClaimSet and implement the LoadClaims method like this:

class CustomerClaimSet : DeferredLoadClaimSet
{
    string _customerName;

    public CustomerClaimSet(string customerName)
    {
        _customerName = customerName;
    }

    protected override void LoadClaims(out ClaimSet issuer, out IList<Claim> claims)
    {
        claims = CreateClaims();
        issuer = new ApplicationIssuerClaimSet(Constants.ApplicationIssuerName);
    }
}

 

The ApplicationIssuerClaimSet also derives from DeferredLoadClaimSet and can be used for custom issuers.

public class ApplicationIssuerClaimSet : DeferredLoadClaimSet
{
    string _issuerName = "Application Issuer";

    public ApplicationIssuerClaimSet()
    { }

    public ApplicationIssuerClaimSet(string issuerName)
    {
        _issuerName = issuerName;
    }

    protected override void LoadClaims(out ClaimSet issuer, out IList<Claim> claims)
    {
        claims = new List<Claim>()
        {
            new Claim(ClaimTypes.System, _issuerName, Rights.Identity),
            new Claim(ClaimTypes.System, _issuerName, Rights.PossessProperty)
        };

        issuer = this;
    }
}

 

HTH


IdentityModel
Tuesday, February 26, 2008 9:40:02 AM UTC  #   
 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  #