Wednesday, January 16, 2008

Simplified Impersonation Model

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

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

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

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

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

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

Most popular Content in 2007

Just looked through my server logs...

Top Downloads:

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

 

Top Documents:

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

 

Now back to serious work....;)


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

HTTP Basic Authentication against Non-Windows Accounts in IIS7

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

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

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

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

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

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

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

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

 

Download the full source here.

 


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        return true;
    }

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

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

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

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

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

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

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

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

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

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

        return true;
    }

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

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

 

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

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

 


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

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

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

IIS

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

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


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

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

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

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

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

The following configuration section reflects these options:

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

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

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

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

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

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

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

The corresponding code looks like this:

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

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

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

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

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

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

 

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

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

 

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

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

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

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

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

            return true;
        }
    }

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

 

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

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

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

The two methods IsCredentialCached and CacheCredential implement this logic:

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

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

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

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

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

    return cacheHit;
}

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

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

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

    return _cachePrefix + Convert.ToBase64String(identifierHash);
}

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

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

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

 

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


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

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

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

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

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

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

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

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

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

Some notes

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

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


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

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

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

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

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

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

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

Stay tuned!


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

System.AddIn Pipeline Builder

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

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

http://www.codeplex.com/clraddins


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

System.DirectoryServices.AccountManagement

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

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

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

Verifying the password of a user:

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

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

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

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

    return user.IsMemberOf(group);
}

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


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

Configuration Section Designer

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

Try this. Very nice.


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

Walkthrough: Setting up a self hosted WCF Service with SSL

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

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

 

1. Create and install an SSL certificate

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

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

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

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

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

2. Map the SSL cert to the WCF listening port

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

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

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

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

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

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

 

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

 

3. Set an URL ACL for the listening URI

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

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

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

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

 

4. Configure the WCF service

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

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

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

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

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

 

HTH


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

The Future of the ASP.NET InfoCardSelector Control

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

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

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


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

Demo Site for InfoCardSelector ASP.NET Control

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

Have fun!

http://www.leastprivilege.com/InfoCardSelector


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

Updated InfoCardSelector ASP.NET Control for No-SSL Scenarios

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

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

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

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

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

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

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

(download)

 


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

Least Privilege, WCF and Named Pipes

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

 

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


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

Extension Methods for AntiXss

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

The following extension methods wrap the AntiXss library:

public static class Extensions

{

    public static string UrlEncode(this string input)

    {

        return AntiXss.UrlEncode(input);

    }

 

    public static string HtmlEncode(this string input)

    {

        return AntiXss.HtmlEncode(input);

    }

 

    // rest omitted

}

This allows doing something like this:

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

Response.Write(input.HtmlEncode());

AntiXssExtensions.zip (17.19 KB)

 


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

Poor Man's File Replication using Robocopy

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

Example:

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


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

WCF Usernames over Transport and IIS Hosting

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

The problem is this:

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

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

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

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


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

Small change to WCF ServiceAuthorizationManager in 3.5

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

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


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

Authorizing Access to WCF Metadata

This post may be interesting for you because you eiter

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

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

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

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


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

Securing WCF Metadata

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

WCF Metadata Architecture Overview

 

Base Address

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

This option only allows to retrieve metadata via HTTP.

How to: Secure WCF Metadata Endpoints

 

MEX endpoint

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

<endpoint address="mex"

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

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

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

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

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

Secure MEX Endpoints

Retrieving Metadata

 

Metadata via WMI

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

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

<diagnostics wmiProviderEnabled="true" />

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

private static void DumpAllMetadata()

{

    ManagementClass mc =

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

 

    ManagementObjectCollection mos = mc.GetInstances();

 

    foreach (ManagementObject mo in mos)

    {

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

 

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

        {

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

            Console.ReadLine();

            Console.WriteLine(s);

        }

    }

}

 


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

STS? Coming soon!

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

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

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


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

