Shibboleth Service Provider Access Control

One of the great advantages of Shibboleth are its capabilities to easily define secure access control rules. On this page one finds useful examples and explanations on how these rules can be used effectively.

A resource can be protected with acces rules defined in the web server configuration, in Shibboleth or by the application itself. In all cases a Shibboleth session must be enforced first. This ensures that the user's attributes are available. A user is then only granted access if his attributes match the defined access control rules.

Find below an overview of the three methods how the Shibboleth session and access control can be enforced with Apache or IIS.

Apache: Static configuration
Most web servers are operated with Apache. Shibboleth allows to use Apache directives to protect directories, files or locations in one of Apache's configuration files (e.g. httpd.conf).
The disadvantage of this method is that the web server has to be restarted if an access control rule was changed.
Apache: Directory configuration (.htaccess) file
Apache supports the use of so-called .htaccess files, which can overwrite the static configuration if these files are placed in web server directories. The rules defined in this file are dynamicly processed. They are therefore reloaded without restarting the web server. This allows also dynamically changing Shibboleth access rules.
Ensure that the directory that contains an .htaccess file is configured with 'AllowOverride AuthConfig' in the Apache static configuration. This is a prerequisite for Apache to process .htaccess files. file to allow usage of .htaccess files.
The disadvantage of this method is that only existing files and directories can be protected but not arbitrary locations.
IIS/Apache: XML Access rules in Shibboleth configuration
In IIS none of the above-two methods to define Shibboleth access control rules are supported. Therefore, access control rules can only be defined directly in the Shibboleth configuration, either inline or by using a reference to an external file. The inline access rules as well as the externally linked file are loaded dynamically by Shibboleth. Defining access control rules in Shibboleth is also possible with Apache.

Example Apache Access Rules

Let's have a look at how access rules can be defined in Apache. A very simple access control rule that could be defined in the Apache configuration looks like this:

# Force user to authenticate on protected-directory
<Location /protected-directory>
  AuthType shibboleth
  ShibRequireSession On
  require valid-user
</Location>

This will enforce a Shibboleth session, which means that users have to authenticate in order to access the content of the directory "protected-directory" and all its sub directories. However, there is no real access control enforced yet. Anybody who manages to authenticate will get access.
The three lines between the <Location> elements are the Shibboleth access control directives. The same lines could also be used in an .htaccess file.

In case the whole Apache web server shall be protected with Shibboleth except for one specific location or sub directory, use a the following directives:

# First define a rule to enforce session on all pages
<Location />
  AuthType shibboleth
  ShibRequireSession On
  require valid-user
</Location>

# Then list the exceptions
<Location /unprotected>
  AuthType shibboleth
  ShibRequireSession Off
  require shibboleth
</Location>

More information Apache access control directives in general can be found on the pages NativeSPhtaccess and NativeSPApacheConfig of the official Shibboleth Wiki.

Find below additional examples of common access control rules, which are based on an example page by University of Zurich. Also have a look at the AAI Attribute Specification in order to see which attributes one can use in order to create fine-grained access control rules. The attribute names of these access control rules are defined as id and aliases in the attribute-map.xml of the Shibboleth configuration.

All users from universities of applied sciences with an AAI login
AuthType shibboleth
ShibRequireSession On
require homeOrganizationType uas
All users from university of Zurich and ETH Zurich
AuthType shibboleth
ShibRequireSession On
require homeOrganization unizh.ch ethz.ch
All users from university of Zurich who are staff members
AuthType shibboleth
ShibRequireSession On
ShibRequireAll On
require affiliation staff
require homeOrganization unizh.ch
Users whose email address match a regular expression (in this case all D-ITET students from ETHZ)
AuthType shibboleth
ShibRequireSession On
ShibRequireAll On
require affiliation staff
require homeOrganization ~ .*@ee.ethz.ch$
All students from university of Zurich or ETH Zurich
AuthType shibboleth
ShibRequireSession On
ShibRequireAll On
require affiliation student
require homeOrganization unizh.ch ethz.ch
All users from university of Zurich, ETH Zurich or two specific VHO groups
AuthType shibboleth
ShibRequireSession On
require homeOrganization unizh.ch ethz.ch
require entitlement http://www.olat.uzh.ch/phzh http://www.olat.uzh.ch/cas_bgm
Only users with the following e-Mail addresses
AuthType shibboleth
ShibRequireSession On
require mail user-x@switch.ch user-y@switch.ch
Only users with the following uniqueIDs
AuthType shibboleth
ShibRequireSession On
require uniqueID 000123@switch.ch 000455@switch.ch
Only users with the following uniqueIDs or from a certain VHO group with sub groups
AuthType shibboleth
ShibRequireSession On
require uniqueID 000123@switch.ch 000455@switch.ch
require entitlement ~ ^http://www.olat.uzh.ch/.*$
Only members of a specific Combo/Virtual Organization
AuthType shibboleth
ShibRequireSession On
require isMemberOf https://vo.switch.ch/kalle
Only Shibboleth users or users from a specific domain/IP range
AllowOverride All

