Security Best Practices

Security Best Practices

This section describes a number of steps which should be taken to ensure that security best practices are followed and enforced.

Upgrade to WSS4J 2.3.x or 2.2.x

The 1.5.x and 1.6.x series of releases of WSS4J are deprecated. You should switch to either 2.3.x or the latest 2.2.x release as a matter of priority, as these branches contain up to date security fixes.

Upgrade to the latest minor release as soon as possible

You should always upgrade to the latest minor release in a timely manner, in order to pick up security fixes.

Use WS-SecurityPolicy to enforce security requirements

WSS4J can be used with a web services stack such as Apache CXF or Apache Axis in one of two ways: either by specifying security actions directly, or via WS-SecurityPolicy. WS-SecurityPolicy is a much richer way of specifying security constraints when processing messages, and gives you more automatic protection against various attacks then when configuring via security actions. See for example, this blog post on XML signature wrapping attacks. Therefore, you should always try to use WSS4J with a WS-SecurityPolicy requirement.

Use RSA-OAEP for the Key Transport Algorithm

WSS4J supports two key transport algorithms, RSA v1.5 and RSA-OAEP. A number of attacks exist on RSA v1.5. Therefore, you should always use RSA-OAEP as the key transport algorithm, and enforce this decision. For WS-SecurityPolicy, this means to avoid using any AlgorithmSuite that ends with "Rsa15" (e.g. "Basic128Rsa15").

For the "Action" based approach, there are different ways of enforcing that RSA v1.5 cannot be used for key transport depending on the version of WSS4J. From WSS4J 2.0.0, it is not allowed by default and no action is required. If you wish to allow it, then you must set the WSHandlerConstants.ALLOW_RSA15_KEY_TRANSPORT_ALGORITHM property to "true". For WSS4J 1.6.x, the RSA v1.5 key transport algorithm is allowed by default. In this case, you should explicitly configure WSHandlerConstants.ENC_KEY_TRANSPORT ("encryptionKeyTransportAlgorithm") to be "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p". This latter point requires the web services stack to set this property on the Request (it is known that Apache CXF does this).

Avoid using a cbc Symmetric Encryption Algorithm

There are some attacks that exploit the "cbc" mode of a Symmetric Encryption Algorithm. WSS4J has support for "gcm" mode algorithms as well. This can be specified via WSHandlerConstants.ENC_SYM_ALGO ("encryptionSymAlgorithm"), for example to "http://www.w3.org/2009/xmlenc11#aes128-gcm".

Specify the symmetric encryption algorithm on the receiving side

Choosing "gcm" for what you send is only half of it. An EncryptedData element states its own algorithm, and nothing in the message binds the content encryption key to the algorithm it was meant for, so the sender of a message - or anyone who can modify one in flight - decides which algorithm the recipient uses to decrypt it. Relabelling an aes256-gcm EncryptedData as aes256-cbc leaves a key of exactly the length either algorithm requires, so the key length check does not notice, and the recipient decrypts in an unauthenticated mode whose padding is a known oracle.

Tell the receiving side what to expect, and it will reject anything else:

  • WS-SecurityPolicy states it in the AlgorithmSuite, which is the reason to prefer that approach.
  • For the streaming code, WSSSecurityProperties.setEncryptionSymAlgorithm.
  • For the DOM code, WSHandlerConstants.ENC_SYM_ALGO ("encryptionSymAlgorithm") - the same property that chooses the outbound algorithm. As with the signature and key transport algorithms below, it only takes effect on the receiving side where the web services stack sets an AlgorithmSuite on the RequestData: Apache CXF’s WSS4JInInterceptor does, a stack calling the engine directly does not.

Where the expected algorithm genuinely is not known in advance, sign the EncryptedData elements instead: a signature covers the Algorithm attribute, so a relabelled element no longer verifies. On the DOM code WSHandlerConstants.REQUIRE_SIGNED_ENCRYPTED_DATA_ELEMENTS ("requireSignedEncryptedDataElements") requires every EncryptedData to sit in a signed subtree. The streaming code does not implement that tag.

Use Subject DN regular expressions with chain trust

WSS4J 1.6.7 introduced the ability to specify regular expressions on the Subject DN of a certificate used for signature validation. It is important to add this constraint when you are supporting "chain trust", which is where you are establishing trust in a certificate based on the fact that the Issuer of the certificate is in your trust store. Otherwise, any certificate of this issuer will pass trust validation. See here for more information.

Restrict the audience of a SAML assertion

A SAML assertion says who the subject is, not who it was meant for. An assertion that one of your services issued, or accepted, is a perfectly valid assertion anywhere else that trusts the same issuer, so a service that does not state which audience it is willing to accept will honour an assertion minted for a different one. Where the assertion carries an AudienceRestriction condition, WSS4J checks it against the list of audience URIs the receiving side supplies - and only against that list. The list is empty unless it is set, and an empty list means the condition is not checked at all.

There is no configuration tag for this: set it programmatically, with RequestData.setAudienceRestrictions for the DOM code or WSSSecurityProperties.setAudienceRestrictions for the streaming code. A web services stack may do it for you - Apache CXF supplies the request URL and the service QName by default for SOAP endpoints, through SecurityConstants.AUDIENCE_RESTRICTIONS, and validates against them unless security.validate.audience-restriction is turned off - but a deployment calling the WSS4J engine directly gets nothing unless it sets the list itself.

Specify signature algorithm on receiving side

When not using WS-SecurityPolicy (see point above about favouring the WS-SecurityPolicy approach), you should specify a signature algorithm to use on the receiving side. This can be done via WSHandlerConstants.SIG_ALGO ("signatureAlgorithm"). Setting this property to (e.g.) "http://www.w3.org/2000/09/xmldsig#rsa-sha1" will ensure that the signature algorithm allowed is RSA-SHA1 and not (e.g.) HMAC-SHA1. WSHandler itself never calls decodeAlgorithmSuite internally, so this property only takes effect on the receiving side if the web services stack explicitly invokes WSHandler.decodeAlgorithmSuite (or otherwise sets an AlgorithmSuite on the RequestData) before processing the security header — Apache CXF’s WSS4JInInterceptor does this. Stacks that call the WSS4J engine directly without doing so get no enforcement from this property. See also the previous point about setting the key encryption transport algorithm.