WCF and SecurityAccessDeniedException

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

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

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

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

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

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

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


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

Finally! Usernames over Transport Authentication in WCF

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

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

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

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

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

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

How does that work?

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

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

b) write a username/password validator

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

c) hook up the validator in a serviceCredentials behavior

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

d) send a username/password using the client proxy

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

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

((IClientChannel)proxy).Close();

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

Does Microsoft regret the Security Push?

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

Not sure what to think about that.

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

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

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

What are your feelings about that?


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

Try XSSDetect

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

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

 


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

ValidateRequest does not mitigate XSS completely

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

 


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

.net@movies Episode 1 - Web Security

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

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

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

Das ganze wird durch ein nettes Abendprogramm abgerundet ;)

Die Details findet Ihr hier.

Vielleicht sieht man sich ja!

 


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

Sichere Software mit Microsoft .NET entwickeln

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

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

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

Zuschlagen!

 


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

TechEd:Developer 2007 Security Track

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

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

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

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

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

 


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

Details of CardSpace RP Identity Generation

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

Just search for "OrgIdBytes".

 


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

CardSpace in 3.5 doesn't require SSL

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

 


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

Certificate based Authentication and WCF (Mode independent)

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

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

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

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

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

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

    return IsTrusted(thumbprints, trustedThumbprints);
}

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

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

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

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

CertAM.zip (51.86 KB)

 


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

Certificate based Authentication and WCF (Transport Security)

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

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

When self hosting, you have to do the following:

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

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


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

Good Article on System.IdentityModel

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

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

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

Have fun!

 


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

Certificate based Authentication and WCF (Message Security)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

CertValidator.zip (16.98 KB)

 


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

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

Read more here and here. finally.

 


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

Certificate based Authentication and WCF

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

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

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

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

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

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

When to use which mode?

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

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

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

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

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


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

Guidance on User and Password Management

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

 


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

Extend Your WCF Services Beyond HTTP With WAS

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

 


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

Custom Principals and WCF

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

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

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

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

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

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

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

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

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

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

    return identities[0];
}

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

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

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

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

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

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

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

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

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

WcfCustomPrincipal.zip (24.83 KB)

 


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

InfoCardSelector for ASP.NET V1.0

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

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

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

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

InfoCardSelectorV1.zip (100.47 KB)

 


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

Excellent ASP.NET Information

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

Recommended!

 


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

Windows Integrity Mechanism

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

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


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

Windows Service Hardening

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

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

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

sc showsid FooService

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

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

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

Nice least privilege work, guys!

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


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

New WCF Security Samples

Justin wrote me an email this morning:

"Dominick,

I hope you are well.

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

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

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

Thanks for getting me into trouble, Justin ;)

 


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

WCF Security Webcast

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

Der ist jetzt downloadbar. Viel Spass!

 


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

Hit by Identity 1.0

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

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

Identity 2.0, where art thou?


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

Re:MVP

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

Thanks Microsoft!

 


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

Information Card Kits for ASP.NET and HTML

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

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

 


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

Feature Complete Version of the ASP.NET CardSpace Control

UPDATE: new version available here.

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

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

Download here. Have fun!

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

Features

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

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

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

</lp:CardSpaceSelector>

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

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

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

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

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

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

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

 

Properties

InfoCard setup

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

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

TokenType
Specifies the token type. Defaults to SAML 1.0.

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

Image

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

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

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

Rendering

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

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

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

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

Misc

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

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

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

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

 

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

Recorded Sessions from IMTC

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

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

Have fun!

 


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

MSDN Security Code Clips

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

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

 


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

MSDN UK CardSpace Nuggets

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

Also find the sample code I used here.

 


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

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

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

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

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

The tag could look like this:

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

CardSpaceSelectorV2.2.zip (51.76 KB)

 


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

Another Update to the CardSpace Control for ASP.NET

Update: updated version here.

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

The tag could look like this:

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