# Use IP Authorisation or Shibboleth Authorisation
Satisfy Any

# IP Authorisation
Order Deny,Allow
Deny from all
Allow from switch.ch
Allow from 130.59.6.143
Allow from 2001:620::4:ca2a:14ff:fe2f:1b97

# Shibboleth Authorisation
AuthType Shibboleth
ShibRequireSession On
require homeOrganization switch.ch

XML access control

XML access rules are defined either directly in the Shibboleth configuration file (e.g. shibboleth2.xml within a <Host> element in the <RequestMap>) or in an externally referenced and dynamically loaded XML file.

Note:
In order to use XML access control rules with Apache (e.g. in order to dynamically protect a location), Apache first has to be made aware that Shibboleth shall be active for a given location. This can be achieved by using the configuration directives:

...
# Activate Shibboleth but don't enforce a session
<Location />
    AuthType shibboleth
    require shibboleth
</Location>
...

An example of an inline access control rule that protects the directory protected-directory but won't enforce an AAI session on the sub directory unprotected then looks like this:

...
<Host name="sp.example.org">
    <Path name="protected-directory" authType="shibboleth" requireSession="true">
        <AccessControl>
            <AND>
                <Rule require="affiliation">student</Rule>
                <OR>
                    <Rule require="homeOrganization">ethz.ch</Rule>
                    <Rule require="homeOrganization">unizh.ch</Rule>
                </OR>
                <NOT>
                    <Rule require="homeOrganization">vho-switchaai.ch</Rule>
                </NOT>
            </AND>
        </AccessControl>
        <Path name="unprotected" authType="shibboleth" requireSession="false" />
    </Path>
</Host>
...

As can be seen in the aboveThe allowed boolean operators are AND, OR and NOT.

An example of a linked XML access control file is given below. This method has the advantage that changes to the file don't require Shibboleth to be restarted to take effect. The name of the file can be choosen arbitrarily.

...
<Host name="sp.example.org">
  <Path name="secure" authType="shibboleth" requireSession="true">
     <AccessControlProvider uri="/var/www/secure/shibacl.xml"
      type="edu.internet2.middleware.shibboleth.sp.provider.XMLAccessControl"
     />
  </Path>
</Host>
...

The syntax in shibacl.xml looks like (similar to inline but note the required xml namespace definition):

<?xml version="1.0" encoding="UTF-8"?>
<AccessControl xmlns="urn:mace:shibboleth:target:config:1.0">
    <AND>
        <Rule require="affiliation">student</Rule>
        <OR>
            <Rule require="homeOrganization">ethz.ch</Rule>
            <Rule require="homeOrganization">unizh.ch</Rule>
        </OR>
        <NOT>
            <Rule require="homeOrganization">vho-switchaai.ch</Rule>
        </NOT>
    </AND>
</AccessControl>

More information on XML access control can be found on the official SP XML Access Control (Shibboleth Wiki).

Combination of .htaccess files and XML access control

Using .htaccess files has the advantage that access control rules are dynamically processed without restarting the web server. In addition, .htaccess files can be set by any user with write privileges for a web server directory. However, one drawback of .htaccess files is the lack of flexibility when it comes to create access control rules. In comparison to the XML Access Control rules the Apache access control directives can only be connected with boolean OR and AND operators in a very simple way. This may not be sufficient.
But since the Shibboleth Service Provider 2.4 there is a way how the advantages of .htaccess files and XML Access Control rules can be combined. This works using a .htaccess file like:

AuthType shibboleth
ShibRequireSession On
ShibRequireAll On
require shibboleth
ShibAccessControl /var/www/aai/shibacl.xml

Add an absolute file path after ShibAccessControl. This file (e.g. /var/www/aai/shibacl.xml) then must contain an XML Access Control rule. For example:

<AccessControl
 type="edu.internet2.middleware.shibboleth.sp.provider.XMLAccessControl">
    <OR>
        <AND>
            <Rule require="homeOrganization">ethz.ch</Rule>
            <Rule require="affiliation">staff</Rule>
            <Rule require="staffCategory">102</Rule>
        </AND>
        <AND>
            <Rule require="affiliation">student</Rule>
            <OR>
                <Rule require="homeOrganization">ethz.ch</Rule>
                <Rule require="homeOrganization">epfl.ch</Rule>
            </OR>
        </AND>
    </OR>
</AccessControl>

This would grant access only to students of ETHZ and EPFL or to lecturers of ETHZ.

Attribute Names

If you wonder what the names of the attributes in a web application (e.g. PHP, Perl, ...), they are defined in the file /etc/shibboleth/attribute-map.xml file (the attribute names correspond to the "id=" and "aliases" values of the <Attribute> elements) for Shibboleth 2.x. Alternatively, one can access the Shibboleth session handler after a successful authentication at '/Shibboleth.sso/Session'. This handler also shows the available attributes and their names in the web server environment.

