Skip to content

Commit

Permalink
Option protection.
Browse files Browse the repository at this point in the history
Cleaned up code.
  • Loading branch information
sybrew committed Aug 20, 2016
1 parent 57661a6 commit 7a9ff5e
Show file tree
Hide file tree
Showing 16 changed files with 569 additions and 656 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@
tsf_extension_manager_load_trait( 'activation' );

/**
* Class TSF_Extension_Manager\Activation
* Class TSF_Extension_Manager\AccountActivation
*
* Holds plugin activation functions.
*
* @since 1.0.0
*/
class Activation extends Panes {
class AccountActivation extends Panes {
use Enclose, Construct_Sub, Activation_Data;

/**
Expand Down Expand Up @@ -245,17 +245,19 @@ protected function handle_premium_deactivation( $results ) {
*/
protected function do_free_activation() {

$success = $this->update_option_multi( array(
$options = array(
'api_key' => '',
'activation_email' => '',
'_activation_level' => 'Free',
'_activation_expires' => '',
'_activated' => 'Activated',
'_instance' => false,
'_instance' => $this->get_activation_instance( false ),
'_data' => array(),
) );
);
$success = $this->update_option_multi( $options );

if ( $success ) {
$this->set_options_instance( $options );
$this->set_error_notice( array( 601 => '' ) );
return true;
} else {
Expand Down Expand Up @@ -325,6 +327,7 @@ protected function do_deactivation() {

$success = array();

$success[] = $this->delete_options_instance();
$success[] = $this->update_option( 'api_key', '' );
$success[] = $this->update_option( 'activation_email', '' );
$success[] = $this->update_option( '_activation_level', '' );
Expand Down Expand Up @@ -459,7 +462,7 @@ protected function update_extra_subscription_data( $doing_activation = false ) {

$data['expire'] = isset( $response['status_extra']['end_date'] ) ? $response['status_extra']['end_date'] : null;

$success = $this->update_option( '_data', $data );
$success = $this->update_option( '_data', $data, 'instance', false );

return $success;
}
Expand Down
6 changes: 2 additions & 4 deletions inc/classes/adminpages.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
*
* @since 1.0.0
*/
class AdminPages extends Activation {
class AdminPages extends AccountActivation {
use Enclose, Construct_Sub;

/**
Expand Down Expand Up @@ -400,9 +400,7 @@ public function init_extension_footer_wrap() {
* @since 1.0.0
*/
public function output_theme_color_meta() {
echo '<meta name="theme-color" content="#0ebfe9" />';
echo '<meta name="msapplication-navbutton-color" content="#0ebfe9" />';
echo '<meta name="apple-mobile-web-app-status-bar-style" content="#0ebfe9" />';
$this->get_view( 'layout/pages/meta' );
}

/**
Expand Down
213 changes: 203 additions & 10 deletions inc/classes/core.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -157,21 +157,36 @@ public function handle_update_post() {
$this->set_error_notice( array( $code => '' ) );
break;

case $this->request_name['activate-ext'] :
$success = $this->activate_extension( $options );
$code = $success ? 704 : 705; // @todo var_dump() set errors.
$this->set_error_notice( array( $code => '' ) );
break;

case $this->request_name['deactivate-ext'] :
$success = $this->deactivate_extension( $options );
$code = $success ? 706 : 707; // @todo var_dump() set errors.
$this->set_error_notice( array( $code => '' ) );
break;

default:
$this->set_error_notice( array( 704 => '' ) );
$this->set_error_notice( array( 708 => '' ) );
break;
endswitch;

//* Adds action to the URI. It's only used to visualize what has happened.
the_seo_framework()->admin_redirect( $this->seo_extensions_page_slug, array( 'did-' . $options['action'] => 'true' ) );
$args = WP_DEBUG ? array( 'did-' . $options['action'] => 'true' ) : array();
the_seo_framework()->admin_redirect( $this->seo_extensions_page_slug, $args );
exit;
}

/**
* Checks the Activation page nonce. Returns false if nonce can't be found or if user isn't allowed to perform nonce.
* Checks the Activation page nonce. Returns false if nonce can't be found
* or if user isn't allowed to perform nonce.
* Performs wp_die() when nonce verification fails.
*
* Never run a sensitive function when it's returning false. This means no nonce can be verified.
* Never run a sensitive function when it's returning false. This means no
* nonce can or has been been verified.
*
* @since 1.0.0
* @staticvar bool $validated Determines whether the nonce has already been verified.
Expand Down Expand Up @@ -199,7 +214,7 @@ public function handle_update_nonce( $key = 'default', $check_post = true ) {
* @since 1.0.0
*/
if ( empty( $_POST ) || ! isset( $_POST[ TSF_EXTENSION_MANAGER_SITE_OPTIONS ] ) || ! is_array( $_POST[ TSF_EXTENSION_MANAGER_SITE_OPTIONS ] ) )
return $validated = false;
return $validated[ $key ] = false;
}

check_admin_referer( $this->nonce_action[ $key ], $this->nonce_name );
Expand Down Expand Up @@ -281,7 +296,7 @@ protected function get_error_notice( $option ) {
case 103 :
case 104 :
case 701 :
case 704 :
case 708 :
$message = esc_html__( 'Invalid API request type.', 'the-seo-framework-extension-manager' );
$type = 'error';
break;
Expand Down Expand Up @@ -356,6 +371,12 @@ protected function get_error_notice( $option ) {
$type = 'updated';
break;

case 7001 :
case 7002 :
$message = esc_html__( 'An error occured while verifying the options. If this error keeps coming back, please deactivate your account and try again.', 'the-seo-framework-extension-manager' );
$type = 'error';
break;

case 9001 :
$message = esc_html__( 'Nonce verification failed. Please try again.', 'the-seo-framework-extension-manager' );
$type = 'error';
Expand Down Expand Up @@ -709,9 +730,11 @@ protected function has_run_update_option() {
*
* @param string $option The option name.
* @param mixed $value The option value.
* @param string $type The option update type, accepts 'instance' and 'regular'.
* @param bool $kill Whether to kill the plugin on invalid instance.
* @return bool True on success or the option is unchanged, false on failure.
*/
protected function update_option( $option, $value ) {
protected function update_option( $option, $value, $type = 'instance', $kill = true ) {

if ( ! $option )
return false;
Expand All @@ -726,7 +749,14 @@ protected function update_option( $option, $value ) {

$this->has_run_update_option();

return update_option( TSF_EXTENSION_MANAGER_SITE_OPTIONS, $options );
$this->initialize_option_update_instance( $type );

$success = update_option( TSF_EXTENSION_MANAGER_SITE_OPTIONS, $options );

if ( false === $this->verify_option_update_instance( $kill ) )
return false;

return $success;
}

/**
Expand All @@ -737,9 +767,11 @@ protected function update_option( $option, $value ) {
* @param array $options : {
* $option_name => $value,
* }
* @param string $type The option update type, accepts 'instance' and 'regular'.
* @param bool $kill Whether to kill the plugin on invalid instance.
* @return bool True on success, false on failure or when options haven't changed.
*/
protected function update_option_multi( array $options = array() ) {
protected function update_option_multi( array $options = array(), $type = 'instance', $kill = true ) {

static $run = false;

Expand All @@ -765,6 +797,167 @@ protected function update_option_multi( array $options = array() ) {
$options = wp_parse_args( $options, $_options );
$run = true;

return update_option( TSF_EXTENSION_MANAGER_SITE_OPTIONS, $options );
$this->initialize_option_update_instance( $type );

$success = update_option( TSF_EXTENSION_MANAGER_SITE_OPTIONS, $options );

if ( false === $this->verify_option_update_instance( $kill ) )
return false;

return true;
}

/**
* Returns verification instance option.
*
* @since 1.0.0
*
* @return string The hashed option.
*/
protected function get_options_instance() {
return $this->get_option( $this->get_option( '_instance' ) );
}

/**
* Binds options to an unique hash and saves it in a comparison option.
* This prevents users from altering the options from outside this plugin.
*
* @since 1.0.0
* @param array $options The options to hash.
*
* @return bool True on success, false on failure.
*/
protected function set_options_instance( $options ) {

if ( empty( $options['instance'] ) )
return false;

$_options = serialize( $options );
$hash = $this->make_hash( $_options, 'auth' );

if ( $hash ) {
$update = update_option( $options['instance'], $hash );

if ( false === $update ) {
$this->set_error_notice( array( 7001 => '' ) );
return false;
}
return true;
} else {
$this->set_error_notice( array( 7002 => '' ) );
return false;
}
}

/**
* Deletes option instance on account deactivation.
*
* @since 1.0.0
*
* @return bool True on success, false on failure.
*/
protected function delete_options_instance() {

$instance = $this->get_option( '_instance' );
delete_option( $instance );

return true;
}

/**
* Returns hash key based on sha256 if available. Otherwise it will fall back
* to md5 (wp_hash()). Hash will alter every 24 hours.
*
* @since 1.0.0
* @see @link https://developer.wordpress.org/reference/functions/wp_hash/
*
* @param string $data The data to hash.
* @return string The hash key.
*/
protected function make_hash( $data ) {

if ( in_array( 'sha256', hash_algos(), true ) ) {
$salt = wp_salt( 'auth' );
$hash = hash_hmac( 'sha256', $data, $salt );
} else {
$hash = wp_hash( $data, 'auth' );
}

return $hash;
}

/**
* Verifies options hash
*
* @since 1.0.0
*
* @param string $data The data to compare hash with.
* @return bool True when hash passes, false on failure.
*/
public function verify_options( $data ) {
return hash_equals( $this->make_hash( $data ), $this->get_options_instance() );
}

/**
* Initializes option update instance.
*
* @since 1.0.0
*
* @param string $type What type of update this is, accepts 'instance' and 'regular'.
*/
protected function initialize_option_update_instance( $type = '' ) {

if ( 'instance' === $type ) {
$type = 'update_option_instance';
} elseif ( 'regular' === $type ) {
$type = 'update_option';
}

$bits = $this->get_bits();
$_instance = $this->get_verification_instance( $bits[1] );

SecureOption::initialize( $type, $_instance, $bits );

$bits = $this->get_bits();
$_instance = $this->get_verification_instance( $bits[1] );
SecureOption::set_update_instance( $_instance, $bits );

}

/**
* Verifies if update went as expected.
* Deletes plugin options if data has been adjusted.
*
* @since 1.0.0
*
* @param bool $kill Whether to kill plugin options.
* @return bool True on success, false on failure.
*/
protected function verify_option_update_instance( $kill = true ) {

$verify = SecureOption::verified_option_update();

if ( false === $verify && $kill )
$this->kill_options();

SecureOption::reset();

return $verify;
}

/**
* Deletes all plugin options when an options breach has been spotted.
*
* @since 1.0.0
*
* @return bool True on success, false on failure.
*/
protected function kill_options() {

$success = array();
$success[] = $this->delete_options_instance();
$success[] = delete_option( TSF_EXTENSION_MANAGER_SITE_OPTIONS );

return ! in_array( false, $success, true );
}
}
Loading

0 comments on commit 7a9ff5e

Please sign in to comment.