[
https://issues.apache.org/jira/browse/CAMEL-24429?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Andrea Cosentino updated CAMEL-24429:
-------------------------------------
Description:
ResponseMDN holds the AS2 security material in mutable instance fields and
overwrites them per request:
{code:java}
private AS2SignatureAlgorithm signingAlgorithm;
private Certificate[] signingCertificateChain;
private PrivateKey signingPrivateKey;
private PrivateKey decryptingPrivateKey;
private Certificate[] validateSigningCertificateChain;
private boolean keysAreDynamic = false;
private final Lock lock = new ReentrantLock();
...
if (this.keysAreDynamic) {
// Dynamically load path-specific security material from the HttpContext,
// which was populated by AS2ConsumerConfigInterceptor.
this.signingAlgorithm = (AS2SignatureAlgorithm)
context.getAttribute(AS2ServerConnection.AS2_SIGNING_ALGORITHM);
this.signingCertificateChain = (Certificate[])
context.getAttribute(...AS2_SIGNING_CERTIFICATE_CHAIN);
this.signingPrivateKey = (PrivateKey)
context.getAttribute(...AS2_SIGNING_PRIVATE_KEY);
this.decryptingPrivateKey = (PrivateKey)
context.getAttribute(...AS2_DECRYPTING_PRIVATE_KEY);
this.validateSigningCertificateChain = (Certificate[])
context.getAttribute(...AS2_VALIDATE_SIGNING_CERTIFICATE_CHAIN);
}
{code}
Three things combine here:
* The instance is registered once on the shared HttpProcessor, so a single
ResponseMDN serves every request.
* The assignment block above is not inside the class's lock.
* The assigned values persist after the request that set them, so they are
still in place when the next request begins.
A deployment hosting more than one partner on different paths, each with its
own keys, can therefore have one partner's MDN signed with another partner's
private key, or validated against the wrong chain. An MDN is the
non-repudiation record for the interchange, so signing it with the wrong key
undermines exactly the property it exists to carry.
Proposal: keep the dynamically loaded algorithm, keys and chains in local
variables for the duration of process(), or carry them through the
HttpCoreContext, rather than in instance fields. A regression test should drive
two requests with different key material through one connection and assert each
MDN is signed with its own path's key.
The context-reuse issue that was originally filed alongside this one has been
split out to CAMEL-24435 - the two share the theme of per-request state
outliving its request, but they are separate defects with separate fixes.
was:
Two places keep per-request state where it can be observed by a later or
concurrent request.
*ResponseMDN* holds the security material in mutable instance fields and
overwrites them per request:
{code:java}
private AS2SignatureAlgorithm signingAlgorithm;
private Certificate[] signingCertificateChain;
private PrivateKey signingPrivateKey;
private PrivateKey decryptingPrivateKey;
private Certificate[] validateSigningCertificateChain;
private boolean keysAreDynamic = false;
...
if (this.keysAreDynamic) {
this.signingAlgorithm = (AS2SignatureAlgorithm)
context.getAttribute(AS2ServerConnection.AS2_SIGNING_ALGORITHM);
this.signingCertificateChain = (Certificate[])
context.getAttribute(...AS2_SIGNING_CERTIFICATE_CHAIN);
this.signingPrivateKey = (PrivateKey)
context.getAttribute(...AS2_SIGNING_PRIVATE_KEY);
this.decryptingPrivateKey = (PrivateKey)
context.getAttribute(...AS2_DECRYPTING_PRIVATE_KEY);
this.validateSigningCertificateChain = (Certificate[])
context.getAttribute(...AS2_VALIDATE_SIGNING_CERTIFICATE_CHAIN);
}
{code}
The instance is registered once on the shared HttpProcessor, so it is shared
across requests. The assignment block is not inside the class's lock, and the
values persist after the request that set them. A deployment hosting two
partners on different paths with different keys can therefore have one
request's MDN signed with the other partner's key.
*AS2ServerConnection* creates the HttpContext once outside the request loop:
{code:java}
final HttpContext context = HttpCoreContext.create();
try {
while (!Thread.interrupted()) {
this.httpService.handleRequest(this.serverConnection, context);
...
String recipientAddress =
coreContext.getAttribute(AS2AsynchronousMDNManager.RECIPIENT_ADDRESS,
String.class);
{code}
RECIPIENT_ADDRESS and ASYNCHRONOUS_MDN set while handling one request are still
present on the next iteration, so a later request that does not request an
asynchronous MDN can trigger a resend to the previous request's address.
Proposal: keep the dynamically loaded keys and chains in local variables (or in
the HttpCoreContext) for the duration of process() rather than in instance
fields, and remove RECIPIENT_ADDRESS/ASYNCHRONOUS_MDN from the context after
the send block - or create a fresh context per request. A test that drives two
requests with different key material through one connection would cover both.
Summary: camel-as2 - ResponseMDN keeps per-request signing keys in
shared mutable instance fields (was: camel-as2 - per-request security material
outlives the request in ResponseMDN and the listener context)
> camel-as2 - ResponseMDN keeps per-request signing keys in shared mutable
> instance fields
> ----------------------------------------------------------------------------------------
>
> Key: CAMEL-24429
> URL: https://issues.apache.org/jira/browse/CAMEL-24429
> Project: Camel
> Issue Type: Bug
> Components: camel-as2
> Reporter: Andrea Cosentino
> Assignee: Andrea Cosentino
> Priority: Major
> Fix For: 4.23.0
>
>
> ResponseMDN holds the AS2 security material in mutable instance fields and
> overwrites them per request:
> {code:java}
> private AS2SignatureAlgorithm signingAlgorithm;
> private Certificate[] signingCertificateChain;
> private PrivateKey signingPrivateKey;
> private PrivateKey decryptingPrivateKey;
> private Certificate[] validateSigningCertificateChain;
> private boolean keysAreDynamic = false;
> private final Lock lock = new ReentrantLock();
> ...
> if (this.keysAreDynamic) {
> // Dynamically load path-specific security material from the HttpContext,
> // which was populated by AS2ConsumerConfigInterceptor.
> this.signingAlgorithm = (AS2SignatureAlgorithm)
> context.getAttribute(AS2ServerConnection.AS2_SIGNING_ALGORITHM);
> this.signingCertificateChain = (Certificate[])
> context.getAttribute(...AS2_SIGNING_CERTIFICATE_CHAIN);
> this.signingPrivateKey = (PrivateKey)
> context.getAttribute(...AS2_SIGNING_PRIVATE_KEY);
> this.decryptingPrivateKey = (PrivateKey)
> context.getAttribute(...AS2_DECRYPTING_PRIVATE_KEY);
> this.validateSigningCertificateChain = (Certificate[])
> context.getAttribute(...AS2_VALIDATE_SIGNING_CERTIFICATE_CHAIN);
> }
> {code}
> Three things combine here:
> * The instance is registered once on the shared HttpProcessor, so a single
> ResponseMDN serves every request.
> * The assignment block above is not inside the class's lock.
> * The assigned values persist after the request that set them, so they are
> still in place when the next request begins.
> A deployment hosting more than one partner on different paths, each with its
> own keys, can therefore have one partner's MDN signed with another partner's
> private key, or validated against the wrong chain. An MDN is the
> non-repudiation record for the interchange, so signing it with the wrong key
> undermines exactly the property it exists to carry.
> Proposal: keep the dynamically loaded algorithm, keys and chains in local
> variables for the duration of process(), or carry them through the
> HttpCoreContext, rather than in instance fields. A regression test should
> drive two requests with different key material through one connection and
> assert each MDN is signed with its own path's key.
> The context-reuse issue that was originally filed alongside this one has been
> split out to CAMEL-24435 - the two share the theme of per-request state
> outliving its request, but they are separate defects with separate fixes.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)