向 Sagepay 提交加密代码的网站可以正常工作,但现在在服务器 php 升级后失败

Website to Sagepay submit encryption code was working but now fails after server php upgrade

我在今年 4 月完成了我网站的 v.3 Sagepay 升级,并且一直运行良好。

但现在我的网络托管服务提供商已将其 PHP 版本从 5.2.4 升级到 5.5.9,这导致网站严重中断。

我已经设法修复了最引人注目的问题(比如所有显示空白的动态产品列表和详细信息页面),但我无法提交到 Sagepay。

更确切地说,一旦按下提交按钮,就会出现一个页面,其中包含一个文本字符串,该字符串显示 functions.php 页面中的大部分代码,该页面处理向 Sagepay 提交的加密;和解密来自 Sagepay 的响应。 (幸好字符串中不包含加密密码。)提交当然无法进行。

此页面中的代码混合了先前版本的 SagePay 文档中的脚本,混合或替换为版本 3 的更新脚本 - 事实上,我记得它主要使用了来自 Whosebug 上贡献者的加密示例.

这是页面中的代码(如果我应该遗漏主要来自 SagePay 的评论,我很抱歉 - 我不确定它是否不适合这里的帖子):

//************ NEW CRYPT STUFF COPIED FRON SAGEPAY KIT util.php
//DH added class definition as shown in Whosebug page - trying to fix error when run, on line static private function etc

 class DHclassInFunc{

/**
 * PHP's mcrypt does not have built in PKCS5 Padding, so we use this.
 *
 * @param string $input The input string.
 *
 * @return string The string with padding.
 */
static protected function addPKCS5Padding($input)
{
 $blockSize = 16;
 $padd = "";

// Pad input to an even block size boundary.
 $length = $blockSize - (strlen($input) % $blockSize);
 for ($i = 1; $i <= $length; $i++)
{
 $padd .= chr($length);
}
 return $input . $padd;
}

/**
 * Remove PKCS5 Padding from a string.
 *
 * @param string $input The decrypted string.
 *
 * @return string String without the padding.
 * @throws SagepayApiException
 */
 static protected function removePKCS5Padding($input)
{
 $blockSize = 16;
 $padChar = ord($input[strlen($input) - 1]);

/* Check for PadChar is less then Block size */
 if ($padChar > $blockSize)
{
 throw new SagepayApiException('Invalid encryption string');
}
/* Check by padding by character mask */
 if (strspn($input, chr($padChar), strlen($input) - $padChar) !=           $padChar)
{
 throw new SagepayApiException('Invalid encryption string');
}

 $unpadded = substr($input, 0, (-1) * $padChar);
/* Chech result for printable characters */
 if (preg_match('/[[:^print:]]/', $unpadded))
{
 throw new SagepayApiException('Invalid encryption string');
}
 return $unpadded;
}

/**
 * Encrypt a string ready to send to SagePay using encryption key.
 *
 * @param  string  $string  The unencrypyted string.
 * @param  string  $key     The encryption key.
 *
 * @return string The encrypted string.
 */
 static public function encryptAes($string, $key)
{
// AES encryption, CBC blocking with PKCS5 padding then HEX encoding.
// Add PKCS5 padding to the text to be encypted.
 $string = self::addPKCS5Padding($string);

// Perform encryption with PHP's MCRYPT module.
 $crypt = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $string, MCRYPT_MODE_CBC, $key);

// Perform hex encoding and return.
 return "@" . strtoupper(bin2hex($crypt));
}

/**
 * Decode a returned string from SagePay.
 *
 * @param string $strIn         The encrypted String.
 * @param string $password      The encyption password used to encrypt the string.
 *
 * @return string The unecrypted string.
 * @throws SagepayApiException
 */
static public function decryptAes($strIn, $password)
{
    // HEX decoding then AES decryption, CBC blocking with PKCS5 padding.
    // Use initialization vector (IV) set from $str_encryption_password.
    $strInitVector = $password;

    // Remove the first char which is @ to flag this is AES encrypted and HEX decoding.
    $hex = substr($strIn, 1);

    // Throw exception if string is malformed
    if (!preg_match('/^[0-9a-fA-F]+$/', $hex))
    {
        throw new SagepayApiException('Invalid encryption string');
    }
    $strIn = pack('H*', $hex);

    // Perform decryption with PHP's MCRYPT module.
    $string = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $password, $strIn, MCRYPT_MODE_CBC, $strInitVector);
    return self::removePKCS5Padding($string);
}
}