Note:
If the Apache directive 'ShibUseHeaders On' is used as described below, the attributes will not only be available in the web server environment in form of web server environment variables but also in form of HTTP header variables. However, the names of HTTP header variables will be transformed by Apache so that they look differently than defined in the attribute-map.xml file. For instance, the attribute whose 'id' in attribute-map.xml is 'Shib-EP-Entitlement' will be transformed (name in uppercase letters, prepended by 'HTTP_' and all '-' are replaced by '_') to 'HTTP_SHIB_EP_ENTITLEMENT' by Apache.

VHO Users

Remember that users from the Virtual Home Organization can also authenticate via AAI and therefore may access all resource that don't enforce access control rules. In order to prevent VHO users from accessing a service they have to be distinguished from other AAI users. Using a rule like below one can prevent VHO users from accessing protected content:

AuthType shibboleth
ShibRequireSession On
require homeOrganizationType ~ ^[^vV][^hH][^oO]

or more elegant with an XML Access Control rule:

...
     <NOT>
         <Rule require="homeOrganizationType">vho</Rule>
     </NOT>
...

Alternative ways to authorize users

There are use-cases that involve protecting certain documents, web applications or just certain functions of a web application in a way where only access shall be granted to very specific users of one or more Home Organizations (see graphic below). In such a case, the users that shall get access most probably don't share a common attribute that would allow setting rules like the one above.

Group Management Tool use case

Therefore, it's necessary to set a common attribute for all these users. However, this is not easy for users from many different Home Organisations. Or for smaller groups of people, it might be sufficient to collect all their uniqueID/email/persistentId values and use them to create an access rule like:

     AuthType shibboleth
     ShibRequireSession On
     require uniqueID 234023480@unizh.ch 32489234@ethz.ch 435kjsm95s4we3ssdfsd5@unige.ch [...] 

Although this solution works as assumed, it may be hard to get all user unique IDs from the people that shall be granted access. There is an easy solution for this problem. It's called Group Management Tool and it allows to easily collect all the uniqueIDs. It's a web application that basically faciliates the mangement of users and groups in order to do authorization.

Java Applications

In case a Java application shall be protected with Shibboleth 2.x and if the applications also shall be able to read Shibboleth attributes, choose one of the following options in order to make Apache forward Shibboleth attributes to the Java application:

  • This is the recommended method.
    If mod_proxy_ajp is used to make Apache forward requests via AJP to a servlet container the following method can be used: In shibboleth2.xml add 'attributePrefix="AJP_"' to the <ApplicationDefaults> (or an appropriate <ApplicationOverride>) element. This is necessary because environment variables are only included in the request by mod_proxy_ajp if they have 'AJP_' prefixes.
    <ApplicationDefaults id="default" policyId="default"
        [...] 
        attributePrefix="AJP_" >
    
  • If mod_jk is used instead of mod_proxy_ajp, configure mod_jk to forward specific environment variables. The Apache configuration directives for accomplishing this are:
            JkEnvVar Shib-Application-ID
            JkEnvVar Shib-Session-ID
            JkEnvVar Shib-Identity-Provider
            JkEnvVar Shib-Authentication-Instant
            JkEnvVar Shib-SwissEP-UniqueID
            JkEnvVar Shib-InetOrgPerson-givenName
            JkEnvVar Shib-Person-surname
    
    This method is not very flexible because each attribute has to be configured explicitly.
  • If for some reason none of the above works, use the old Shibboleth 1.3 behaviour where attributes are stored in the web server environment as header variables. This can be done using an Apache directive called ShibUseHeaders On. This then would look like:
    	AuthType Shibboleth
    	ShibRequireSession On
    	ShibUseHeaders On
    	require valid-user
    
    This option is less secure than the others because headers are easier to spoof in web browsers (particularily IIS).

The underlying reason why one has to adapt the configuration with one of the above options is that Shibboleth attributes are not forwaded to a Java application by default. Shibboleth 2.x in its default configuration doesn't store the attributes as header variables in the web server environment but as environment variables. The effect of this that mod_proxy_ajp and mod_jk won't forward the attributes anymore to the servlect container unless one uses either of the above solutions. More information on this topic can be found on the pages NativeSPhtaccess of the Shibboleth Wiki.

Note: Due to a bug in some servlet containers it may not be possible to read the Shibboleth attributes directly using a loop like:

java.util.Enumeration names = request.getHeaderNames();while (names.hasMoreElements()) {
    String name = (String)names.nextElement();
    out.println(name+": "+request.getHeader(name)+"<br>");
}       

out.print("<br><br><b>Attribute</b><br>");
names = request.getAttributeNames();
while (names.hasMoreElements()) {
    String name = (String)names.nextElement();
    out.println(name+": "+request.getAttribute(name)+"<br>");
}
		

Instead, one has to access the header names directly using something like:

request.getAttribute("Shib-Identity-Provider");

This of course implies that one knows the attribute names. These are defined in the attribute-map.xml as id and aliases of the Attribute elements.