WsseAuthenticationProvider can cause Symfony\Component\Cache\Exception\InvalidArgumentException exception
I'm trying to call the OroCRM 4.0 REST API using WSSE authentication. My client is a Laravel app using Guzzle (6.3.3). My X-WSSE header is generated using the package available on Packagist (I'm using v4.3.0).
The WSSE middleware I'm using uses the following code to generate the nonce that's included in the request...
/**
* Generate Nonce (number user once)
*
* @version 1.0
* @since 2013-10
*
* @return string
*/
public function generateNonce()
{
return base64_encode(random_bytes(128));
}
The nonce generated by this code almost always includes a / character. It's completely valid Base64 output, just happens to include a /.
When a request containing such a nonce is sent to Oro, I get a 500 response back, and the var/log/prod.log contains an error like the following:
[2019-09-25 02:39:37] request.CRITICAL: Uncaught PHP Exception Symfony\Component\Cache\Exception\InvalidArgumentException: "Cache key "9yp3V3I8YNv7bOruVB/HK+/zC/tGpYKHPTrYjRd9NBwN2BgJR8STZzw63zkFLpxqyJWKPPhe2aEzxZSQOXXQri5tArmbUryWKg7/feMZC6YNCpLVyrX8RXxY5AFd/9yDcKcTL/bOrl8h8+S1DrfoY3bp+GGFtWnUqNd5vjxFt/o=" contains reserved characters {}()/@:" at /var/www/html/oro/vendor/symfony/symfony/src/Symfony/Component/Cache/CacheItem.php line 175 {"exception":"[object] (Symfony\Component\Cache\Exception\InvalidArgumentException(code: 0): Cache key "9yp3V3I8YNv7bOruVB/HK+/zC/tGpYKHPTrYjRd9NBwN2BgJR8STZzw63zkFLpxqyJWKPPhe2aEzxZSQOXXQri5tArmbUryWKg7/feMZC6YNCpLVyrX8RXxY5AFd/9yDcKcTL/bOrl8h8+S1DrfoY3bp+GGFtWnUqNd5vjxFt/o=" contains reserved characters {}()/\@: at /var/www/html/oro/vendor/symfony/symfony/src/Symfony/Component/Cache/CacheItem.php:175)"} []
It doesn't matter what URL/resource I request, request params, etc. If the nonce includes a / character the exception always occurs.
Unwinding the call stack with a debugger to get back into Oro code leads us to https://github.com/oroinc/platform/blame/master/src/Oro/Bundle/WsseAuthenticationBundle/Security/Core/Authentication/Provider/WsseAuthenticationProvider.php#L228 . This line of code passes the nonce directly to the Symfony cache classes without attempting to validate that the nonce is in fact a valid cache key. In the case of the nonce's generated by my Guzzle middleware, it's not a valid cache key, and causes the exception above.
Section 3.1 of the OASIS Web Services Security UsernameToken Profile 1.1 specification (ref: https://www.oasis-open.org/committees/download.php/16782/wss-v1.1-spec-os-UsernameTokenProfile.pdf) which describes this authentication profile says:
A nonce is a random value that the sender creates to include in each UsernameToken that it sends.
The standard does not impose any restrictions on the content of the nonce in either it's raw form or base64 encoded. By treating the nonce as if it will not contain a /, Oro Platform is not implementing the standard correctly. Which is why I believe this is an Oro Platform bug not the middleware client library I'm using. It also limits any API implemented on Oro Platform to being used with a subset of client libraries that can reliably generate a nonce that's compatible with your implementation of WSSE.
To resolve this issue, Oro should either resolve this bug so that any valid Base64 string can be a nonce (preferable), or update the docs to list the client implementations that are known to produce a nonce that's compatible with your WSSE implementation.
Confirmed. Thank you for your report. Internal ticket id #BAP-19277
Thanks for confirming the issue. I will look for a fix in a future Platform release.
Workaround for anyone else with this issue until Oro is able to release a fix... I put together this class which wraps the 8Points class and Base64 encodes a hash of the random bytes (compare the below with the original implementation above).
namespace App\Service\Middleware;
use EightPoints\Guzzle\WsseAuthMiddleware as BaseWsseAuthMiddleware;
class WsseAuthMiddleware extends BaseWsseAuthMiddleware
{
/**
* Generate Nonce (number user once)
*
* @version 1.0
* @since 2013-10
*
* @return string
*/
public function generateNonce()
{
return base64_encode(hash('sha512', random_bytes(128)));
}
}
This wrapper class should be added to the Guzzle handler stack instead of the original 8points class, and aside from changing the Nonce generation as discussed below functions identically.
By constraining the input for base64_encode to [0-9A-F] the Base 64 algorithm can't get the string of 1 bits required to produce a / character in the output. It makes the nonce significantly less random, 512 bits instead of 1024. However, still a large search space so security won't be substantially compromised. I have also written a test script to ensure there's no combination of these characters that produces a / when Base 64 encoded. so we can be confident this will avoid the issue.
Obviously the root cause is still with Oro Platform, but this workaround ensures it's not possible to produce a nonce that triggers the Oro bug in the mean time.