Skip to content

Commit 1081db2

Browse files
authored
Harden security (#47)
* Harden security * Code style fixes
1 parent f62c8c5 commit 1081db2

11 files changed

Lines changed: 23 additions & 12 deletions

File tree

src/CXml/Authentication/SimpleSharedSecretAuthenticator.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ public function __construct(private string $sharedSecret)
1616

1717
public function authenticate(Header $header, Context $context): void
1818
{
19-
if ($this->sharedSecret !== $header->sender->credential->getSharedSecret()) {
19+
// use hash_equals() for constant-time comparison to prevent timing attacks
20+
if (!hash_equals($this->sharedSecret, (string)$header->sender->credential->getSharedSecret())) {
2021
throw new CXmlAuthenticationInvalidException($header->sender->credential);
2122
}
2223
}

src/CXml/Builder/OrderRequestBuilder.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,6 @@ public static function fromPunchOutOrderMessage(
125125
$orderDate,
126126
$currency,
127127
$language,
128-
null,
129128
);
130129

131130
$orb->setShipTo($punchOutOrderMessage->punchOutOrderMessageHeader->getShipTo());

src/CXml/Credential/Registry.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ public function authenticate(Header $header, Context $context): void
5858
$senderCredential->identity,
5959
);
6060

61-
if ($baseCredential->getSharedSecret() !== $senderCredential->getSharedSecret()) {
61+
// use hash_equals() for constant-time comparison to prevent timing attacks
62+
if (!hash_equals((string)$baseCredential->getSharedSecret(), (string)$senderCredential->getSharedSecret())) {
6263
throw new CXmlAuthenticationInvalidException($senderCredential);
6364
}
6465
}

src/CXml/Endpoint.php

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,13 @@ public function __construct(
3232
*/
3333
public function parseAndProcessStringAsCXml(string $xml, ?Context $context = null): ?CXml
3434
{
35-
$this->logger->info('Processing incoming CXml message', ['xml' => $xml]);
35+
$this->logger->info('Processing incoming CXml message', ['xml' => $this->removeSharedSecret($xml)]);
3636

3737
// validate
3838
try {
3939
$this->dtdValidator->validateAgainstDtd($xml);
4040
} catch (CXmlInvalidException $cXmlInvalidException) {
41-
$this->logger->error('Incoming CXml was invalid (via DTD)', ['xml' => $xml]);
41+
$this->logger->error('Incoming CXml was invalid (via DTD)', ['xml' => $this->removeSharedSecret($xml)]);
4242

4343
throw $cXmlInvalidException;
4444
}
@@ -47,7 +47,7 @@ public function parseAndProcessStringAsCXml(string $xml, ?Context $context = nul
4747
try {
4848
$cxml = $this->serializer->deserialize($xml);
4949
} catch (RuntimeException $runtimeException) {
50-
$this->logger->error('Error while deserializing xml to CXml: ' . $runtimeException->getMessage(), ['xml' => $xml]);
50+
$this->logger->error('Error while deserializing xml to CXml: ' . $runtimeException->getMessage(), ['xml' => $this->removeSharedSecret($xml)]);
5151

5252
throw new CXmlInvalidException('Error while deserializing xml: ' . $runtimeException->getMessage(), $xml, $runtimeException);
5353
}
@@ -56,13 +56,18 @@ public function parseAndProcessStringAsCXml(string $xml, ?Context $context = nul
5656
try {
5757
$result = $this->processor->process($cxml, $context);
5858
} catch (CXmlException $cXmlException) {
59-
$this->logger->error('Error while processing valid CXml: ' . $cXmlException->getMessage(), ['xml' => $xml]);
59+
$this->logger->error('Error while processing valid CXml: ' . $cXmlException->getMessage(), ['xml' => $this->removeSharedSecret($xml)]);
6060

6161
throw $cXmlException;
6262
}
6363

64-
$this->logger->info('Success after processing incoming CXml message', ['xml' => $xml]);
64+
$this->logger->info('Success after processing incoming CXml message', ['xml' => $this->removeSharedSecret($xml)]);
6565

6666
return $result;
6767
}
68+
69+
private function removeSharedSecret(string $xml): string
70+
{
71+
return (string)preg_replace('/<SharedSecret>.*?<\/SharedSecret>/s', '<SharedSecret>***REDACTED***</SharedSecret>', $xml);
72+
}
6873
}

src/CXml/Model/BusinessPartner.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
class BusinessPartner
1212
{
1313
use IdReferencesTrait;
14+
1415
final public const ROLE_SOLD_TO = 'soldTo';
1516

1617
final public const ROLE_SHIP_FROM = 'shipFrom';

src/CXml/Model/Response/ProfileResponse.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ class ProfileResponse implements ResponsePayloadInterface
2626
/**
2727
* @var Transaction[]
2828
*/
29-
#[Serializer\XmlList(inline: true, entry: 'Transaction')]
29+
#[Serializer\XmlList(entry: 'Transaction', inline: true)]
3030
#[Serializer\Type('array<CXml\Model\Transaction>')]
3131
private array $transactions = [];
3232

src/CXml/Validation/DtdValidator.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ public function validateAgainstDtd(string $xml): void
4444
$internalErrors = libxml_use_internal_errors(true);
4545

4646
$old = new DOMDocument();
47-
$old->loadXML($xml);
47+
// disable network access for security reasons
48+
$old->loadXML($xml, LIBXML_NONET);
4849

4950
$this->validateAgainstMultipleDtd($this->pathToDtds, $old);
5051

src/CXml/Validation/Exception/CXmlInvalidException.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public static function fromLibXmlError($libXmlError, string $xml): self
3434

3535
return new self(
3636
$message,
37-
$xml,
37+
(string)preg_replace('/<SharedSecret>.*?<\/SharedSecret>/s', '<SharedSecret>***REDACTED***</SharedSecret>', $xml),
3838
);
3939
}
4040
}

tests/CXmlTest/Handling/HandlerTest.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
<?php
22

3+
declare(strict_types=1);
4+
35
namespace CXmlTest\Handling;
46

57
use CXml\Authentication\SimpleSharedSecretAuthenticator;

tests/CXmlTest/Model/PunchoutOrderMessageAdvancedPricingTest.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
<?php
22

3+
declare(strict_types=1);
4+
35
namespace CXmlTest\Model;
46

57
use CXml\Builder;

0 commit comments

Comments
 (0)