Overview
Most of the user interface involves Spring MVC “views”, which are rendered with a templating engine theoretically chosen from a number of supported options. While this is extensible, the three view resolvers installed by default, in order, are:
org.springframework.web.servlet.view.BeanNameViewResolver – used rarely, it resolves a view against a bean of the same name in the Spring context
net.shibboleth.ext.spring.velocity.VelocityViewResolver – the most commonly used, supports the Velocity template engine used in other places in the IdP
org.springframework.web.servlet.view.InternalResourceViewResolver – a fallback that relies on JSP templates, mainly used for the IdP’s status page
Virtually of the views supplied with the software are Velocity templates (with a “vm” extension). A handful are installed by default because they are core functionality and not installed by a specific Module. The rest are installed as specific modules that manage them are enabled. For example the “login form” consists of a couple of views that are actually part of the Password login flow, which is enabled by default.
When Spring looks for a view template, it will search for one with the “vm” extension in the location(s) specified by the idp.views property, and if it can’t locate one, it will search inside the war within WEB-INF/jsp for a template with the “jsp” extension and run that instead.
For detailed information on the Velocity-related content of the views, see the VelocityVariables topic.
Look and Feel
The main purpose of the supplied templates is to define the necessary form submission / link interface contracts required for proper functionality of the various features. In some cases, the views render information from the IdP to the user (e.g., the Consent features) while in others they merely collect information from the user. Logout is the most complex case, and has a bit of both.
The actual look and feel of the views is not in general expected to meet anybody’s needs in particular. Deployers are not only encouraged but expected to change them fairly extensively in most cases. The goal of the installation process is to ensure that functionality is usable and can be tested or demonstrated before the work of aligning the look and feel to meet local expectations is done.
Defaults aside, do NOT use the delivered style sheet or image filenames in production, and always adjust the relevant values in messages/messages.properties to reflect your own chosen names for these files (or replace the use of the properties entirely):
idp.css = /css/osu.css
idp.logo = /images/osu-logo.png
While the original style sheets and images will not be overwritten by upgrades, the properties that refer to them could have their default values changed, which could alter the appearance of your pages if you haven’t defined values for them yourself to supplant the system values.
A sample logo is installed to edit-webapp/images/placeholder.png and is referenced by default via the message property idp.logo
All views link to a style sheet installed to edit-webapp/css/placeholder.css; this is referenced by default via the message property idp.css
Upgrades are meant to be safe, but prior to V4.2, the default CSS and logo files (which were strictly provided as examples) have been overwritten during upgrades. This is no longer true, but please heed the caution box above: do NOT rely on the default filenames for your “final” configuration. Even if you make use of the provided CSS, copy it to a filename of your own and set the idp.css property to switch to it.
Content Security Policy
This section refers to features added in V5.1.
The IdP has generally made limited use of CSP, which is a quite complex specification designed to allow web servers to signal to clients to disallow various sorts of content that attackers may be able to inject into pages using various exploits. V5.1 has introduced a number of changes to the default view templates, and adds a number of built-in objects to the Spring environment and to the views to facilitate relatively simple CSP additions to views to lock them down without requiring the very complex step of exbedding and expressing inline event handlers as declarative script blocks.
Despite the conventional wisdom, in fact the use of Javascript to install and respond to events is extremely non-portable, and that “recommended” technique in practice requires the adoption of JQuery or similar libraries to achieve portability. While we do rely on JQuery for logout status reporting (which itself is not really advisable to expose to end users), we have not adopted it across the views and believe the low level of complexity and releatively small usage of Javascript doesn’t warrant it.
Instead, we have altered the default views to exploit the new CSP objects and demonstrate their usage. In the event that deployers need to make changes to the Javascript itself, it should be generally obvious how to make those changes without losing the protection afforded by CSP.
“Root” Declarations
At the most general/top level, there was already a property defined (idp.csp) to control the “default” CSP header applied to most of the responses issued by the IdP, with the exception of the logout views. This is because most (thought not all) of the expected default rules don’t apply to the logout views due to their extensive use of frames and complex Javascript that (being generated in loops) can’t easily be hashed.
Prior to V5.1, the default value of the idp.csp property was set to frame-ancestors 'none'
, essentially just blocking frames. V5.1 adds other CSP declarations that should generally be risk-free to apply and this was done by default, so it applies to any systems not overriding the property. The default rules are intended to work regardless of (expected) changes to the views and do not specifically disallow any Javascript or CSP behavior.
Use in OpenSAML Binding Templates
While many deployers may override them, by default the templates used to carry SAML messages in forms for the non-redirect-based bindings are internal to the OpenSAML library. To allow use (or non-use) of CSP with the default templates, the idp.encoders.cspEnabled property was added to control this. It does default to true, so has to be set to false by hand to disable the new feature. Depending on how much of the templates are overridden, the property may or may not be applicable or effective.
Use in Views
The bulk of this “feature” has to do with supplying tools to make it practical to markup the use of Javascript in views. To that end, two new Spring beans were added to provide support for generating hashes and nonces:
shibboleth.CSPDigester – this is a StringDigester (that is a Function, with apply() called with a Javascript string as input) and produces a base64-encoded SHA-256 digest suitable for use in CSP’s
unsafe-hashes
“source expression”.shibboleth.CSPNonceGenerator – this is an IdentifierGenerationStrategy (an interface in our API) whose generateIdentifier() method will produce an adequately random value to use as a nonce.
Hash Example
Hashes are the “strongest” rule possible because they specifically allow a single script that hashes to the matching value. They work well when the content of the script is maintained within a view, and in particular for simple one-liner event handlers for onLoad or onClick events. It is a simple matter (and is demonstrated in the new default views in various places) to assign a Javascript string to a Velocity variable, and apply the digester to produce the hash needed to generate the header.
This example comes from the default login.vm template:
#set ($onClick = "this.childNodes[0].nodeValue='#springMessageText('idp.login.pleasewait', 'Logging in, please wait...')'") $response.addHeader("Content-Security-Policy", "script-src-attr 'unsafe-hashes' 'sha256-$cspDigester.apply($onClick)'")
The particular rule would generally be “script-src-attr” when the script is used in an HTML attribute, or “script-src-elem” in the less common case of a <script>
element. (Generally nonces work better for script blocks.)
As the example shows, changing the script in some way would only require changing the text in the #set macro, because the hash is computed against whatever is found in that variable. Later on the variable would be used to embed the script into the desired spot in the template.
Nonce Example
When hashes are too unwieldy to use, nonces are usually the best fallback, and work well for scripts, particularly external ones. Nonces are generated and then added to both a header at the top of a view and in a nonce
attribute in the <script>
element:
#set ($nonce = $cspNonce.generateIdentifier()) $response.addHeader("Content-Security-Policy", "script-src-elem 'nonce-$nonce'") ... <script nonce="$nonce"><!-- #include("client-storage/local-storage-read.js") // --></script>
The main advantage of nonces is they are invariant to the script itself so rarely need to be modified.
Generally it’s fine to use the same nonce value in multiple places in a view, but it’s also a simple enough matter to produce multiple values if desired.