And the corresponding code:

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

Have fun!

CardSpaceSelectorV2.1.zip (48.28 KB)

 


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

Getting CardSpace Tokens Programmatically

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

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

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

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

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

IdentitySelector selector = new IdentitySelector();

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

IdentitySelectorWrapper.zip (13.29 KB)

 

 


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

Updated CardSpace Control for ASP.NET

Update: updated version here.

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

A typical tag could look like this:

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

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

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

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

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

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

CardSpaceSelectorV2.zip (51.13 KB)

 


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

Online Vorlesungen zu Betriebssystem Architektur

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

Mehr Infos bei Sebastian.

 


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

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

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

If you are around say Hi!

 


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

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

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

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

A CardSpace STS currently supports four credential types

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

Each authentication type has its use.

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

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

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

<quote>

5.4. Self-issued Token Credential

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

Syntax:

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

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

.../ic:SelfIssuedCredential

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

.../ic:SelfIssuedCredential/ic:PrivatePersonalIdentifier

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

</quote>

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

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

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

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

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

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

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


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

Live Alerts

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

Click here to subscribe:

Windows Live Alerts

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

 


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

Primary Keys in Identity

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

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

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


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

Orcas, WF, WCF and CardSpace

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


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

UAC Manifest Support in "Orcas"

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

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

 

 


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

Token Decryption Service for CardSpace

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

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

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

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

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

TDSv1.zip (167.91 KB)

 


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

UAC Demo and Helpers

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

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

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

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

UAC_Demo.zip (151.69 KB)

 


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

TechDays IIS7 Talk

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

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

 


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

CardSpace, PPIDs and UniqueIDs – my Conclusion

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

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

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

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

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

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

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

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

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

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

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

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

CardSpaceUniqueIdDemo.zip (25.08 KB)

 


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

InfoCards and Identity Stability

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

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

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

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

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

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

You can read more details about that here.

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

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


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

Wrap-up of HTTP.SYS Discussion

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

Kenny commented on my post:

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

Keith then posted this:

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

and:

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

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

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

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

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

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

Kenny also asked:

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

and Keith replied:

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

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

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

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

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

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

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

So to sum up:

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

Make up your mind yourself.


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

UAC is for Developers

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

 

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

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

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

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

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

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

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

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

A manifest is a resource file that looks like this:

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

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

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

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

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

rc.exe manifest.rc

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

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

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

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

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

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

  Process.Start(startInfo);
}

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

 


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

Making my Visual Studio better

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

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

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

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


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

WCF Performance Comparison

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

The following section made me wonder (quoting):

"3.3.2 Self-Hosted Secure Request/Reply TCP Application

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

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

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

 


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

WCF Faults and Information Disclosure

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

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

Server stack trace:

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

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

This is (unnecessary) information disclosure.


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

Decrypting CardSpace Tokens in Partial Trust

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

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

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

Works fine. (download)

 


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

Punching Holes into HTTP.SYS

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

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

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

httpcfg.exe query urlacl

or netsh on Vista/LHS:

netsh http show urlacl

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

One of those "wildcard" reservations is this one:

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

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

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

Doesn't this totally defeat the purpose???


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

SQL Server 2005 Security

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

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


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

CardSpace and decrypting Tokens

While it is (technically) easy to CardSpace enable a web application or service, there are some implications regarding certificates and keys you should be aware of. Let's focus here on the web application scenario as I think this is what most people will try mostly these days. But first some background…

CardSpace only works over SSL – that means the page that triggers the identity selector UI must use HTTPS (which is a good thing). This has two reasons – first the identity selector wants to give the user information about the relying party (information from the SSL certificate as well as the issuer). But in addition to simply sending the token in clear text over SSL transport protection, CardSpace also encrypts the token using the SSL public key found in the certificate.

