Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

The term "persistent" refers to the fact that it's not a per-session identifier, but a stable value; in addition, SAML persistent identifiers have very particular properties, notably they're intended to be opaque and "pairwise". The latter means that for the most part every SP receives a different value for the same user. The term "pairwise" is the more typical way of describing this whole concept today, so the Format constant is unfortunate and confusing.

You should consider carefully whether this kind of identifier meets your needs. They can be difficult to deal with, they work very poorly with a wide variety of applications, and they are often more trouble than they're worth. Be cautious, and don't commit to supporting something you don't want to have to support.

There were some internal code changes in V4 but the configuration is designed to be backward-compatible. The most significant change is that a public PairwiseIdStore interface now exists to support third party extension of the way these values are produced and managed if it becomes necessary.

...

Warning

It's come to light that at least some (perhaps many, or even most) applications do not support case-sensitive handling of identifiers. This SAML format is explicitly defined to be case-sensitive, but it is much, much wiser not to expect rely on that. Older versions of the software generate identifiers with the Base64 encoding and this is much less safe, so if you're not already supporting identifiers produced by them, you would be wise to generate the values using a Base32 encoding, which is designed to support case-insensitive applications. New installs include a property explicitly set to produce Base32 values, but upgrades of older configurations will continue to use Base64 for compatibility reasons.

...

Expand
titleComputed IDs

The default PairwiseIdStore implementation is a hash/digest-based approach called "Computed" that avoids the need for a database to store the IDs, but is incapable of reverse-mapping a given identifier (e.g., as part of a SAML attribute query), or revoking or changing the identifier associated with a subject. Tracking back to a subject for debugging purposes generally involves the use of audit logs rather than direct access to a mapping of users. It's not the best approach in the abstract, but it is much simpler to deploy.

To enable the Computed strategy, you must set additional properties:

  • idp.persistentId.sourceAttribute

    • A list of attributes from which to derive a "source" key for the subject. The key is used as the hash input, and should be a very stable value for each subject and must never be reassigned later to a different subject. This should be a permanent serial number associated by an IDMS to each account, and not a name-based identifier like a login ID or email address. It should also be technology-neutral; using a GUID generated by an Active Directory is a very bad choice that will lead to problems if you ever change directories.

  • idp.persistentId.salt

    • A secret string used as a default salt when hashing the subject key derived from the property above. This is required to prevent trivial attacks to determine the identifier for a given subject, and must be kept secret. Note that leading or trailing whitespace is not trimmed from the property, though using whitespace in the salt is not advisable.

  • idp.persistentId.encodedSalt

    • If your salt value contains special characters that Spring won't accept safely, you can work around this by base64-encoding the salt you want to use, and specifying the encoded version in this property instead of the previous property. Do not set both.

    idp.persistentId.encoding

    • Controls the encoding of the generated hash value. Defaults to BASE64 if not set, but new installations set this property to BASE32 to produce values without mixed case.

The attribute used as the source key need not be released (in the sense of an attribute filter policy) to the SP.

Dynamic Salt 4.3

V4.3 introduces the option to configure a dynamic salt source using a BiFunction (a Java function object that takes two inputs). If defined, the function will override the value of the salt to use for a request, or if it returns null will suppress generation of a value entirely. This works in combination with the Sparse Override map desribed in the next section (i.e., the map overrides the BiFunction which overrides the default). It is also now legal to only define the BiFunction and omit the global salt if desired.

The BiFunction is of type BiFunction<ProfileRequestContext,PairwiseId> but in this release for design reasons the first argument will always be null. This will be addressed in V5 but the signature is defined this way so the same code can be used now and later by checking for null.

The intent is that the second argument will supply details about the request such as the subject and relying party, while the first will (in V5) provide full access to request state. For the time being, accessing the ProfileRequestContext requires workarounds. The usual one is to inject an instance of shibboleth.HttpServletRequestSupplier into the bean. Assuming a script and the “customObject-ref” trick to inject the supplier:

Code Block
prc = custom.get().getAttribute("opensamlProfileRequestContext");

Sparse Overrides

One of the disadvantages of strictly computing IDs is a loss of manageability of the values, particularly the ability to change a value should it become compromised. The IdP includes a feature allowing fine-grained override of the salt value used to generate IDs for specific users and/or relying parties, by means of a Java Map bean, which can be declared in saml-nameid.xml, and by default is named shibboleth.ComputedIdExceptionMap

The Java type of the object is a mouthful: Map<String,Map<String,String>> (i.e., it's a string-keyed map whose values are themselves maps). It's easier to grasp this in practice in the example below.

The primary keys are the names of subjects/users, or an asterisk (*) to signify a wildcard rule.

The values are maps of Relying Party names to salt values. These keys are the names of relying parties or an asterisk (*) as a wildcard, and the values are either a substitute salt string to use, or can be null to block the generation of an ID altogether.

One use for this feature is to maintain an old salt value for a legacy service while relying on a new value for everybody else:

Overriding salt for a single SP
Code Block
languagexml
<util:map id="shibboleth.ComputedIdExceptionMap">
	<entry key="*"> <!-- all users -->
		<map>
			<entry key="https://legacysp.example.org/sp" value="legacysalt" />
			<entry key="https://invalid.example.org/sp">
				<null/>	<!-- blocks generation of a value for this SP -->
			</entry>
		</map>
	</entry>
</util:map>

...