ACS Security Guidelines

Updated: June 19, 2015

Applies To: Azure

Applies To

  • Microsoft Azure Active Directory Access Control (also known as Access Control Service or ACS)

  • Windows Identity Foundation (WIF)

Summary

This topic consolidates the security guidelines for ACS. Use these guidelines to improve the quality of your implementation from a security perspective. You can use these guidelines when reviewing the architecture of your application, reviewing code and logging security bugs, and reviewing production deployment. These guidelines refer you for more information to How To’s with prescriptive steps for accomplishing a specific task and to conceptual topics to learn more about a particular feature or function of ACS.

Objectives

  • Address security issues related to the code and configuration of an application in the context of ACS.

  • Address security issues related to ACS configurations.

  • Address security issues related to Azure deployments in the context of ACS.

Following are security guidelines related to your application’s code and configuration.

  • Consider setting the replay detection feature (DetectReplayedTokens) to true.

  • Set the requireHttps attribute of wsFederation to true.

  • Set the requireSsl attribute of cookieHandler to true.

  • Set the aggressive value for lifetime attribute of sessionTokenRequirement.

  • List the trusted STS (token issuers) in issuerNameRegistry.

  • Scope tokens that should be accepted by your application using audienceUri.

Consider setting the replay detection feature (DetectReplayedTokens) to true

WIF has a replay detection cache specifically for security token service (STS) bearer tokens, which is just a cache of previously used STS tokens. When the client first authenticates to the relying party using the STS token, it receives a session security token that it uses to authenticate to the relying party for any additional requests. It does this over Secure Sockets Layer (SSL) so that the session security token cannot be stolen. If a client or an attacker tries to authenticate to the relying party with an STS token that the client has used already, the relying party can look up the STS token in the replay cache and refuse the request.

This cache does not guarantee that a token can never be replayed. It performs best-effort detection based on the size of the cache, the expiration time of the STS token, and the rate of unique authentication requests received by the relying party. We strongly recommend that you set the cache size and STS token expiration time for your relying party to get the right balance between performance and security.

Example:

<securityTokenHandlers>
  <securityTokenHandlerConfiguration>
    <tokenReplayDetection enabled="true" capacity="1000" expirationPeriod="500"/>
  </securityTokenHandlerConfiguration>
</securityTokenHandlers>

Note

Using this feature introduces server affinity that may lead to scalability issues in load-balanced environments, including Azure. To overcome this, you may consider implementing your own token replay detection using a base abstract class Microsoft.IdentityModel.Tokens.TokenReplayCache and then referencing it in the configuration file, with code similar to the following.

<system.identityModel>
  <identityConfiguration>
    <tokenReplayDetection>
      <replayCache type='FQTN of your type' />
    </tokenReplayDetection>
  </identityConfiguration>
</system.identityModel>

Set the requireHttps attribute of wsFederation to true

The requireHttps attribute controls whether the module will only redirect a secure URL for the STS. The default is “true”. This ensures secure communications with STS over clear HTTPS/SSL traffic, mitigating credentials and token sniffing over the wire.

Example:

<federatedAuthentication>
  <wsFederation
        passiveRedirectEnabled="true"
        issuer="http://STS URL GOES HERE/"
        realm="http://RP REALM GOES HERE/"
        requireHttps="ture" />
  <cookieHandler requireSsl="true" />
</federatedAuthentication>

Set the requireSsl attribute of cookieHandler to true

This value is Boolean; the default is false. The requireSsl attribute controls whether the "Secure" flag is emitted for any cookies written. If this value is set, the sign-in session cookies will be available only over HTTPS. This prevents sending session cookies over the clear traffic, mitigating the threat of the token being sniffed over the wire.

Example:

<federatedAuthentication>
  <wsFederation
        passiveRedirectEnabled="true"
        issuer="http://STS URL GOES HERE/"
        realm="http://RP REALM GOES HERE/"
        requireHttps="ture" />
  <cookieHandler requireSsl="true" />
</federatedAuthentication>

Set the aggressive value for lifetime attribute of sessionTokenRequirement

Consider requiring tokens to be issued with aggressive lifetime limits. This would limit an attacker’s time frame for replaying the token in case it was stolen.

Example:

<add type="Microsoft.IdentityModel.Tokens.SessionSecurityTokenHandler, Microsoft.IdentityModel">
  <sessionTokenRequirement securityTokenCacheType="Microsoft.IdentityModel.MruSecurityTokenCache, Microsoft.IdentityModel"
                           saveBootstrapTokens="true"
                           securityTokenCacheSize="500"
                           useWindowsTokenService="false"
                           lifetime="10:00" />
</add>

List trusted STS (token issuers) in issuerNameRegistry

All issuer tokens are validated using the IssuerNameRegistry. The purpose of the IssuerNameRegistry is to map the issuer token to a string name. If validation fails the token will not be accepted. This mitigates the threat of accepting tokens that are not trusted. Any custom type can be registered using the type attribute of the <issuerNameRegistry> element. The <issuerNameRegistry> can have one child element that will serve as custom configuration to the IssuerNameRegistry. One IssuerNameRegistry type is provided out of the box—ConfigurationBasedIssuerNameRegistry—that can be used to configure a set of trusted issuer certificates in configuration. This type requires a child configuration element <trustedIssuers> where trusted issuer certificates configured. <trustedIssuers> configuration adds trusted certificatess using the ASN.1 encoded form of the certificate thumbprint.

Example:

<issuerNameRegistry type="Microsoft.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry, Microsoft.IdentityModel">
  <trustedIssuers>
    <add thumbprint="97249e1a5fa6bee5e515b82111ef524a4c9158de" name="contoso.com" />
    <remove thumbprint="97249e1a5fa6bee5e515b82111ef524a4c9158de" />
    <clear/>
  </trustedIssuers>