Now when you want to decrypt the token in the web application, you need access to the SSL private key. This causes some problems and when you search the usual forums you will find a lot of questions around that. Mostly centered around an error message saying "Keyset not found" you will most likely get when using the TokenProcessor that comes with the SDK. This error message means that the application account doesn't have read access to the private key. Now to get this working I wrote a tool some time ago that allows you to easily change ACLs on key containers (Vista has this functionality built in) – but back up for a second – and think about what you are doing here…

Web applications are the primary targets of attacks and since these key requirements for CardSpace are well known there will be a new class of attacks against such applications directly aimed at stealing your SSL private key. So whenever you application (or IIS itself) has a code defect that allows to disclose files on the file system (e.g. directory traversal) your private key lives in a very risky environment. This would allow an attacker to spoof your web site identity using the correct certificate/key combination – and this, of course, is something very dangerous.

So what's the recommended way of dealing with such a situation? I think you should never grant your application account direct access to the SSL private key – a better approach would be to factor out the decryption code into a separate secured process that runs under a different account (and only this account should be able to read the private key). Your web app and this process would talk to each other using a locked down communication channel (e.g. using WCF or Enterprise Services).

This is not hard to do – but is of course more work than simply setting an ACL. Now one of the goals of CardSpace is to get rid of the myriad of passwords we have to deal with, also for non high-security applications like forums. I have my doubts that these types of applications will a) enable SSL and b) take the burden of getting such a server side infrastructure in place. And if you are running on a shared host with no access besides to your web directory this is impossible.

Now while at shared hosting – most standard hosting offers don't give you a separate application pool which means you have to share the process with other customers. In this case there is no way of granting you access to your SSL key in a secure fashion. This of course is also closely related to the fact that the token decryption code has some dependencies on WCF which forces you to run in full trust – and this does not only mean that you are fully trusted – but that you fully trust everyone you are sharing the server with.

I think it would be useful to have an option in CardSpace (for self issued cards) to send the token in clear text over SSL – this would remove the risk of needing access to the private key. This should be of course not the default and opt-in. But I think this would solve some security problems in the end (even if it looks more insecure at first – but making these trade offs is one of the most fascinating aspects of security IMHO).


Work in Progress | CardSpace
Saturday, March 17, 2007 9:43:13 AM UTC  #   
 Tuesday, March 13, 2007

I can read your Googlemail

Enno asked me yesterday why Googlemail is using clear text HTTP by default – WTF?!

I didn't want to believe him and tried it out myself – and yes – if you go to http://www.googlemail.com they use SSL only for the initial login and redirect back to clear text directly after that (and I am pretty sure this has changed since I used the web front end the last time…). That means all the XmlHttpRequest calls and everything goes in clear text over the wire. Why are you doing that Google?? I heard you have so many machines – don't you have some spare CPU cycles to protect your innocent users??

Clear text HTTP connections are easy to eavesdrop (especially over wireless network like in hotels, airports or conferences) and given the way how googlemail works, someone could send "delete" or "send" requests and misuse your mailbox to send away offers for pharmacy, software and the like ;)

Interestingly if you open the login page using SSL – they don't redirect you back – so as an immediate solution, change your bookmarks to https…Besides that you are getting this annoying "do you want to display secure and non secure items" dialog (which also says a lot about the design) – this seems to work…tsts…Hall of shame!


Misc
Tuesday, March 13, 2007 9:13:02 PM UTC  #   

Hotels and Emails

It is not often that I rant on this blog. But this really pisses me off.

At least in Europe, hotels think they have to "proxy" my SMTP connections – well a better word than "proxying" would be "man in the middle attack" I guess. So all outgoing mail goes via their SMTP servers and they forward them to the real server. I am using SMTP over SSL for all my mail accounts – and of course this cannot work with a man in the middle. So this means I have to lower my security just to be able to send emails – and to add insult to injury – some of them force you to do this over unsecured wireless networks. WTF!?