/* The getToken function.                                                                                                      **
** NOTE: A function of convenience that extracts the value from the     "name=value&name2=value2..." VSP reply string **
** Works even if one of the values is a URL containing the & or = signs. */

 function getToken($thisString) {

// List the possible tokens
 $Tokens = array(
"Status",
"StatusDetail",
"VendorTxCode",
"VPSTxId",
"TxAuthNo",
"Amount",
"AVSCV2", 
"AddressResult", 
"PostCodeResult", 
"CV2Result", 
"GiftAid", 
"3DSecureStatus", 
"CAVV" );

// Initialise arrays
 $output = array();
 $resultArray = array();

// Get the next token in the sequence
 for ($i = count($Tokens)-1; $i >= 0 ; $i--){
// Find the position in the string
 $start = strpos($thisString, $Tokens[$i]);
// If it's present
 if ($start !== false){
// Record position and token name
 $resultArray[$i]->start = $start;
 $resultArray[$i]->token = $Tokens[$i];
}
}

// Sort in order of position
 sort($resultArray);

// Go through the result array, getting the token values
 for ($i = 0; $i<count($resultArray); $i++){
// Get the start point of the value
$valueStart = $resultArray[$i]->start + strlen($resultArray[$i]->token) + 1;
// Get the length of the value
 if ($i==(count($resultArray)-1)) {
 $output[$resultArray[$i]->token] = substr($thisString, $valueStart);
} else {
 $valueLength = $resultArray[$i+1]->start - $resultArray[$i]->start -      strlen($resultArray[$i]->token) - 2;
 $output[$resultArray[$i]->token] = substr($thisString, $valueStart, $valueLength);
}      
}

// Return the ouput array
 return $output;

}

// Randomise based on time
 function randomise() {
list($usec, $sec) = explode(' ', microtime());
return (float) $sec + ((float) $usec * 100000);
}
?>

我会提到这个页面的一个较早的问题,我认为它是相关的并且可能有助于指出问题的根源 -

在尝试将服务器切换到新的 PHP 版本后 - 产品显示全部消失 - 我恢复到旧版本的 PHP (在测试期间允许恢复,但现在升级是最终的)。还原后,网站显示 returned 恢复正常,但 Sagepay 功能无法正常工作 - 尽管在那种情况下提交页面工作正常,但在到达 function.php 页面时响应失败

在那种情况下,错误消息引用了“throw new SagepayApiException('Invalid encryption string');”这一行在下面的片段中,它位于页面脚本的 return 部分。

// Throw exception if string is malformed
 if (!preg_match('/^[0-9a-fA-F]+$/', $hex))
{
 throw new SagepayApiException('Invalid encryption string');
}
 $strIn = pack('H*', $hex);

以我的总体 php 技能,尤其是调试,在我看来实际上是在几行之前出现问题,而不是 return ed crypt 字符串本身有问题。

我从显示 return 错误的页面上的 url 复制了 crypt 字符串,并通过对 return 字符串作为输入而不是 $hex,然后脚本 运行 超出错误陷阱,而是在“throw new SagepayApiException('Invalid encryption string');”行的其他实例处停止

这让我得出结论,前面的部分存在问题,即 -

