ict.ken.be

Delivering solid user friendly software solutions since the dawn of time.

Introduction to Identity and Access Control

Categories: .Net Security

by Dominick Baier
dbaier@leastprivilege.com
http://www.leastprivilege.com
@leastprivilege Principals & Identities

interface IIdentity
{
	bool IsAuthenticated { get; }
	string AuthenticationType { get; }
	string Name { get; } 
}

interface IPrincipal
{
	IIdentity Identity { get; }
	bool IsInRole(string roleName);
}
  • Thread.CurrentPrincipal Every thread can have his own client security context Plumbing sets it, application gets it.
WindowsPrincipal
var id = WindowsIdentity.GetCurrent();
var principal = new WindowsPrincipal(id);

principal.IsInRole("Builtin\\Users") //Don't use because they are localized.
var localAdmins = new SecurityIdentifier(WellknownSidType.BuiltinAdministratorSid, null);
var domainAdmins = new SecurityIdentifier(WellknownSidType.AccountDomainAdminsSid, id.User.AccountDomainSid);
var users = new SecurityIdentifier(WellknownSidType.BuiltinUsersSid, null);

var account = new NTAccount(id.name);
var sid = account.Translate(typeof(SecurityIdentifier));

var groups = user.Groups.Translate(typeof(NTAccount));
GenericPrincipal

var roles = new string[] { "Sales", "Marketing" };
var p = new GenericPrincipal(new GenericIdentity("bob"), roles);
Thread.CurrentPrincipal = p;
p.Identity.Name
Role-based access control (RBAC)

if p.IsInRole("Sales") {} //returns true/false
new PrincipalPermission(null, "Development").Demand(); //will throw security exception if fails