</issuerNameRegistry>

Scope tokens to your application using only audienceUri

<audienceUris> specifies the set of URIs that are acceptable as identifying this relying party. Tokens will not be accepted unless they are scoped for one of the allowed audience URIs. This mitigates the threat of replaying valid tokens that were issued for other relying parties. By default, no URIs will be added to the collection. The SecurityTokenHandler for the SAML 1.1 and SAML 2.0 token types use the values in this collection to configure any allowed audience uri restrictions in SamlSecurityTokenRequirement objects.

Example:

<audienceUris>
  <clear/>
  <add value="http://www.example.com/myapp/" />
  <remove value="http://www.example.com/myapp/" />
</audienceUris>

Following are security guidelines related to ACS Management Portal configuration.

  • Set aggressive expiration for STS tokens

  • Provide adequate data validation when using the Error URL feature

  • Consider encrypting tokens for highly sensitive scenarios

Set aggressive expiration for STS tokens

The token lifetime attribute controls the lifetime of the token. It is specified when you are creating or configuring the relying party using the ACS portal or Management Service. You can use the Token Lifetime property to specify the amount of time for a security token issued by ACS to the relying party application to remain valid. By default, in ACS, this value is set to 10 minutes (600 seconds). In ACS, this value must be greater than zero but less than or equal to 24 hours (86400 seconds).

Provide adequate data validation when using the Error URL feature

You can use the Error URL to specify a URL to which ACS redirects users if an error occurs during the login process. It can be a custom page hosted on the relying party application, for example, http://www.fabrikam.com/billing/error.aspx. As part of the redirect, ACS supplies details about the error to the relying party application as a JSON-encoded HTTP URL parameter. The custom error page can be crafted to consume the JSON-encoded error information to render the actual error message received or display static help text. If the page requires authorization, the result will be an infinite-redirection loop in a case where ACS will try to access it and send the JSON encoded error. Therefore, it should be configured for anonymous access. Because the page can be accessed anonymously and because it might include code that echoes back HTML or write data to the database, you should take steps to prevent cross-site scripting and SQL Injection attacks.

Consider encrypting tokens for highly sensitive scenarios

For highly sensitive scenarios for passive federation consider encrypting tokens. Read more about encrypting and decrypting token in Certificates and Keys topic.

Following are security considerations related to applications that are using ACS and that are deployed to Azure.

  • Encrypt cookies using RSA

Encrypt cookies using RSA

In Azure, the default cookie encryption mechanism (which uses data protection application programming interfaces (DPAPI)) is not appropriate because each instance has a different key. This would mean that a cookie created by one web role instance would not be readable by another web role instance. This could lead to service failures, effectively causing denial of the service. The following is the error message that you will encounter if you use the default cookie encryption mechanism:

[CryptographicException: Key not valid for use in specified state.
]
System.Security.Cryptography.ProtectedData.Unprotect(Byte[] encryptedData, Byte[] optionalEntropy, DataProtectionScope scope) +577
Microsoft.IdentityModel.Web.ProtectedDataCookieTransform.Decode(Byte[] encoded) +80
[InvalidOperationException: ID1073: A CryptographicException occurred when attempting to decrypt the cookie using the ProtectedData API (see inner exception for details). If you are using IIS 7.5, this could be due to the loadUserProfile setting on the Application Pool being set to false. ]
Microsoft.IdentityModel.Web.ProtectedDataCookieTransform.Decode(Byte[] encoded) +433
Microsoft.IdentityModel.Tokens.SessionSecurityTokenHandler.ApplyTransforms(Byte[] cookie, Boolean outbound) +189
Microsoft.IdentityModel.Tokens.SessionSecurityTokenHandler.ReadToken(XmlReader reader, SecurityTokenResolver tokenResolver) +862
Microsoft.IdentityModel.Tokens.SessionSecurityTokenHandler.ReadToken(Byte[] token, SecurityTokenResolver tokenResolver) +109
Microsoft.IdentityModel.Web.SessionAuthenticationModule.ReadSessionTokenFromCookie(Byte[] sessionCookie) +356
Microsoft.IdentityModel.Web.SessionAuthenticationModule.TryReadSessionTokenFromCookie(SessionSecurityToken& sessionToken) +123
Microsoft.IdentityModel.Web.SessionAuthenticationModule.OnAuthenticateRequest(Object sender, EventArgs eventArgs) +61
System.Web.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +80
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +270

To solve this problem, use a cookie encryption mechanism that uses a key that is shared by all the web role instances. The following code shows how to replace the default SessionSecurityHandler object and configure it to use the RsaEncryptionCookieTransform class.

private void OnServiceConfigurationCreated(object sender, 
    ServiceConfigurationCreatedEventArgs e)
{
    List<CookieTransform> sessionTransforms =
        new List<CookieTransform>(
            new CookieTransform[] 
            {
                new DeflateCookieTransform(), 
                new RsaEncryptionCookieTransform(
                    e.ServiceConfiguration.ServiceCertificate),
                new RsaSignatureCookieTransform(
                    e.ServiceConfiguration.ServiceCertificate)  
            });
   SessionSecurityTokenHandler sessionHandler = 
    new
     SessionSecurityTokenHandler(sessionTransforms.AsReadOnly());

    e.ServiceConfiguration.SecurityTokenHandlers.AddOrReplace(
        sessionHandler);
}

void Application_Start(object sender, EventArgs e)
{
    FederatedAuthentication.ServiceConfigurationCreated += OnServiceConfigurationCreated;
}

Additional Resources