xmlseclibs

repository·master·Indexed 19 days ago

https://github.com/robrichards/xmlseclibs

A PHP library for secure XML Encryption and XML Signature operations, commonly used in SAML and WS-Security implementations. It provides classes for signing and verifying XML documents (XMLSecurityDSig), encrypting and decrypting XML nodes (XMLSecEnc), and managing cryptographic keys (XMLSecurityKey). The library includes security hardening against XXE, SSRF, and Bleichenbacher attacks, requiring PHP 8.0+ and phpseclib/phpseclib ~3.0.

Tokens
8.6K
Snippets
25
Records
32
Agent score
64%

What's inside xmlseclibs

  1. How Legacy Mode works in xmlseclibs

    master

    If you must support legacy peers that rely on pre-4.0 behavior, you can call $objDSig->enableLegacyMode() or $objenc->enableLegacyMode() once after construction.

    What Legacy Mode restores:

    • DOCTYPE allowed during signature verification.
    • XPath transforms allowed without count caps.
    • RSA-1.5 key transport allowed.

    What Legacy Mode DOES NOT undo:

    • Algorithm/key binding.
    • Uniform decryption errors.
    • DOCTYPE rejection in decrypted XML.
    • Reference fail-closed behavior.
    • SSRF hardening rules.
    • PHP/phpseclib requirements.
  2. Security Hardening: XPath Transforms and DOCTYPE

    master

    To prevent Denial-of-Service and bypass attacks, xmlseclibs implements several defaults:

    • XPath Transforms: Rejected by default during verification to prevent pre-authentication CPU DoS. If you must use them, set $objDSig->allowXPathTransforms = true. When enabled, they are capped by maxXPathTransforms (default 5) and maxXPathNamespaces (default 20).
    • DOCTYPE Rejection: Documents carrying a DOCTYPE are rejected during signature verification to prevent entity-reference bypasses (e.g., CVE-2025-23369). Set $objDSig->forbidDoctype = false only if you fully trust the source.
    • Decrypted XML: Any decrypted XML containing a DOCTYPE is rejected to guard against XXE.
  3. Security Hardening: Certificate URL Fetching

    master

    When using add509Cert(..., $isURL = true), the library implements SSRF hardening:

    • Protocol: Only http and https are allowed by default. To allow file://, pass array('allow_file_scheme' => true) in the $options array.
    • Target Filtering: The library rejects hosts that resolve to loopback, private, link-local, reserved, or CGNAT addresses.
    • Redirects: Redirects are disabled by default.

    Warning: Only fetch certificates from trusted URLs to mitigate DNS-rebinding risks.

  4. Requirements for xmlseclibs

    master

    To use the current master branch of xmlseclibs, you must meet the following requirements:

    • PHP Version: 8.0 or greater.
    • Dependencies: phpseclib/phpseclib ~3.0 is required. ext-openssl is optional as phpseclib handles the cryptography.
  5. Decrypt XML (Recommended Recipe)

    master

    To decrypt XML, you must supply your own private key to unwrap the document's session key. The following pattern covers the common EncryptedKey (key transport) + EncryptedData (data) layout used in SAML and WS-Security.

    Security Best Practices applied in this recipe:

    • Pin algorithm policies using allowedDataAlgorithms and allowedKeyAlgorithms (defaults use authenticated/OAEP-only presets).
    • RSA-1.5 key transport is denied by default to prevent Bleichenbacher attacks.
    • Decrypted content containing a DOCTYPE is rejected to prevent XXE.
    use RobRichards\\XMLSecLibs\\XMLSecEnc;
    use RobRichards\\XMLSecLibs\\XMLSecurityKey;
    
    $doc = new DOMDocument();
    $doc->load('./path/to/encrypted.xml');
    
    $objenc = new XMLSecEnc();
    
    // Pin the algorithm policy (all optional, but recommended):
    //  - RSA-1.5 key transport is already denied by default (Bleichenbacher);
    //    only set this if you must interoperate with a legacy peer.
    // $objenc->allowRSA15KeyTransport = true;
    //  - Restrict data encryption to authenticated AES-GCM (rejects CBC):
    $objenc->allowedDataAlgorithms = XMLSecEnc::DEFAULT_DATA_ALGORITHMS;
    //  - Restrict key transport to RSA-OAEP:
    $objenc->allowedKeyAlgorithms  = XMLSecEnc::DEFAULT_KEY_ALGORITHMS;
    
    $encData = $objenc->locateEncryptedData($doc);
    if (! $encData) {
        throw new Exception('Cannot locate EncryptedData');
    }
    $objenc->setNode($encData);
    $objenc->type = $encData->getAttribute('Type');
    
    // Resolve the session key. locateKey() reads the data algorithm from the
    // document; locateKeyInfo() finds the EncryptedKey.
    $objKey = $objenc->locateKey();
    if (! $objKey) {
        throw new Exception('Unknown data encryption algorithm');
    }
    
    if ($objKeyInfo = $objenc->locateKeyInfo($objKey)) {
        if ($objKeyInfo->isEncrypted) {
            // Load YOUR trusted private key to unwrap the session key.
            $objKeyInfo->loadKey('./path/to/your-private-key.pem', true);
            $sessionKey = $objKeyInfo->encryptedCtx->decryptKey($objKeyInfo);
            $objKey->loadKey($sessionKey);
        }
    }
    
    // If the session key was supplied out-of-band (no EncryptedKey), load it here:
    // if (empty($objKey->key)) { $objKey->loadKey($sharedSecretBytes); }
    
    // Decrypted content is returned; a DOCTYPE in the plaintext is rejected.
    $decrypted = $objenc->decryptNode($objKey, true);
  6. Verify an XML Signature (Recommended)

    master

    The recommended way to verify signatures is using the verifyDocument() method. This method is safe-by-default because it:

    1. Requires a caller-supplied (pinned) key and never derives the key from the document's KeyInfo.
    2. Enforces an algorithm allowlist for both SignatureMethod and DigestMethod.
    3. Only reports success when every reference is validated.
    4. Returns the validated nodes for you to operate on.

    Security Note: After receiving the validated nodes, operate ONLY on those nodes (especially for SAML/WS-Security) rather than re-selecting elements by ID from the whole document.

    use RobRichards\
    XMLSecLibs\\XMLSecurityDSig;
    use RobRichards\\XMLSecLibs\\XMLSecurityKey;
    
    $doc = new DOMDocument();
    $doc->load('./path/to/signed.xml');
    
    // Pin the key/certificate you trust (do NOT read it from the document).
    $objKey = new XMLSecurityKey(XMLSecurityKey::RSA_SHA256, array('type' => 'public'));
    $objKey->loadKey('./path/to/trusted-cert.pem', true, true);
    
    $objDSig = new XMLSecurityDSig();
    
    // If assertions use custom Id attributes (e.g. WS-Security), declare them first:
    // $objDSig->idKeys = array('wsu:Id');
    // $objDSig->idNS   = array('wsu' => 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd');
    
    try {
        // Throws on any failure; returns the validated nodes on success.
        $validatedNodes = $objDSig->verifyDocument($objKey, $doc);
        // Operate ONLY on $validatedNodes from here on.
    } catch (Exception $e) {
        // Verification failed - reject the message.
    }
  7. Configure XMLSecurityDSig security settings

    master

    The XMLSecurityDSig class provides several public properties to tune security and compatibility.

    Security Defaults

    • forbidDoctype: (Default: true) Rejects documents containing a DOCTYPE to prevent signature-verification bypasses and entity-expansion DoS attacks.
    • allowXPathTransforms: (Default: false) Disables XPath Filtering Transforms during verification to prevent attacker-controlled XPath expressions. Set to true only if you trust the source.
    • maxXPathTransforms: (Default: 5) Limits the number of XPath transforms per Reference to prevent DoS.
    • maxXPathNamespaces: (Default: 20) Limits namespaces allowed on a single XPath transform to prevent DoS.

    Algorithm Allowlists

    • allowedSignatureAlgorithms: When null, verifyDocument() uses DEFAULT_SIGNATURE_ALGORITHMS (RSA-SHA256, SHA384, SHA512, etc.). You can set this to an array of specific URIs to restrict or expand accepted algorithms.
    • allowedDigestAlgorithms: When null, verifyDocument() uses DEFAULT_DIGEST_ALGORITHMS (SHA256, SHA384, SHA512). You can set this to an array of specific URIs to restrict or expand accepted algorithms.
  8. Sign an XML document using SHA-256

    master

    To sign an XML document, use the XMLSecurityDSig class to manage the signature process and XMLSecurityKey to handle the private key.

    Steps involved:

    1. Load the target XML into a DOMDocument.
    2. Initialize XMLSecurityDSig and set the canonicalization method (e.g., XMLSecurityDSig::EXC_C14N).
    3. Add a reference to the document using a digest algorithm (e.g., XMLSecurityDSig::SHA256) and specify any required transforms like enveloped-signature.
    4. Initialize XMLSecurityKey with the appropriate algorithm and key type ('type'=>'private').
    5. Load the private key using loadKey(). If the key is passphrase-protected, set the $objKey->passphrase property.
    6. Call sign($objKey) to perform the signature.
    7. Attach the public certificate using add509Cert().
    8. Append the signature to the XML element using appendSignature() and save the document.
    use RobRichards\
    XMLSecLibs\XMLSecurityDSig;
    use RobRichards\XMLSecLibs\XMLSecurityKey;
    
    // Load the XML to be signed
    $doc = new DOMDocument();
    $doc->load('./path/to/file/tobesigned.xml');
    
    // Create a new Security object 
    $objDSig = new XMLSecurityDSig();
    // Use the c14n exclusive canonicalization
    $objDSig->setCanonicalMethod(XMLSecurityDSig::EXC_C14N);
    // Sign using SHA-256
    $objDSig->addReference(
        $doc, 
        XMLSecurityDSig::SHA256, 
        array('http://www.w3.org/2000/09/xmldsig#enveloped-signature')
    );
    
    // Create a new (private) Security key
    $objKey = new XMLSecurityKey(XMLSecurityKey::RSA_SHA256, array('type'=>'private'));
    /*
    If key has a passphrase, set it using
    $objKey->passphrase = '<passphrase>';
    */
    // Load the private key
    $objKey->loadKey('./path/to/privatekey.pem', TRUE);
    
    // Sign the XML file
    $objDSig->sign($objKey);
    
    // Add the associated public key to the signature
    $objDSig->add509Cert(file_get_contents('./path/to/file/mycert.pem'));
    
    // Append the signature to the XML
    $objDSig->appendSignature($doc->documentElement);
    // Save the signed XML
    $doc->save('./path/to/signed.xml');
  9. Configure algorithm allowlists for XML Encryption

    master

    By default, XMLSecEnc does not restrict algorithms, but you can enforce security policies by setting allowlists on the instance properties. This prevents attackers from forcing the use of weak or deprecated algorithms.

    • allowedKeyAlgorithms: An array of URIs for acceptable asymmetric key-transport algorithms.
    • allowedDataAlgorithms: An array of URIs for acceptable symmetric data-encryption algorithms.
    • allowRSA15KeyTransport: A boolean to permit RSA-1.5 (PKCS#1 v1.5) key transport. This is disabled by default due to Bleichenbacher attack risks. Set to true only for legacy interoperability.
  10. Add X.509 certificates to KeyInfo using add509Cert()

    master

    Use add509Cert() to insert X.509 certificate data into the KeyInfo element of a signature. This method can handle raw certificate strings, PEM formatted strings, or certificates fetched from a URL.

    URL Fetching Security: If $isURL is set to true, the library fetches the certificate with SSRF protections. It only allows http or https (unless allow_file_scheme is explicitly enabled in $options), validates that the host resolves to a public IP, and disables HTTP redirects to prevent internal network probing.

    Options:

    • issuerSerial: If provided in the $options array, it will include X509IssuerName and X509SerialNumber nodes.
    • subjectName: If provided in the $options array, it will include an X509SubjectName node.
    // Adding a PEM certificate
    $xmlSecDSig->add509Cert($certString, true, false);
    
    // Adding a certificate from a URL with extra metadata
    $options = ['issuerSerial' => true, 'subjectName' => true];
    $xmlSecDSig->add509Cert('https://example.com/cert.pem', true, true, $options);
  11. Create a key from an EncryptedKey element

    master

    The static method XMLSecurityKey::fromEncryptedKeyElement(DOMElement $element, $depth = 0, $allowRSA15 = false) allows you to instantiate an XMLSecurityKey directly from an XML <EncryptedKey> element. This is typically used during the decryption of enveloped XML signatures or encrypted XML documents where the symmetric key is wrapped by an asymmetric key.

    // Assuming $domElement is a DOMElement representing an <EncryptedKey> node
    $key = \RobRichards\XMLSecLibs\XMLSecurityKey::fromEncryptedKeyElement($domElement);