[PrincipalPermission(SecurityAction.Demand, Role="Development"] //hard to unit test
private static void DoDevelopment() {}
  • Claims are statements
  • How do we handle things no longer in corporate network like cloud, partners and customers ?
  • Bell-Lapadula Model for goverment and military document security.

2002 Identity
2006 WCF System.IdentityModel with SecurityToken
2009 WIF Microsoft.IdentityModel with IClaimsIdentity & IClaimsPrincipal
2012 .NET 4.5 System.IdentityModel & System.Security.Claims eg. Bob is an administrator, Jim's email address is jim@foo.com, ...

public class Claim
{
	public virtual string Type { get; }
	public virtual string Value { get; }
	public virtual string Issuer { get; }
	//...
}

class ClaimsIdentity : IIdentity
{
	IEnumerable<Claim> Claims { get; }
}

class ClaimsPrincipal : IPrincipal
{
	ReadOnlyCollection<ClaimsIdentity> Identities { get; }	
}

var claim = new Claim("name", "dominick");
var claim = new Claim(ClaimTypes.Name, "dominick");

var Claims = new List<Claim>
{
	new Claim(ClaimTypes.Name, "dominick"),
	new Claim(ClaimTypes.Email, "dominick@foo.com"),
	new Claim(ClaimTypes.Role, "Geek"),
	new Claim("http://myClaims/location", "Heidelberg")
};

var id = new ClaimsIdentity(claims);
id.IsAuthenticated -> false because you can add claims to anonymous
var id = new ClaimsIdentity(claims, "Console App", ClaimTypes.Name, ClaimTypes.Role); //what do name and isrole map too for legacy
id.IsAuthenticated -> true

var cp = new ClaimsPrincipal(id); //prefered entry point
Thread.CurrentPrincipal = cp;
-> var cp = ClaimsPrincipal.Current;

var email = cp.FindFirst(ClaimTypes.Email).Value;

RolePrincipal, GenericPrincipal, WindowsPrincipal : ClaimsPrincipal : IPrincipal
Generalization & Specialization Interface level: IIdentity : Name, AuthenticationType, IsAuthenticated
Claims identity: ClaimsIdentity : Claims, FindAll(), FindFirst(), HasClaim()
Domain specific: WindowsIdentity : Token, Impersonate(), User/Device

  • Claims Always try to use ClaimsIdentity for your custom principal implementation.
class CorpIdentity : ClaimsIdentity
{
	public CorpIdentity(string name, string reportsTo, string office)
	{
		AddClaim(new Claim(ClaimTypes.Name, name));	
		AddClaim(new Claim("reportsto", reportsTo));	
		AddClaim(new Claim("office", office));	
	}
	
	public string office
	{
		get { return FindFirst("reportsto").Value; }	
	}
}
  • Services Unification of various credential formats to common ClaimsPrincipal representation Windows/Kerberos, Forms Authentication, HTTP basic authentication, SSL client certificates, WS-Security tokens, SAML, extensible, ...
  • Processing Pipeline Request (xml/binary/text) -> Security token handler (seserialization/validation) -> Claims transformation (skipped when session available) -> Security Session Management -> Session Security Token -> Authorization
public class ClaimsTransformer : ClaimsAuthenticationManager
{
	public overrride ClaimsPrincipal Authenticate(string resourceName, ClaimsPrincipal incomingPrincipal)
	{
		var name = incomingPrincipal.Identity.Name;
		if (string.IsNUllOrWhiteSpace(name)) throw new SecurityException("Name claim is missing");
		if (incomingPrincipal.Identity.IsAuthenticated)
		{
			return TransformClaims(incomingPrincipal);
		}
		return incomingPrincipal;
	}
}
<system.identityModel>
<identityConfiguration>
	<claimsAuthenticationManger type="assembly/class" />
</identityConfiguration>
</system.identityModel>
var p = new WindowsPrincipal(WindowsIdentity.GetCurrent());
Thread.CurrentPrincipal = FederationAuthentication.FederationConfiguration.identityConfiguration.ClaimsAuthenticationManager.Authenticate("none", p) as IPrincipal;
  •  Session management Preserve a ClaimsPrincipal across round trips (cookies, ws-secureconversation) 
var sessionToken = new SessionSecurityToken(principal, TimeSpan.FromHours(8));
FederatedAuthentication.SessionAuthenticationModule.WriteSessionTokenToCookie(sessionToken);
  • Data protection api, zero configuration but only for single server.
  • For webfarms use machinekey or ssl-certificate to protect your cookie.
  • Roundtrip the identity and cache the claims principal.
  • Claims authorization manager Extensibility point for loading/parsing authorization policy
  • Extensibility point for mapping operations/resources to required claims
  • Auto-invoked during request processing
  • Application code should not check for claims directly 
public class AuthorizationContext
{
	public AuthorizationContext();
	public Collection<Claim> Action { get; }
	public ClaimsPrincipal Principal { get; }
	public Collection<Claim> Resource { get; }
}

<claimsAuthorizationManager type="ClaimsAuthorizationManagerClass, theAssembly" >
	<policy file="foo.xml" /> 
</claimsAuthorizationManager>
public class ClaimAuthZManager : ClaimsAuthorizationManager
{
	public override bool CheckAccess(AuthorizationContext context)
	{
		//inspect context and make authorization decision
		var resource = context.Resource.First().Value;
		var action = context.Action.First().Value;
		if (action == "Show" && resource == "Castle")
		{
			var hasCastle = context.Principal.HasClaim("http://myclaims/hasCastle", "true");
			return hasCastle;
		}
		return false;
	}
	override void LoadCustomConfiguration(XmlNodeList nodelist)
	{
		base.LoadCustomConfiguration(nodelist);
	}
}
[ClaimsPrincipalPermission(SecurityAction.Demand, Operation = "Add", Resource = "Customer")]
public void AddCustomer(Customer customer) { ... }
void Print(Document document)
{
	if (ClaimsPrincipalPermission.CheckAccess(document.Printer, "Print")) { ... }
}
var authZ = FederatedAuthentication.FederationConfiguration.IdentityConfiguration.Claims.ClaimsAuthZManager;
authZ.CheckAccess(...);

http://msdn.microsoft.com/en-us/library/system.security.claims.claimsauthorizationmanager.aspx

Protocol support

  • Web Application : WS-Federation
  • SOAP : WS-Trust & WS-Security
  • WebApi : OAuth2 Client -> STS -> Token -> Client -> Token -> Relying Party/Application (no authentication on RP) <saml:Assertion ... Signature ...> tokens for seamless third party authentication

Security Token Services

  • Microsoft Active Directory
  • Federation Service 2
  • IBM Tivoli Federation Manager
  • Oracle Identity Manager
  • Ping Federate
  • Thinktecture .NET 4.5 (http://identityserver.codeplex.com/) ASP.Net
<authentication mode="windows">
	<forms loginUrl="~/Account/Login" timeout="2880" />
</authentication>

<system.identityModel configSource="identity.config" />
<system.identityModel>
	<identityConfiguration>
		<audienceUris />>
		<claimsAuthenticationManager type="Security.ClaimsTransformer, Web" />
		<issuerNameRegistry />
	</identityConfiguration>
</system.identityModel>

private void EstablishSession(ClaimsPrincipal principal)
{
	if (HttpContext.Current != null)
	{
		var sessionToken = new SessionSecurityToken(principal);
		FederatedAuthentication.SessionAuthenticationModule.WriteSessionTokenToCookie(sessionToken);
	}
}
<system.webServer>
	<add name="ClaimsTransformationModule" type="Security.ClaimsTransformationHttpModule" />
	<add name="SessionAuthenticationModule" type="Security.IdentityModel.Services.SessionAuthenticationModule, ..." />
</system.webServer>

<system.identityModel.services configSource="identity.Services.config" />
<system.identityModel.services>
	<federationConfiguration>
		<wsFederation passiveRedirectionEnable="true" issuer="remote login page location"
		realm="" requireHttps="true" />
		<cookieHandler requireSsl="true" />
	</federationConfiguration>
</system.identityModel>

for wcf: <bindings> <ws2007FederationHttpBinding>

webapi

[Authorize]
public class IdentityController : ApiController
{
	public Identity Get()
	{
		return new Identity(User.Identity);
	}
}