Part 5: WS-Security and Authentication

Security in SOAP

REST APIs secure communication at the transport layer — use HTTPS and attach an API key or Bearer token to the HTTP headers. The security lives outside the message.

SOAP adds another layer: WS-Security (Web Services Security, published by OASIS in 2004) allows you to secure the message itself — independent of transport. You can sign a SOAP body so the recipient knows it has not been tampered with. You can encrypt part of the body so only the intended recipient can read it. The signed or encrypted payload is valid regardless of whether it travelled over HTTPS, JMS, or any other transport.

This distinction matters in regulated industries. A banking integration I worked on required both TLS and message signing. The bank's system verified the XML signature on every request because they needed a cryptographic proof of message integrity that could not be stripped by a proxy or load balancer.

This part covers the most common WS-Security patterns you will encounter and implement with Python.

WS-Security Overview

WS-Security extends the SOAP Header with security-related information. The standard defines three core mechanisms:

  1. UsernameToken — username and password (plaintext or hashed) in the header

  2. Timestamp — a created/expires window to prevent replay attacks

  3. X.509 Certificate Tokens — digital signature and/or encryption using public-key cryptography

These can be combined. A common enterprise configuration is: UsernameToken + Timestamp for authentication, plus TLS for transport confidentiality.

A WS-Security header looks like this:

<soap:Header>
    <wsse:Security
        xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
        xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
        soap:mustUnderstand="1">

        <!-- Timestamp to prevent replay attacks -->
        <wsu:Timestamp wsu:Id="TS-1">
            <wsu:Created>2026-03-14T10:00:00Z</wsu:Created>
            <wsu:Expires>2026-03-14T10:05:00Z</wsu:Expires>
        </wsu:Timestamp>

        <!-- Username and password credentials -->
        <wsse:UsernameToken wsu:Id="UT-1">
            <wsse:Username>api_user</wsse:Username>
            <wsse:Password
                Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">
                <!-- Base64(SHA1(nonce + created + password)) -->
                3V5JQJR...
            </wsse:Password>
            <wsse:Nonce EncodingType="...#Base64Binary">abc123</wsse:Nonce>
            <wsu:Created>2026-03-14T10:00:00Z</wsu:Created>
        </wsse:UsernameToken>

    </wsse:Security>
</soap:Header>

WS-Security with zeep

zeep's wsse module provides ready-to-use WS-Security plugins that attach to the transport layer.

UsernameToken — Password Text

The simplest form: sends the username and password in plaintext. Only use this over TLS (HTTPS).

UsernameToken — Password Digest

A more secure option: sends Base64(SHA1(nonce + created + password)) instead of the plaintext password. The server computes the same digest and compares. This adds some protection even without TLS, but cannot replace encryption.

Internally, zeep generates a random nonce, a current timestamp, computes the digest, and inserts a wsse:UsernameToken into the Security header automatically.

Adding a Timestamp

Timestamps declare a validity window for the message. The receiving service rejects requests outside that window, preventing replay attacks.

Message Signing with X.509 Certificates

Message signing is common when integrating with banking and government systems. The client signs the SOAP body (and optionally headers) with its private key. The server verifies the signature using the client's public certificate.

You need:

  • A client private key (.pem or .pfx)

  • A client certificate (.pem) — the public part, shared with the server

  • The server's public certificate (for encrypting or verifying server responses)

zeep will sign the SOAP body using the private key and embed the certificate reference in the Security header. The server uses the public certificate to verify the signature.

Combined: Signature + UsernameToken

When a service requires both authentication credentials and a signed message:

Implementing WS-Security in a spyne Server

Requiring UsernameToken on the Server

spyne does not validate WS-Security out of the box. You implement it as an event handler that runs before the method call:

Register the handler in the application:

Configuring TLS

For production, always run SOAP services behind TLS. With Flask, use ssl_context:

In production deployments, terminate TLS at a reverse proxy (nginx, AWS ALB, etc.) and let Flask run plain HTTP on localhost. This is the standard pattern.

Storing Credentials Securely

Never hardcode credentials in source code. For SOAP client credentials, use environment variables or a secrets manager:

For certificates in cloud environments, store PEM content as secrets and write to a temp file at startup:

Security Summary

Mechanism
Provides
Requires
zeep support

PasswordText over HTTPS

Authentication + confidentiality

TLS

UsernameToken(use_digest=False)

PasswordDigest

Authentication + replay protection

Nothing extra

UsernameToken(use_digest=True)

Timestamp

Replay attack prevention

Nothing extra

UsernameToken(timestamp_token=True)

X.509 Signature

Message integrity + non-repudiation

Certificates

Signature(...)

X.509 Encryption

Message confidentiality (transport-independent)

Certificates

zeep[xmlsec] needed

For the vast majority of enterprise SOAP integrations I have worked on: TLS + UsernameToken with PasswordDigest is sufficient and widely supported. Reserve X.509 signing for integrations where the requirement is explicitly specified in the service contract.

What's Next

You can now:

  • Add WS-Security headers to zeep clients (UsernameToken, Signature)

  • Validate WS-Security tokens in spyne servers

  • Protect against replay attacks with timestamps

  • Manage certificates and credentials securely

In Part 6, we cover SOAP Faults — how to define and raise structured errors in spyne services and how to handle them gracefully in zeep clients.

Last updated