static public function decryptAes($strIn, $password)
{
// HEX decoding then AES decryption, CBC blocking with PKCS5 padding.
// Use initialization vector (IV) set from $str_encryption_password.
 $strInitVector = $password;

// Remove the first char which is @ to flag this is AES encrypted and HEX decoding.
 $hex = substr($strIn, 1);

但是,我无法弄清楚为什么会这样,因为此页面中的代码没有被触及,因此应该像以前一样工作,在临时切换到 php 5 之前。 5.x 然后再返回 5.2.x.

最后我想再尝试一件事:在服务器 php 版本更改期间,服务器自动禁用了我的站点 php.in 文件(除了它我对它了解不多可用于更改 php 版本的某些默认特征,服务器为 运行)。我已经恢复了 php.ini 文件,但现在有相当多的禁用 2.2 文件在该位置运行。

试试另一个怎么样? ZAP,问题已解决。

但是我看不到(或者更确切地说理解)新的默认 5.5.9 服务器 ini 文件设置中是否有任何东西可能是加密代码最新问题的根源,除了我的本地php.ini 文件将 2.2.4 的 default_charset 设置为 iso-8859-1,而 5.5.9 中的默认设置是 utf-8。

我现在也在 5.5.9 ini 文件中将 default_charset 设置为 iso-8859-1,但尽管它已将 £s 显示为 ?在网站内容中它对 SagePay 提交错误没有影响。

有什么建议可以解决这个严重的问题(网站目前无法处理任何付款..)?

2016 年 10 月 23 日更新 - 问题已更改;但仍然需要帮助

提交到 Sagepay 现在已解决,但我仍然需要帮助,因为现在我无法处理 Sagepay 的回复。响应从 SagePay 发送并由我的响应文件接收,但在处理 returned 加密字符串期间导致错误。

因此用户的浏览器 window 似乎挂起 - 它是空白的 - 并且用户没有 return 进入我的 website.None 网站的自动处理之一解密过程出错,发起支付结果

错误发生在 functions.php 文件中的行,其中包含我已经发布在这个问题中的代码。它在第184行生成,错误为:

PHP Fatal error:  Class 'SagepayApiException' not found in /web/sites/user/3/84/42307/public/www/My_folder/protx/functions.php on line 184

第184行是下面这行:

throw new SagepayApiException('Invalid encryption string');

在以下片段中:

// Throw exception if string is malformed
 if (!preg_match('/^[0-9a-fA-F]+$/', $hex))
{
 throw new SagepayApiException('Invalid encryption string');
}
 $strIn = pack('H*', $hex);

紧接在致命错误之前,网络服务器日志中有这一行:

PHP Notice:  Undefined variable: crypt in /web/sites/user/3/84/42307/public/www/myfolder/protx/completed.php on line 30

completed.php 是 return url 中的文件,Sagepay 响应指向该文件(并且包含 functions.php 文件)。

文件中的第 30 行 completed.php 文件是:

$Decoded = DHclassInFunc::decryptAes($crypt,$EncryptionPassword);

我整个周末都在做这件事,昨晚我收到 Sageway 的提交后又开始工作了,虽然我已经到了那里(至少网站可以再次接受付款)。

顺便说一句,看起来这方面修复的关键是在加密文件之前的文件中替换了函数 htmlspecialchars 的许多实例,因此他们现在使用 php 5.5.9 可以包含字符集,如下例:

htmlspecialchars($rs->Fields($kartProps['Korders']['orders']['email']), ENT_COMPAT,'ISO-8859-1', true);

我还通过 .ini 文件中的本地 character_set 设置将 php.ini 文件中的默认 UTF-8 编码更改为 ISO-8859-1。

我推断在 Sagepay 响应文件的解密过程中仍然存在某种 character_set 冲突,这导致 preg_match 函数失败。

我已经精疲力竭了,无法弄清楚正在发生的事情,我将不胜感激任何建议。

已解决

解密脚本无法正常工作,因为它们没有从 Sagepay 发送的响应中的 url 内容中接收任何数据。

应在文件 'completed.php' 中接收此响应,然后将响应字符串传递给包含解密代码的 'functions.php'。

在 completed.php 文件中,提取用于 php 5.2.4 的 url 内容的以下行:

$Decoded = DHclassInFunc::decryptAes($crypt,$EncryptionPassword);  this worked in 5.5.3 but not 5.5.59

我将其更改为:

$Decoded = DHclassInFunc::decryptAes(($_GET['crypt']),$EncryptionPassword);// HURRAH ! finally the scripts get the url contents so now works in php 5.5.9.

可能是初级的 php 编码员;但至少我到了那里。

已解决

解密脚本无法正常工作,因为它们没有从 Sagepay 发送的响应中的 url 内容中接收任何数据。

应在文件 'completed.php' 中接收此响应,然后将响应字符串传递给包含解密代码的 'functions.php'。

在 completed.php 文件中,提取用于 php 5.2.4 的 url 内容的以下行:

$Decoded = DHclassInFunc::decryptAes($crypt,$EncryptionPassword);  this worked in 5.5.3 but not 5.5.59

我将其更改为:

$Decoded = DHclassInFunc::decryptAes(($_GET['crypt']),$EncryptionPassword);// HURRAH ! finally the scripts get the url contents so now works in php 5.5.9.

对于称职的 php 编码员来说可能是初级的;但至少我到了那里。