Skip to content

Commit

Permalink
[PW-2385] Improvements for accepting notifications (Adyen#736)
Browse files Browse the repository at this point in the history
* Remove unused ResultPos.php

* Improve username password validation when accepting notifications

* [PW-2387]: IP validation implementation  (Adyen#713)

* [PW-2387]: IP address cache config, cronjob and notification check

* [PW-2387]: Removing unrelated files

* [PW-2387]: Removing unrelated files

* [PW-2387]: Newlines

* [PW-2387]: Replacing PHPUnit functions

* [PW-2387]: Formatting

* Update etc/adminhtml/system/adyen_security.xml

Co-authored-by: cyattilakiss <[email protected]>

* [PW-2387]: Adding log message when there's no IP addresses in cache

* [PW-2387]: Updating IP addresses in cache on notification-receving

* [PW-2387]: Adjusting unit tests for IpAddress class

* [PW-2387]: Adjusting unit tests for IpAddress class

* PHP API Library version

Co-authored-by: cyattilakiss <[email protected]>

* [PW-2385] - Use mandatory HMAC validation for notifications (Adyen#716)

* Initial commit

* Add the event codes list

* Remove debug statements

* Add hmac_key for test and live. Add in the sensitive section in di

* Fix recommended changes

* Create isDemoMode config method

* Minor code improvements

* Replace demo_mode with const

* Revert isDemoMode

* Update etc/adminhtml/system/adyen_security.xml

Co-authored-by: Ángel Campos <[email protected]>

* Remove unnecessary conditional

* Add IP check cronjob to README

* Update Helper/IpAddress.php

Co-authored-by: Alessio Zampatti <[email protected]>

* Add comment to Security section

* Hmac->HMAC

* Require adyen/php-api-library version ^6.3

Co-authored-by: Ángel Campos <[email protected]>
Co-authored-by: Alexandros Moraitis <[email protected]>
Co-authored-by: Marcos Garcia <[email protected]>
Co-authored-by: Alessio Zampatti <[email protected]>
  • Loading branch information
5 people authored Jun 26, 2020
1 parent 94f97aa commit 12d0f1f
Show file tree
Hide file tree
Showing 12 changed files with 549 additions and 295 deletions.
79 changes: 73 additions & 6 deletions Controller/Process/Json.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

namespace Adyen\Payment\Controller\Process;

use Adyen\Util\HmacSignature;
use Symfony\Component\Config\Definition\Exception\Exception;
use Magento\Framework\App\Request\Http as Http;

Expand Down Expand Up @@ -56,26 +57,50 @@ class Json extends \Magento\Framework\App\Action\Action
*/
private $serializer;

/**
* @var \Adyen\Payment\Helper\Config
*/
protected $configHelper;

/**
* @var \Adyen\Payment\Helper\IpAddress
*/
protected $ipAddressHelper;

/**
* @var HmacSignature
*/
private $hmacSignature;

/**
* Json constructor.
*
* @param \Magento\Framework\App\Action\Context $context
* @param \Adyen\Payment\Helper\Data $adyenHelper
* @param \Adyen\Payment\Logger\AdyenLogger $adyenLogger
* @param \Magento\Framework\Serialize\SerializerInterface $serializer
* @param \Adyen\Payment\Helper\Config $configHelper
* @param \Adyen\Payment\Helper\IpAddress $ipAddressHelper
* @param HmacSignature $hmacSignature
*/
public function __construct(
\Magento\Framework\App\Action\Context $context,
\Adyen\Payment\Helper\Data $adyenHelper,
\Adyen\Payment\Logger\AdyenLogger $adyenLogger,
\Magento\Framework\Serialize\SerializerInterface $serializer
\Magento\Framework\Serialize\SerializerInterface $serializer,
\Adyen\Payment\Helper\Config $configHelper,
\Adyen\Payment\Helper\IpAddress $ipAddressHelper,
HmacSignature $hmacSignature
) {
parent::__construct($context);
$this->_objectManager = $context->getObjectManager();
$this->_resultFactory = $context->getResultFactory();
$this->_adyenHelper = $adyenHelper;
$this->_adyenLogger = $adyenLogger;
$this->serializer = $serializer;
$this->configHelper = $configHelper;
$this->ipAddressHelper = $ipAddressHelper;
$this->hmacSignature = $hmacSignature;

// Fix for Magento2.3 adding isAjax to the request params
if (interface_exists(\Magento\Framework\App\CsrfAwareActionInterface::class)) {
Expand Down Expand Up @@ -180,6 +205,25 @@ protected function _validateNotificationMode($notificationMode)
*/
protected function _processNotification($response, $notificationMode)
{
if ($this->configHelper->getNotificationsIpHmacCheck()) {
//Validate if the notification comes from a verified IP
if (!$this->isIpValid()) {
$this->_adyenLogger->addAdyenNotification(
"Notification has been rejected because the IP address could not be verified"
);
return false;
}

if ($this->hmacSignature->isHmacSupportedEventCode($response)) {
//Validate the Hmac calculation
if (!$this->hmacSignature->isValidNotificationHMAC($this->configHelper->getNotificationsHmacKey(),
$response)) {
$this->_adyenLogger->addAdyenNotification('HMAC key validation failed ' . print_r($response, 1));
return false;
}
}
}

// validate the notification
if ($this->authorised($response)) {
// log the notification
Expand Down Expand Up @@ -271,18 +315,18 @@ protected function authorised($response)
}

// validate username and password
if ((!isset($_SERVER['PHP_AUTH_USER']) && !isset($_SERVER['PHP_AUTH_PW']))) {
if ((!isset($_SERVER['PHP_AUTH_USER']) || !isset($_SERVER['PHP_AUTH_PW']))) {
if ($this->_isTestNotification($response['pspReference'])) {
$this->_returnResult(
'Authentication failed: PHP_AUTH_USER and PHP_AUTH_PW are empty. See Adyen Magento manual CGI mode'
'Authentication failed: PHP_AUTH_USER or PHP_AUTH_PW are empty. See Adyen Magento manual CGI mode'
);
}
return false;
}

$usernameCmp = strcmp($_SERVER['PHP_AUTH_USER'], $username);
$passwordCmp = strcmp($_SERVER['PHP_AUTH_PW'], $password);
if ($usernameCmp === 0 && $passwordCmp === 0) {
$usernameIsValid = hash_equals($username, $_SERVER['PHP_AUTH_USER']);
$passwordIsValid = hash_equals($password, $_SERVER['PHP_AUTH_PW']);
if ($usernameIsValid && $passwordIsValid) {
return true;
}

Expand All @@ -295,6 +339,29 @@ protected function authorised($response)
return false;
}

/**
* Checks if any of the possible remote IP address sending the notification is verified and returns the validation result
*
* @return bool
*/
protected function isIpValid()
{
$ipAddress = [];

//Getting remote and possibly forwarded IP addresses
if (!empty($_SERVER['REMOTE_ADDR'])) {
array_push($ipAddress, $_SERVER['REMOTE_ADDR']);
}
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
array_push($ipAddress, $_SERVER['HTTP_X_FORWARDED_FOR']);
}
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
array_push($ipAddress, $_SERVER['HTTP_CLIENT_IP']);
}

return $this->ipAddressHelper->isIpAddressValid($ipAddress);
}

/**
* If notification is already saved ignore it
*
Expand Down
Loading

0 comments on commit 12d0f1f

Please sign in to comment.