After spending some time talking to support staff I finally got an answer why they are doing that: "It is because of SPAM – we don't want that anybody can send SPAM emails without notice over the hotel network…" Wow – how lame!

So what I normally end up doing is to use my Googlemail account – they also do SSL over SMTP but don't use the normal TCP/25 for that - and this just works (but of course causes other problems like ending up in junk mail folders because people don't have this account in their address book and messing up replies etc).

What an effective countermeasure against SPAM…


Misc
Tuesday, March 13, 2007 10:55:29 AM UTC  #   
 Thursday, March 08, 2007

Windows Process Activation and Faulted Application Pools

This week I ran into a nasty bug in the Windows Process Activation Service (WAS or WPAS?) on Vista. Here is what happened…

I fired up my little WAS application to verify some behavior with non-HTTP WCF endpoints hosted in WAS. But suddenly I got an EndpointNotFoundException in the client. This used to work before and only affected the non-HTTP endpoints. I had no clue what was going on.

After a little more looking around I noticed that the WAS listener adapter NT services are not started (net.tcp, net.msmq and net.namedpipes as well as the tcp port sharing service). And when I tried to start them I got the following scary error in the event log (in SMSvcHost):

System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

at System.Runtime.InteropServices.Marshal.ReadByte(IntPtr ptr, Int32 ofs)
at System.Security.Principal.Win32.ConvertIntPtrSidToByteArraySid(IntPtr binaryForm)
at System.Security.Principal.SecurityIdentifier..ctor(IntPtr binaryForm, Boolean noDemand)
at System.Security.Principal.SecurityIdentifier..ctor(IntPtr binaryForm)
at System.ServiceModel.Activation.ListenerAdapterBase.onApplicationPoolCreated(IntPtr context, String appPoolId, IntPtr sid)

With some help from the IIS/WAS team (thanks to Mike for routing me to the right people) we could narrow down the problem – it turned out that this is related to another event log entry I found (from WAS):

Application pool LabPool has been disabled. Windows Process Activation Service (WAS) did not create a worker process to serve the application pool because the application pool identity is invalid.

Now this rang a bell. Some time ago I created an additional application pool that used a custom worker account. After I was finished with my work (and as a good security guy ;) I disabled the worker process account. But I did not delete or disable the application pool.

This was some weeks ago and since I rarely reboot my machine and mostly just put it into sleep mode at night (seems logical ;) everything was fine. Now I did a reboot beginning this week and suddenly all this WAS/SMSvcHost error messages popped up because the application pool could not be started.

Now it turns out that there is a bug in WAS. When a single application pool on the system fails to start, the whole WAS infrastructure dies. Regardless if the application pool is used or not…

Hopefully this post can save you some hours of pulling hair and reading log files…Thanks to all the guys at MS that helped me troubleshoot this!


Work in Progress
Thursday, March 08, 2007 5:59:56 PM UTC  #   
 Wednesday, March 07, 2007

Debugging Services on Vista

With the new session isolation features in Vista you cannot easily debug services like you used to do. Here is a good writeup.

 


For Your Favourites
Wednesday, March 07, 2007 12:49:38 PM UTC  #   

Worker Accounts and Resetting Passwords

This week I spent some hours troubleshooting a problem with IIS7 (more on that in another post) and came across a gotcha you can run into when using user profiles. This is something I knew and I just forgot about it – so I thought I share with you – just in case…

In this post I said that IIS7 now loads user profiles for worker accounts. This is a nice feature since it allows using per account data that can be easily isolated from other accounts on the same machine. One example I gave was using the per-user certificate/key store. So far so good.

But you should be extremely careful whenever you want to change the password of a worker account. There are two ways of "changing" a password. The first is, when logged in as that user, to press Ctrl-Alt-Delete and select "Change password". The other way is to go to "Computer Management" and reset the password of the account. These two methods are fundamentally different and the latter is not recommended at all.

