SOAP services still carry payments, benefits claims, healthcare messages and government data exchanges, and their security often rests on XML signatures that are verified correctly but checked against the wrong element. WS-Security adds tokens, signatures and encryption inside the SOAP envelope, which is powerful but complex: a service can confirm that a signature is valid and still act on unsigned content an attacker has moved into place, the attack known as XML signature wrapping. This guide covers the WS-Security message model, how to verify signatures safely, XML parser hardening, authentication and authorisation for SOAP clients, and how to monitor and test legacy services. It is for developers and architects maintaining SOAP integrations, API security teams and assessors reviewing them.
Key takeaways
- A valid signature is not the same as signed content. The application must confirm that the element it processes is the exact element the signature covers, by identity and position, not by element name.
- Harden the XML parser before anything else. External entities, DTDs and entity expansion must be off; XXE and XML bombs remain the most common SOAP findings.
- Use the platform's secure validation mode. The JDK's XML signature API enforces unique reference IDs, bans XSLT and weak algorithms and limits references by default; do not switch it off.
- Bind authentication to the message. Use mutual TLS or signed tokens, sign the timestamp and body together, and reject replays within the validity window.
- Signing keys are tier-zero non-human credentials. Whoever holds the signing key can mint any message the service will trust.
The WS-Security message model
OASIS Web Services Security: SOAP Message Security 1.1.1 is an OASIS Standard dated 18 May 2012; it consolidates corrections to WS-Security 1.1 and adds no new features. Security information travels in a wsse:Security header addressed to a recipient by SOAP actor or role. Each new sub-element is prepended, so the header records the order in which signing and encryption happened.
| Element | Purpose | Security note |
|---|---|---|
wsse:Security | Container for tokens, signatures and encryption data for one recipient | Several may exist, one per actor or role; process only the one addressed to you |
wsu:Timestamp (wsu:Created, wsu:Expires) | Message freshness | Must be signed; enforce a short expiry and allow only small clock skew |
wsse:UsernameToken | Username and optional password or digest | Plain-text passwords need TLS; digests require the server to hold a usable password; prefer certificates or SAML |
wsse:BinarySecurityToken | X.509 certificate or other binary token | Validate chain and revocation; do not trust a certificate simply because it is in the message |
wsse:SecurityTokenReference | Points to the token used for a signature or key | Signing the reference does not sign the token unless the STR Dereference Transform is used |
ds:Signature | XML Signature over selected parts of the envelope | Exclusive Canonicalization is strongly recommended; list signed elements explicitly |
xenc:EncryptedKey, xenc:ReferenceList, wsse11:EncryptedHeader | XML Encryption of body parts and headers | The Envelope, Header and Body elements themselves are never encrypted |
wsse11:SignatureConfirmation | Response proves which request signature it answers | Initiators should reject responses that omit it or carry a mismatched value |
The specification itself notes that the security model alone is subject to multiple attacks, that wsu:Id values must be unique within a document, and that applications should not rely on schema validation alone to enforce that uniqueness. Those two points are exactly where wrapping attacks live.
How XML signature wrapping works and how to stop it
An XML Signature does not sign "the message". It signs one or more references, usually by ID (URI="#body-1") or XPath. A verifier finds the referenced element, canonicalises it, checks the digest and then checks the signature over SignedInfo. Wrapping exploits the gap between that lookup and the application's own logic: the attacker keeps the originally signed element somewhere the verifier will still find it, for example inside a new header or wrapper element, and places a different, unsigned element where the application expects the body. Signature verification passes, and the business logic processes the attacker's content. The same technique has repeatedly affected SAML implementations, which use the same signature standard.
The W3C XML Signature Best Practices Working Group Note (11 April 2013) gives the defences. The most relevant for SOAP services are:
| Best practice | What to implement |
|---|---|
| BP 1 and BP 25 | Authenticate the signature before dangerous processing, and verify before schema validation or use a parser that does not change the DOM destructively |
| BP 2 | Establish trust in the verification key: pinned certificate or trusted chain with revocation checks, never "any key in KeyInfo" |
| BP 3, BP 5, BP 9 | Avoid XSLT and limit XPath transforms; limit the number of transforms per reference |
| BP 7, BP 8 | Avoid or limit ds:RetrievalMethod; block external and network references |
| BP 10, BP 12 | Use library interfaces that return what was signed, and drive the application from that returned content |
| BP 13, BP 14 | Verify that the signature covers exactly the intended elements; when checking a reference, check both the element's name and its position, not just its name |
| BP 15 | Unless impractical, sign all parts of the document that matter, which limits substitution and wrapping |
Practical verification rules for a SOAP endpoint
- Know your expected references: the policy should state that the
soap:Body, thewsu:Timestampand any security-relevant headers (for example WS-AddressingToandAction) must be signed. - Resolve by position as well as ID: after verification, confirm that the signed body is the single
soap:Bodychild of thesoap:Envelopethat the application will read, and that the signed timestamp is in thewsse:Securityheader for your role. - Reject duplicate IDs: fail closed on any repeated
wsu:IdorIdattribute in the document. - Process what was verified: pass the verified node, not a fresh XPath lookup by name, to the business logic.
- Reject unexpected structure: extra Body children, multiple
wsse:Securityheaders for the same role, or signatures over elements you did not expect.
Prefer a maintained WS-Security stack configured with a security policy (WS-SecurityPolicy) over hand-written verification code. The configuration of required signed parts is what the wrapping defence depends on.
Platform controls: secure validation and parser hardening
- Java XML Digital Signature API: Oracle documents that secure validation mode is enabled by default. It forbids XSLT transforms, limits
SignedInfoorManifestreferences to 30 and transforms per reference to 5, forbids MD5 and SHA-1 signatures and MD5 MACs, ensures reference IDs are unique to help prevent signature wrapping, forbidshttp,httpsandfilereference URIs, prevents chainedRetrievalMethodelements, and rejects RSA or DSA keys under 1024 bits and EC keys under 224 bits. Settingorg.jcp.xml.dsig.secureValidationtofalsedisables all of this; treat that setting as a finding. Thejdk.xml.dsig.secureValidationPolicysecurity property can tighten the policy further. - XML parsers: disable DTDs and external entity resolution, and enforce entity expansion, depth and size limits. OWASP ASVS 5.0 V1.5.1 requires restrictive XML parser configuration. See the XXE Guide for parser settings per language.
- Consistent parsing: if a gateway and the service parse the same message with different libraries, they may disagree about which element is the body. ASVS V1.5.3 asks that parsers for the same data behave consistently. See the HTTP Parameter Pollution and Parser Differential Guide.
- Schema validation: OWASP's Web Service Security Cheat Sheet requires SOAP payloads to be validated against their XSD, with maximum length and character set for every parameter and allow-list patterns for fixed formats. Validate after signature verification, and do not rely on the schema for ID uniqueness.
- Size and complexity limits: limit message size, nesting depth, element name length and processing time, and protect against recursive payloads and entity expansion, as the cheat sheet's XML denial of service rules require.
- Attachments: scan files carried as SOAP attachments inline before they are written to disk.
Authentication, authorisation and service identity
| Option | Strength | Weakness | Use when |
|---|---|---|---|
| HTTP Basic over TLS | Simple | OWASP does not recommend it: the secret travels in every request | Only as a transitional measure, with IP restriction and vaulted credentials |
| UsernameToken | Message-level, survives intermediaries | Password-based; digest form needs server-side access to the password | Legacy partners that cannot do certificates |
| Mutual TLS | Strong client authentication at transport level; recommended by OWASP where appropriate | Terminates at the first TLS endpoint; identity must be passed on safely | Point-to-point B2B and service-to-service |
| X.509 signature (BinarySecurityToken) | Message-level integrity and sender authentication | Wrapping risk if verification is careless; key management burden | Messages that cross intermediaries or must be non-repudiable |
| SAML token in WS-Security | Federated identity, end-user context | Assertion signature and replay handling must be correct | Calls on behalf of users across organisations |
- Authorise every operation: the cheat sheet requires checking that the client may call the method and access the requested resource on every request, and separating administrative functions from the service itself.
- Replay protection: sign the timestamp, enforce short validity, and cache nonces or message IDs for the validity window. ASVS V6.8.3 requires SAML assertions to be used only once within their validity period.
- Service accounts and certificates: SOAP integrations are classic long-lived non-human identities: partner certificates, keystore passwords and gateway accounts that outlive the people who set them up. Inventory them with owners and expiry dates. See the Machine Identity, PKI and Certificate Lifecycle Guide.
- Signing key protection: keep signing keys in an HSM or managed key store and rotate them on suspicion of compromise. The SolarWinds Golden SAML attack showed what happens when a token-signing key is stolen: the attacker's forged tokens carried perfectly valid signatures.
Decision guidance: maintain, wrap or migrate
- Maintain in place when the partner ecosystem is fixed (for example a regulated message exchange) and the stack is supported: apply the verification rules above and add monitoring.
- Front with a gateway when the service code is hard to change: an XML gateway or API gateway can enforce schema, size limits, XXE protection and mutual TLS. Make sure the backend does not trust anything the gateway did not verify, and that both parse identically.
- Migrate to REST or event APIs when partners can move, but plan the identity mapping carefully; replacing a signed SOAP message with a bearer token can weaken non-repudiation. See the Secure API Design Guide.
- Small organisations with one or two SOAP integrations get most value from parser hardening, mutual TLS and a supported library. Large organisations with many partners should centralise policy in a gateway, keep a register of partner certificates, and test wrapping resistance as part of release testing.
Rolling out hardening on existing SOAP services
- Inventory: list SOAP endpoints, WSDLs, partners, authentication method, signing and encryption policy, libraries and versions.
- Hide what need not be public: do not publish WSDLs and schemas on internet-facing endpoints unless partners need them.
- Harden parsers: turn off DTDs and external entities; run XXE and entity expansion tests in a test environment first.
- Tighten signature policy: require signed body and timestamp, confirm secure validation is on, reject duplicate IDs. Deploy to a test partner, then one production partner, before all.
- Upgrade authentication: move partners from Basic or UsernameToken to mutual TLS or signed messages with a dated deprecation plan.
- Monitor and test: add the detections below and include signature wrapping, XXE and replay cases in authorised testing.
Rollback: keep the previous policy configuration per partner so a partner that breaks can be moved back while it fixes its client; never roll back parser hardening.
Detection and measurement
- Log: signature verification failures with the reason, duplicate ID rejections, timestamp and replay rejections, schema violations, oversized messages and parser errors mentioning entities or DTDs.
- Alert: any message containing a DOCTYPE, any duplicate
wsu:Id, signatures that cover unexpected elements, and spikes in verification failures from one partner. - Monitor keys: certificate expiry, use of a partner certificate from a new network, and changes to trust stores.
Metrics:
- Percentage of SOAP endpoints with DTD and external entity processing disabled and tested.
- Percentage of partners on mutual TLS or signed messages rather than Basic or password tokens.
- Number of services with secure validation disabled or custom verification code.
- Partner certificates expiring within 30 days, and certificates without a named owner.
- Wrapping and replay test cases passing in each release.
Framework mapping
| Framework | Reference | Relevance |
|---|---|---|
| OWASP ASVS 5.0 | V1.5.1, V1.5.3, V4.1.5, V6.8.2, V6.8.3, V12.3.5 | XML parser hardening, consistent parsing, per-message signatures, assertion signature validation, replay, service-to-service authentication |
| MITRE CAPEC | CAPEC-475, CAPEC-197, CAPEC-221, CAPEC-231, CAPEC-250 | Signature spoofing by improper validation; entity expansion; external entities; oversized payloads; XML injection |
| MITRE ATT&CK | T1190, T1606.002, T1557 | Exploiting exposed services; forged SAML tokens; adversary-in-the-middle |
| NIST SP 800-53 Rev. 5 | SC-8, SC-13, SC-23, IA-9, SI-10, AU-12 | Transmission integrity, cryptography, session authenticity, service authentication, input validation, auditing |
| NIST CSF 2.0 | PR.DS-02, PR.AA-03 | Data in transit; authentication of users and services |
Common failure patterns
- Lookup by element name after verification: the verifier checks the signed body inside a wrapper, the application reads the first
Bodyit finds. - Trusting the key in the message: any certificate in
KeyInfoor aBinarySecurityTokenis accepted without checking it against a trust store or partner pin. - Unsigned timestamp or no replay cache, so a captured message can be replayed for hours.
- DTDs left on because an old partner sends a DOCTYPE, keeping XXE and entity expansion open for everyone.
- Secure validation switched off to accept a legacy SHA-1 signature, which also removes the duplicate ID check.
- Gateway and backend disagree: the gateway validates one interpretation of the message and the backend parses another.
Lessons from incidents
Most public signature wrapping cases have been in SAML single sign-on rather than in SOAP services, because SAML endpoints are internet-facing and widely tested, but the flaw and the fixes are the same standard and the same libraries. The SolarWinds Golden SAML compromise is the complementary lesson: even perfect verification fails when the signing key itself is stolen, so key custody in an HSM, monitoring of signing activity and a rehearsed rotation plan belong in the same programme. See the SAML Security Guide for the assertion-specific controls.
Practitioner checklist
- Disable DTDs and external entities on every XML parser and test for XXE and entity expansion.
- Require the SOAP Body, Timestamp and security-relevant headers to be signed, and reject messages that do not match the expected signed parts.
- Check signed elements by position as well as ID, reject duplicate IDs, and process only the verified node.
- Keep JDK secure validation on (or the equivalent in your stack) and ban XSLT, external references, MD5 and SHA-1.
- Validate the signing certificate against a trust store or partner pin with revocation checking.
- Enforce short timestamps and a replay cache for the validity window.
- Move partners from Basic and password tokens to mutual TLS or signed messages.
- Keep signing keys in an HSM, inventory partner certificates with owners and expiry, and rehearse rotation.
- Validate payloads against tight XSDs after signature verification, with size and depth limits.
- Log and alert on DOCTYPEs, duplicate IDs, verification failures and replays.
Standards and references
- OASIS: Web Services Security SOAP Message Security 1.1.1
- W3C: XML Signature Best Practices
- OWASP: Web Service Security Cheat Sheet
- OWASP: XML External Entity Prevention Cheat Sheet
- OWASP: XML Security Cheat Sheet
- Oracle: Java XML Digital Signature API Overview and Tutorial
- OWASP: Application Security Verification Standard 5.0
- MITRE CAPEC: Signature Spoofing by Improper Validation
Related NHI Mgmt Group resources: XXE Guide · SAML Security Guide · Digital Signatures Guide · API Security Guide