{"id":2767,"library":"signxml","title":"signxml","description":"signxml is a Python library that implements the W3C XML Signature standard (XMLDSig), used for payload security in standards like SAML 2.0, XAdES, EBICS, and WS-Security. It provides features for signing and verifying XML documents, including support for X.509 certificate chains and XAdES signatures. The library is actively maintained with regular releases, supporting modern Python versions (3.9-3.13+).","status":"active","version":"4.4.0","language":"en","source_language":"en","source_url":"https://github.com/XML-Security/signxml","tags":["xml","signature","security","xades","saml","xmlsec"],"install":[{"cmd":"pip install signxml","lang":"bash","label":"Install signxml"}],"dependencies":[{"reason":"Required for XML parsing, manipulation, and canonicalization, offering superior resistance to XML attacks.","package":"lxml","optional":false},{"reason":"Used for core cryptographic operations including certificate parsing, key processing, and signature validation, replacing older PyOpenSSL functionality.","package":"cryptography","optional":false}],"imports":[{"symbol":"XMLSigner","correct":"from signxml import XMLSigner"},{"symbol":"XMLVerifier","correct":"from signxml import XMLVerifier"},{"note":"signxml explicitly uses and requires lxml.etree for robust XML handling and security; standard library ElementTree may lead to parsing and namespace issues.","wrong":"from xml.etree import ElementTree as ET","symbol":"etree","correct":"from lxml import etree"}],"quickstart":{"code":"from lxml import etree\nfrom signxml import XMLSigner, XMLVerifier, SignatureConfiguration\nimport os\n\n# --- Setup: Generate test certificate and key (requires OpenSSL) ---\n# openssl req -x509 -nodes -subj \"/CN=test\" -days 1 -newkey rsa -keyout privkey.pem -out cert.pem\n# In a real application, you would load these from secure storage.\n\n# Ensure cert.pem and privkey.pem exist in the current directory for this example to run.\nif not (os.path.exists('cert.pem') and os.path.exists('privkey.pem')):\n    print(\"Please generate 'cert.pem' and 'privkey.pem' using OpenSSL as described in the comments.\")\n    exit()\n\ncert = open(\"cert.pem\").read()\nkey = open(\"privkey.pem\").read()\n\n# --- Signing an XML document ---\ndata_to_sign = \"<Test><Data>Hello World!</Data></Test>\"\nroot = etree.fromstring(data_to_sign)\n\nsigner = XMLSigner()\nsigned_root = signer.sign(root, key=key, cert=cert)\n\nprint(\"\\n--- Signed XML ---\")\nprint(etree.tostring(signed_root, pretty_print=True).decode())\n\n# --- Verifying the signed XML document ---\nverifier = XMLVerifier()\n\ntry:\n    # It's crucial to explicitly provide the trusted certificate or CA for verification\n    # and to ensure the returned data is what was expected to prevent signature wrapping attacks.\n    verified_data = verifier.verify(signed_root, x509_cert=cert).signed_xml\n    \n    print(\"\\n--- Verification Successful! ---\")\n    print(\"Signed data content:\", etree.tostring(verified_data, pretty_print=False).decode())\n    \n    # Optionally, assert the signature location (best practice for SAML etc.)\n    config = SignatureConfiguration(location='./')\n    verifier.verify(signed_root, x509_cert=cert, expect_config=config)\n    print(\"Signature location asserted successfully.\")\n\nexcept Exception as e:\n    print(f\"\\n--- Verification Failed: {e} ---\")\n","lang":"python","description":"This quickstart demonstrates basic XML signing and verification using `XMLSigner` and `XMLVerifier`. It includes placeholder instructions for generating a test certificate and key using OpenSSL. In production, always load certificates and keys securely and explicitly configure trust for verification. It also highlights the best practice of verifying the `signed_xml` attribute and asserting the signature's expected location."},"warnings":[{"fix":"Update your code to use `cryptography` types and `ca_pem_file` where applicable. Review API documentation for updated methods, especially for certificate chain validation.","message":"Version 4.0.0 introduced a major infrastructure change, replacing PyOpenSSL with Cryptography for core certificate and key handling. The `ca_path` parameter for specifying CA certificate stores was removed and replaced by `ca_pem_file`.","severity":"breaking","affected_versions":">=4.0.0"},{"fix":"Upgrade to signxml version 4.0.4 or newer immediately to mitigate these vulnerabilities.","message":"Version 4.0.4 contained critical security fixes addressing HMAC algorithm confusion and timing attacks. Running older versions with HMAC-based signatures is highly insecure.","severity":"breaking","affected_versions":"<4.0.4"},{"fix":"Ensure your XML inputs do not contain DTD declarations. If you are processing external XML, sanitize or transform it to remove DTDs before passing it to signxml.","message":"As of version 4.4.0, DTD (Document Type Declaration) declarations are forbidden in XML input for enhanced security, preventing XML External Entity (XXE) attacks.","severity":"breaking","affected_versions":">=4.4.0"},{"fix":"Always import `etree` from `lxml` (`from lxml import etree`) and pass `lxml.etree` objects or raw XML strings directly to signxml. Avoid converting to/from `xml.etree.ElementTree` objects.","message":"signxml relies heavily on `lxml.etree` for its advanced XML parsing, canonicalization, and security features. Using Python's standard `xml.etree.ElementTree` can lead to inconsistent behavior, especially with namespace handling, and may bypass lxml's security protections.","severity":"gotcha","affected_versions":"all"},{"fix":"When verifying, access `result.signed_xml` and incorporate `expect_config=SignatureConfiguration(location='./')` in your `verify()` calls where appropriate.","message":"For robust security, always follow the 'See what is signed' principle. After `XMLVerifier.verify()`, explicitly use the `signed_xml` attribute of the return value, as this is the actual data covered by the signature. Also, for specific standards like SAML, it's a best practice to assert the expected signature location using `SignatureConfiguration(location='./')` to prevent signature wrapping attacks.","severity":"gotcha","affected_versions":"all"},{"fix":"Always specify trust anchors during verification (e.g., `verifier.verify(..., x509_cert=my_trusted_cert)` or `ca_pem_file=\"/path/to/my_ca.pem\")`).","message":"The default `XMLVerifier().verify()` behavior trusts any valid X.509 certificate that validates against your system's CA store. For production, you must explicitly establish trust using parameters like `x509_cert` (a pre-shared certificate), `cert_subject_name` (to validate the subject name in the signing certificate), or `ca_pem_file` (a custom CA bundle) to prevent unauthorized signatures.","severity":"gotcha","affected_versions":"all"},{"fix":"Do not pretty-print or reformat signed XML documents if their signatures are to remain valid.","message":"XML canonicalization, a crucial step in XML Signature, is highly sensitive to whitespace. Pretty-printing an XML document *after* it has been signed will almost certainly invalidate its signature.","severity":"gotcha","affected_versions":"all"}],"env_vars":null,"last_verified":"2026-04-10T00:00:00.000Z","next_check":"2026-07-09T00:00:00.000Z"}