Open Source API Gateway
3. Security

wsSecurity

Version

Owns the WS-Security (wsse:Security) header of a SOAP message: the validate list consumes the security the peer sent, the secure list applies fresh security for the next hop. Both lists are optional and run in the order they are written; the whole validate list runs first, and the inbound header is removed at that boundary before secure creates a new one. With no validate list the header is emptied of the tokens the peer sent instead of being forwarded unchecked; what survives is only what asserts nothing on its own — its wsu:Timestamp, since a secure signature may cover it, and its XML Encryption material (xenc:EncryptedKey and xenc:EncryptedData) for as long as encrypted content is still in the message, since dropping key material while forwarding the ciphertext would leave a message nobody downstream can read. Header blocks targeted at a different actor are left untouched.

Signing and encrypting draw on opposite stores: a signature uses the keystore's private key and is verified against the truststore, while encrypt uses a truststore certificate (the recipient's) and decrypt the keystore's private key.

Direction is not part of this element: nest it in request or response to say which message it applies to. A gateway commonly validates what a client sent and re-secures for the backend in one element, and mirrors that on the way back. Use two elements with a transformation between them when the body has to change between validating and re-securing.

A failing check answers with a soap:Fault matching the envelope version of the offending message, carrying the WS-Security fault code (wsse:FailedAuthentication, wsse:FailedCheck, and so on); a body that is not SOAP at all answers with Problem Details, since no fault envelope can be produced for it.

Apache CXF compatibility

Interoperability is tested with Apache CXF. Membrane supports SOAP 1.1 and SOAP 1.2 in both directions: it can secure requests for CXF and validate its secured responses, or validate CXF requests and secure responses for CXF. Compatible security features include:

  • Signing the SOAP body and timestamp with RSA-SHA256 and SHA-256 digests, using a wsse:SecurityTokenReference pointing to an embedded certificate.
  • UsernameToken authentication with PasswordText (Membrane passwordType: PLAIN_TEXT).
  • Encrypting SOAP body content with AES-256-GCM and XML Encryption 1.1 RSA-OAEP key transport using SHA-256 and MGF1-SHA256, with a certificate thumbprint key identifier.
  • Signing the body and timestamp before encrypting the body.

For signatures, add securityTokenReference to Membrane's outbound signature and configure WSS4J's signature key identifier as DirectReference. On Membrane, secure in the order timestamp, signature, then optionally encrypt; validate in the order decrypt when encrypted, timestamp, then signature. Configure WSS4J with the action string Signature Timestamp or Signature Timestamp Encrypt to place the timestamp before the signature in the security header. Require signature coverage of both body and timestamp, and body content encryption when using encryption, on the receiving side.

Example Configuration

<pre><code>
api:
port: 2000
flow:
- request:
- wsSecurity:
keystore:
location: signing.p12
password: secret
truststore:
location: partner-ca.p12
password: secret
validate:
- decrypt:
requiredReferences:
- by: BODY
- timestamp:
clockSkew: PT1M
- usernameToken:
username: ${property.apiUser}
password: ${property.apiPassword}
- signature:
requiredReferences:
- by: BODY
- by: TIMESTAMP
- by: USERNAME_TOKEN
secure:
- timestamp:
ttl: PT5M
- signature:
references:
- by: BODY
- by: TIMESTAMP
- xpath: //*[local-name()='order']
- encrypt:
recipientAlias: backend
references:
- by: BODY
</code></pre>
<p>The placement in that example is the lesson, not a detail: <code>encrypt</code> comes last so the
body is signed before it is encrypted, and <code>decrypt</code> comes first so the receiving side
undoes that in reverse. Swap either and the signature is computed over one form of the body and
checked against another.</p>
<pre><code>
 api:
   port: 2000
   flow:
     - request:
         - wsSecurity:
             keystore:
               location: signing.p12
               password: secret
             truststore:
               location: partner-ca.p12
               password: secret
             validate:
               - decrypt:
                   requiredReferences:
                     - by: BODY
               - timestamp:
                   clockSkew: PT1M
               - usernameToken:
                   username: ${property.apiUser}
                   password: ${property.apiPassword}
               - signature:
                   requiredReferences:
                     - by: BODY
                     - by: TIMESTAMP
                     - by: USERNAME_TOKEN
             secure:
               - timestamp:
                   ttl: PT5M
               - signature:
                   references:
                     - by: BODY
                     - by: TIMESTAMP
                     - xpath: //*[local-name()='order']
               - encrypt:
                   recipientAlias: backend
                   references:
                     - by: BODY
 </code></pre>
 <p>The placement in that example is the lesson, not a detail: <code>encrypt</code> comes last so the
 body is signed before it is encrypted, and <code>decrypt</code> comes first so the receiving side
 undoes that in reverse. Swap either and the signature is computed over one form of the body and
 checked against another.</p>

Syntax

wsSecurity:
'$ref': <string>
actor: <string>
keystore: {}
mustUnderstand: <boolean>
secure:
- <secureParts>
truststore: {}
validate:
- <validateParts>
xmlConfig: {}
wsSecurity:
  '$ref': <string>
  actor: <string>
  keystore: {}
  mustUnderstand: <boolean>
  secure:
    - <secureParts>
  truststore: {}
  validate:
    - <validateParts>
  xmlConfig: {}

Attributes

NameRequiredDefaultDescriptionExamples
$reffalse-Reference a component defined under components.-
actorfalse-The SOAP actor (SOAP 1.1) or role (SOAP 1.2) whose wsse:Security header this element owns. When omitted, that is the header addressed to the ultimate receiver, i.e. the one carrying no actor/role attribute. Headers belonging to any other actor are neither validated nor removed.http://example.com/gateway
mustUnderstandfalsetrueWhether the wsse:Security header created by secure carries mustUnderstand, which obliges the next hop to either process the header or answer with a fault instead of ignoring it.-

Child Structure

ElementCardinalityDescription
keystore0..1Loads a private key and its certificate chain from a keystore file, to be presented as this side's identity during a TLS handshake (or, inside wsSecurity, to sign a message). See tutorials/web-services-security/50-Sign-And-Validate-Body.yaml.
secureParts0..*The security applied to the outbound message, in order.
truststore0..1Supplies the CA certificates trusted when validating a peer's certificate chain, for example during a TLS handshake or, inside wsSecurity, when verifying a signature. See tutorials/web-services-security/50-Sign-And-Validate-Body.yaml.
validateParts0..*The checks applied to the inbound wsse:Security header, in order.
xmlConfig0..1Declares additional XML namespace prefixes usable in the xpath attribute of an XPATH reference, in any part. soap, wsse, and wsu are always available, even when this is set.

Can be used in