向 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 编码员来说可能是初级的;但至少我到了那里。
我在今年 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 编码员来说可能是初级的;但至少我到了那里。