Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix mcrypt fatal - Removed on PHP 7.2 #159

Merged
merged 25 commits into from
Feb 11, 2024
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
a844294
Fix mcrypt fatal on data encryption
vaurdan Jul 5, 2021
e89712c
Fix error with unset `push_syndicate_settings`
vaurdan Jul 8, 2021
2997500
Add initial version of a test suite for the encryption functions.
vaurdan Jul 8, 2021
671576d
Make push_syndicate_decrypt return an array by default
vaurdan Jul 8, 2021
f3ad7a1
Use `is_string` if `assertIsString` is not available.
vaurdan Jul 8, 2021
4d5d0f4
Use `is_string` if `assertIsString` is not available.
vaurdan Jul 8, 2021
9927d42
Merge branch 'vip/deprecated-mcrypto' of github.com:Automattic/syndic…
vaurdan Jul 8, 2021
22ae798
Use `is_array` if `assertIsArray` is not available.
vaurdan Jul 8, 2021
ffa394f
Only test with `mcrypt` if the PHP version is < 7.1
vaurdan Jul 8, 2021
1f67318
Extend the PHP 7.1 validation to the encrypt and decrypt functions.
vaurdan Jul 8, 2021
5024eaa
Use yoast/wp-test-utils for compatibility with different phpunit vers…
vaurdan Jul 12, 2021
b1690a9
Refactor encryption to use Syndication_Encryption class
vaurdan Jul 19, 2021
02f0aa8
Fix tests failing on PHP 7.1
vaurdan Jul 19, 2021
8a4d2e7
Move imports and initialization to plugin root file
vaurdan Jul 20, 2021
6f09b04
Refactor tests to have individual tests for each encryptor
vaurdan Jul 20, 2021
1e6d373
Refactor tests to use a abstract test class for Encryptors
vaurdan Jul 20, 2021
8663284
Change Syndication_Encryptor to interface
vaurdan Jul 20, 2021
18fa88c
Remove leftover require_once
vaurdan Jul 20, 2021
fdb506a
Change Syndication_Encryption from a static class to an instantiable …
vaurdan Jul 21, 2021
d418b9c
Add extra validation for older PHP versions (<5.2.7), just in case.
vaurdan Jul 21, 2021
4e3873b
Remove encryptor strategy getters and setters
vaurdan Jul 23, 2021
16ec5b8
Address feedback on Encryption_Test
vaurdan Jul 23, 2021
c9f684e
Correction on the tests PHPDOC
vaurdan Jul 23, 2021
1f6b87c
Change EncryptionTest to test the `encrypt` and `decrypt` methods.
vaurdan Jul 28, 2021
9a3434d
Fix DocBlock
GaryJones Feb 11, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"phpunit/phpunit": "^4 || ^5 || ^6 || ^7",
"squizlabs/php_codesniffer": "^3.5",
"wp-coding-standards/wpcs": "^2.3.0",
"yoast/phpunit-polyfills": "^0.2.0"
"yoast/wp-test-utils": "^0.2.2"
},
"scripts": {
"cbf": [
Expand Down
69 changes: 69 additions & 0 deletions includes/class-syndication-encryption.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php

/**
* Class Syndication_Encryption
*/
class Syndication_Encryption {

/**
* Stores the current Syndication_Encryptor, used for the encryption/decryption operations.
*
* @var Syndication_Encryptor
*/
private static $encryptor;

/**
* Returns the best possible Encryptor, given the current environment.
*
* @return Syndication_Encryptor|false
*/
public static function get_encryptor() {
if ( isset( self::$encryptor ) && self::$encryptor instanceof Syndication_Encryptor ) {
return self::$encryptor;
}

return false;
}

/**
* Set the Encryptor that will be used for the encryption and decryption operations.
*
* @param Syndication_Encryptor $encryptor Encryptor to be used in the encryption.
*
* @return Syndication_Encryptor|false Returns the encryptor
*/
public static function set_encryptor( $encryptor ) {
if ( $encryptor instanceof Syndication_Encryptor ) {
self::$encryptor = $encryptor;
vaurdan marked this conversation as resolved.
Show resolved Hide resolved
return self::$encryptor;
}

return false;
}

/**
* Given $data, encrypt it using a Syndication_Encryptor and return the encrypted string.
*
* @param string|array|object $data the data to be encrypted.
*
* @return false|string
*/
public static function encrypt( $data ) {
$encryptor = self::get_encryptor();
return $encryptor->encrypt( $data );
}

/**
* Decrypts an encrypted $data using a Syndication_Encryptor, and returns the decrypted object.
*
* @param string $data The encrypted data.
* @param bool $associative If true, returns as an associative array. Otherwise returns as a class.
*
* @return false|array|object
*/
public static function decrypt( $data, $associative = true ) {
$encryptor = self::get_encryptor();
return $encryptor->decrypt( $data, $associative );
}

}
36 changes: 36 additions & 0 deletions includes/class-syndication-encryptor-mcrypt.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

/**
* Class Syndication_Encryptor_OpenSSL
GaryJones marked this conversation as resolved.
Show resolved Hide resolved
*/
class Syndication_Encryptor_MCrypt implements Syndication_Encryptor {

/**
* @inheritDoc
*/
public function encrypt( $data ) {
$data = serialize( $data ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize
// phpcs:ignore PHPCompatibility.FunctionUse.RemovedFunctions.mcrypt_encryptDeprecatedRemoved,PHPCompatibility.Extensions.RemovedExtensions.mcryptDeprecatedRemoved,PHPCompatibility.Constants.RemovedConstants.mcrypt_rijndael_256DeprecatedRemoved,PHPCompatibility.Constants.RemovedConstants.mcrypt_mode_cbcDeprecatedRemoved
return base64_encode( mcrypt_encrypt( MCRYPT_RIJNDAEL_256, md5( PUSH_SYNDICATE_KEY ), $data, MCRYPT_MODE_CBC, md5( md5( PUSH_SYNDICATE_KEY ) ) ) );
}

/**
* @inheritDoc
*/
public function decrypt( $data, $associative = true ) {
// phpcs:ignore PHPCompatibility.FunctionUse.RemovedFunctions.mcrypt_decryptDeprecatedRemoved,PHPCompatibility.Extensions.RemovedExtensions.mcryptDeprecatedRemoved,PHPCompatibility.Constants.RemovedConstants.mcrypt_rijndael_256DeprecatedRemoved,PHPCompatibility.Constants.RemovedConstants.mcrypt_mode_cbcDeprecatedRemoved
$data = rtrim( mcrypt_decrypt( MCRYPT_RIJNDAEL_256, md5( PUSH_SYNDICATE_KEY ), base64_decode( $data ), MCRYPT_MODE_CBC, md5( md5( PUSH_SYNDICATE_KEY ) ) ), "\0" );
if ( ! $data ) {
return false;
}
// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_unserialize,WordPress.PHP.NoSilencedErrors.Discouraged
return @unserialize( $data );
}

/**
* @inheritDoc
*/
public function get_cipher() {
return MCRYPT_RIJNDAEL_256; // phpcs:ignore PHPCompatibility.Constants.RemovedConstants.mcrypt_rijndael_256DeprecatedRemoved
}
}
63 changes: 63 additions & 0 deletions includes/class-syndication-encryptor-openssl.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php

/**
* Class Syndication_Encryptor_OpenSSL
*/
class Syndication_Encryptor_OpenSSL implements Syndication_Encryptor {

/**
* The cipher to be used for encryption.
*
* @var string
*/
private $cipher = 'aes-256-cbc';

/**
* @inheritDoc
*/
public function encrypt( $data ) {
$data = wp_json_encode( $data );
$cipher = $this->get_cipher();

if ( ! $cipher ) {
return $data;
}

$encrypted_data = openssl_encrypt( $data, $cipher['cipher'], $cipher['key'], 0, $cipher['iv'] );
return base64_encode( $encrypted_data ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
}

/**
* @inheritDoc
*/
public function decrypt( $data, $associative = true ) {
$cipher = $this->get_cipher();

if ( ! $cipher ) {
return $data;
}

$data = openssl_decrypt( base64_decode( $data ), $cipher['cipher'], $cipher['key'], 0, $cipher['iv'] ); //phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode

if ( ! $data ) {
return false;
}

return json_decode( $data, $associative );
}

/**
* @inheritDoc
*/
public function get_cipher() {
if ( in_array( $this->cipher, openssl_get_cipher_methods(), true ) ) {
return array(
'cipher' => $this->cipher,
'iv' => substr( md5( md5( PUSH_SYNDICATE_KEY ) ), 0, 16 ),
'key' => md5( PUSH_SYNDICATE_KEY ),
);
}

return false; // @TODO: return another default cipher? return exception?
}
}
2 changes: 1 addition & 1 deletion includes/class-wp-push-syndication-server.php
Original file line number Diff line number Diff line change
Expand Up @@ -1225,7 +1225,7 @@ public function cron_add_pull_time_interval( $schedules ) {

// Adds the custom time interval to the existing schedules.
$schedules['syn_pull_time_interval'] = array(
'interval' => intval( $this->push_syndicate_settings['pull_time_interval'] ),
'interval' => isset( $this->push_syndicate_settings ) ? intval( $this->push_syndicate_settings['pull_time_interval'] ) : 0,
'display' => __( 'Pull Time Interval', 'push-syndication' )
);

Expand Down
33 changes: 33 additions & 0 deletions includes/interface-syndication-encryptor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php
GaryJones marked this conversation as resolved.
Show resolved Hide resolved

/**
* Interface Syndication_Encryptor
*/
interface Syndication_Encryptor {

/**
* Encrypts data.
*
* @param string|array $data Data to be encrypted.
*
* @return string
*/
public function encrypt( $data );

/**
* Decrypts data
*
* @param string $data Data to be decrypted.
* @param bool $associative If true, returns as an associative array. Otherwise returns as a class.
*
* @return mixed
*/
public function decrypt( $data, $associative = true );

/**
* Returns the cipher being used. It can be a string, or a array with the cipher, key and iv.
*
* @return string|array
*/
public function get_cipher();
}
34 changes: 21 additions & 13 deletions includes/push-syndicate-encryption.php
Original file line number Diff line number Diff line change
@@ -1,18 +1,26 @@
<?php

function push_syndicate_encrypt( $data ) {

$data = serialize( $data );
return base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, md5(PUSH_SYNDICATE_KEY), $data, MCRYPT_MODE_CBC, md5(md5(PUSH_SYNDICATE_KEY))));
require_once dirname( __FILE__ ) . '/class-syndication-encryption.php';
vaurdan marked this conversation as resolved.
Show resolved Hide resolved

/**
* Encrypts data.
*
* @param string $data The data to encrypt.
*
* @return false|string
*/
function push_syndicate_encrypt( $data ) {
vaurdan marked this conversation as resolved.
Show resolved Hide resolved
return Syndication_Encryption::encrypt( $data );
}

function push_syndicate_decrypt( $data ) {

$data = rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, md5(PUSH_SYNDICATE_KEY), base64_decode($data), MCRYPT_MODE_CBC, md5(md5(PUSH_SYNDICATE_KEY))), "\0");
if ( !$data )
return false;

return @unserialize( $data );

}
/**
* Decrypts data.
*
* @param string $data The encrypted data to decrypt.
* @param bool $associative If true, returns as an associative array. Otherwise returns as a class.
*
* @return array|false|object
*/
function push_syndicate_decrypt( $data, $associative = true ) {
return Syndication_Encryption::decrypt( $data, $associative );
}
28 changes: 22 additions & 6 deletions push-syndication.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,23 @@

define( 'SYNDICATION_VERSION', 2.0 );

if ( ! defined( 'PUSH_SYNDICATE_KEY' ) )
if ( ! defined( 'PUSH_SYNDICATE_KEY' ) ) {
define( 'PUSH_SYNDICATE_KEY', 'PUSH_SYNDICATE_KEY' );
}

/**
* Load syndication logger
*/
require_once( dirname( __FILE__ ) . '/includes/class-syndication-logger.php' );
require_once dirname( __FILE__ ) . '/includes/class-syndication-logger.php';
Syndication_Logger::init();

require_once( dirname( __FILE__ ) . '/includes/class-wp-push-syndication-server.php' );
require_once dirname( __FILE__ ) . '/includes/class-wp-push-syndication-server.php';

if ( defined( 'WP_CLI' ) && WP_CLI )
require_once( dirname( __FILE__ ) . '/includes/class-wp-cli.php' );
if ( defined( 'WP_CLI' ) && WP_CLI ) {
require_once dirname( __FILE__ ) . '/includes/class-wp-cli.php';
}

$GLOBALS['push_syndication_server'] = new WP_Push_Syndication_Server;
$GLOBALS['push_syndication_server'] = new WP_Push_Syndication_Server();

// Create the event counter.
require __DIR__ . '/includes/class-syndication-event-counter.php';
Expand All @@ -38,3 +40,17 @@
// Create the site auto retry functionality
require __DIR__ . '/includes/class-syndication-site-auto-retry.php';
new Failed_Syndication_Auto_Retry();

// Load encryption classes
require_once dirname( __FILE__ ) . '/includes/class-syndication-encryption.php';
require_once dirname( __FILE__ ) . '/includes/interface-syndication-encryptor.php';
require_once dirname( __FILE__ ) . '/includes/class-syndication-encryptor-mcrypt.php';
require_once dirname( __FILE__ ) . '/includes/class-syndication-encryptor-openssl.php';

// On PHP 7.1 mcrypt is available, but will throw a deprecated error if its used. Therefore, checking for the
// PHP version, instead of checking for mcrypt is a better approach.
if ( version_compare( PHP_VERSION, '7.1', '<' ) ) {
Syndication_Encryption::set_encryptor( new Syndication_Encryptor_MCrypt() );
} else {
Syndication_Encryption::set_encryptor( new Syndication_Encryptor_OpenSSL() );
}
14 changes: 14 additions & 0 deletions tests/bootstrap.php
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
<?php
use Yoast\WPTestUtils\WPIntegration;

$_tests_dir = getenv( 'WP_TESTS_DIR' );
if ( ! $_tests_dir ) {
$_tests_dir = rtrim( sys_get_temp_dir(), '/\\' ) . '/wordpress-tests-lib';
putenv( 'WP_TESTS_DIR=' . $_tests_dir );
}

if ( ! file_exists( $_tests_dir . '/includes/functions.php' ) ) {
echo "Could not find $_tests_dir/includes/functions.php, have you run bin/install-wp-tests.sh ?" . PHP_EOL; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
exit( 1 );
Expand All @@ -18,3 +21,14 @@ function _manually_load_plugin() {

require $_tests_dir . '/includes/bootstrap.php';

/*
* Load WordPress, which will load the Composer autoload file, and load the MockObject autoloader after that.
*/
require_once dirname( __DIR__ ) . '/vendor/yoast/wp-test-utils/src/WPIntegration/bootstrap-functions.php';
WPIntegration\bootstrap_it();

/*
* Load tests dependencies
*/

require_once dirname( __FILE__ ) . '/class-encryptor-test-case.php';
Loading