Some background: Windows uses per-used DPAPI to encrypt some data in the profile (e.g. key stores). When you manually use DPAPI with the user scope, the same key is used. This key is partially derived from the user's password. That means whenever the user changes his password, the DPAPI key gets renewed and rotated. When the user changes his own password (method 1) Windows can rotate the DPAPI key (because it knows the old and new password). Everything is fine.

But when you reset the password, Windows cannot rotate the key and all encrypted data that used the "old" key will get lost.

So when setting up worker/service accounts you have two options:

  • Choose a really strong (and random) password upfront, e.g. using Keith's excellent Password Minder tool.
  • Login as the account and use the change password option

Don't reset the password – or at least be aware of the implications.

You can easily test this yourself: Use the protected configuration sample described in my post here and encrypt the connection string. After that reset the worker account password and reconfigure the app pool in IIS7 with the new password. After that do a reboot and call the protected configuration page again. You will see a runtime exception and the connection string cannot be decrypted again.

Just keep that in mind.

 


Work in Progress
Wednesday, March 07, 2007 12:16:44 PM UTC  #   

MSDN US Webcast: ASP.NET Security - Partial Trust

So this was the last of my 5 part mini series on ASP.NET Security. Find the samples for partial trust ASP.NET here.

Thanks to all attendees!

 


Work in Progress
Wednesday, March 07, 2007 11:55:09 AM UTC  #   
 Monday, March 05, 2007

MSDN US Webcast: ASP.NET Security - Logging and Instrumentation

Thanks to all that attended todays webcast. Find the demos here. Enjoy!

 


Work in Progress
Monday, March 05, 2007 9:20:20 PM UTC  #   
 Thursday, March 01, 2007

DevWeek 2007

Thanks to everyone who attended my talks!

Find the samples for my Windows security talk here - and for IIS7 here.

For IIS7 you maybe also wanna have a look at the end to end extensibility walkthrough I posted some weeks ago.



Thursday, March 01, 2007 12:29:24 PM UTC  #   
 Sunday, February 25, 2007

IE7 and local Proxies (e.g. Fiddler)

This is just a repeat of a german blog post by Marc Höppner - but I never read this elsewhere and it is so incredibly useful....

IE7 always bypasses proxies for "localhost" addresses. Regardless of configuration. Now when you want to use e.g. Fiddler with a local web app, this can become an issue.

If you are using IIS it's easy, just use the local machine name instead of "localhost" (something I usually do anyways because of SSL). But Cassini doesn't accept machine names - the (quite funny) workaround for that is to use "localhost." (note the dot) instead. Works fine....

 


Work in Progress
Sunday, February 25, 2007 11:05:27 AM UTC  #   
 Friday, February 23, 2007

MSDN US WebCast: ASP.NET Security - Storing Secrets

As always, the slides and demos I showed can be downloaded here.

See you next time!

 


Work in Progress
Friday, February 23, 2007 7:43:44 PM UTC  #   
 Thursday, February 22, 2007

MSDN US WebCast: ASP.NET Security - Input Validation

Thanks to all attendees - find the slides and source here.

See you next time!

 


Work in Progress
Thursday, February 22, 2007 8:34:00 PM UTC  #   

MSDN WebCast: ASP.NET Security 4 - Logging und Instrumentation

So es ist geschafft - vier WebCasts zu ASP.NET Security. Mir hat es Spass gemacht und ich hoffe für euch war etwas interessantes dabei!

Wie immer Slides und Code hier - und die ganze Serie im Überblick mit WMV download gibt es hier (ich hoffe der bleibt stabil...).

 


Microsoft Deutschland Security Portal
Thursday, February 22, 2007 4:34:19 PM UTC  #   

New (and finalized) CardSpace Resources

Recently Kim released the finalized versions of the CardSpace spec documents – get them here:

  • Information Card Profile V1.0 Technical Reference (here)
  • A Guide to Interoperating with the Infomation Card Profile V1.0 (here)
  • Guide to Supporting Information Cards within Web Applications and Browsers (here)

In addition there are now two sample Security Token Services you can play with:

Have fun…


For Your Favourites
Thursday, February 22, 2007 9:15:50 AM UTC  #   
 Wednesday, February 21, 2007

IIS7: Custom Worker Accounts and User Profiles

One of my favorite feature of IIS7 (well, there a lot) is that user profiles are now loaded for the worker process accounts. Why is this important?

A lot of the Windows functionality (and in my case – security features) depend on loaded profiles, e.g. DPAPI user keys and per user certificate/key stores. Prior to IIS7 you were always forced to use DPAPI machine keys (with application defined entropy) or to store application certificates in the machine store (and manually ACLing the private keys). This also affects the protected configuration feature which relies either on DPAPI keys or the key store. Per application pool profiles (and thus keys) makes isolating applications on a server now much easier.

Another interesting fact is that IIS7 injects the required group (called IIS_IUSRS) into the worker process account – in IIS6 you had to manually add the worker process account into IIS_WPG group.

To demonstrate all this I hacked a little proof of concept web site together that shows the worker process groups and uses the ProtectedData APIs, protected configuration and the X509Store class to query the user certificate store. Nice!

IIS7UserProfilesTest.zip (3.17 KB)

 


Work in Progress
Wednesday, February 21, 2007 12:12:03 PM UTC  #   
 Tuesday, February 20, 2007

MSDN US WebCast: Authentication and Authorization with ASP.NET 2.0

Thanks for attending the first webcast in my mini series!

Find the slides and code here.

 


Work in Progress
Tuesday, February 20, 2007 8:55:19 PM UTC  #   
 Wednesday, February 14, 2007

My 1st MSDN Magazine Article

The march issue of MSDN Magazine has just been released - it includes an article about certificates support in the .NET Framework I wrote a while ago.

I think MSDN Magazine is great and I am really happy this worked out...

http://msdn.microsoft.com/msdnmag/issues/07/03/NETSecurity/default.aspx

 


For Your Favourites | Work in Progress
Wednesday, February 14, 2007 11:07:19 PM UTC  #   
 Monday, February 12, 2007

DevelopMentor and Me

After my last post I received some emails asking me if I have left DevelopMentor …

No – that’s not the case! I am still taking care of their security curriculum and I am still teaching/authoring WCF, .NET Security and ASP.NET. Great people work at DM that I would really miss – besides the fact that I really enjoy teaching and helping people understand technologies that I find interesting.

Since I am doing consulting only for Thinktecture and training only for DevelopMentor there is no overlap at all.

 


Work in Progress
Monday, February 12, 2007 10:58:35 PM UTC  #   

Thinktecture and Me

I know Christian and Ingo for quite some time now. I respect their work and knowledge and (equally important) we always have a good time when we meet (late nights and high hotel/mini bar bills included).
Since security is part of every serious project today, I often helped the guys out when they had questions about Windows, ASP.NET or WCF security. So it seemed like a logical step to “officially” join them as a consultant. Since today the new website is online and you can have a look at which services we offer.

www.thinktecture.com


Work in Progress
Monday, February 12, 2007 1:36:00 PM UTC  #   
 Thursday, February 08, 2007

Admin Title Bar for PowerShell

I like this feature in Vista that elevated command prompts have the word "Administrator" in the title bar. This seems to be some special functionality baked into the command prompt - at least this does not work when starting PowerShell elevated.

This little script can do the trick - just call it from your profile.ps1:

$id = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$p = New-Object System.Security.Principal.WindowsPrincipal($id)

if ($p.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator))
{
 $Host.UI.RawUI.WindowTitle = "Administrator: " + $Host.UI.RawUI.WindowTitle
}

Thanks to Christian for the title bar code.

 


Work in Progress
Thursday, February 08, 2007 5:15:50 PM UTC  #