Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 5 additions & 2 deletions cleantalk.php
Original file line number Diff line number Diff line change
Expand Up @@ -782,14 +782,17 @@ function apbct_wpms__delete_blog(WP_Site $old_site)
// if User is not logged in
// if Unknown action or Known action with mandatory check
if (
( ! apbct_is_user_logged_in() || $apbct->settings['data__protect_logged_in'] == 1) &&
Post::get('action') !== '' &&
(
! in_array(Post::get('action'), $_cleantalk_hooked_actions) ||
in_array(Post::get('action'), $_cleantalk_ajax_actions_to_check)
)
) {
add_action('plugins_loaded', 'ct_ajax_hook');
add_action('plugins_loaded', function () use ($apbct) {
if ( ! apbct_is_user_logged_in() || $apbct->settings['data__protect_logged_in'] == 1 ) {
ct_ajax_hook();
}
});
}

//QAEngine Theme answers
Expand Down
60 changes: 43 additions & 17 deletions inc/cleantalk-pluggable.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
use Cleantalk\ApbctWP\Constant;
use Cleantalk\ApbctWP\Helper;
use Cleantalk\ApbctWP\RemoteCalls;
use Cleantalk\ApbctWP\Variables\AltSessions;
use Cleantalk\ApbctWP\Variables\Cookie;
use Cleantalk\ApbctWP\Variables\Get;
use Cleantalk\ApbctWP\Variables\Post;
use Cleantalk\ApbctWP\Variables\Request;
Expand Down Expand Up @@ -82,6 +84,10 @@ function apbct_wp_set_current_user($user = null)
*/
function apbct_wp_validate_auth_cookie($cookie = '', $scheme = '')
{
if ( function_exists('wp_validate_auth_cookie') ) {
return wp_validate_auth_cookie($cookie, $scheme);
}

$cookie_elements = apbct_wp_parse_auth_cookie($cookie, $scheme);

if (!is_array($cookie_elements) || empty($cookie_elements)) {
Expand Down Expand Up @@ -109,17 +115,8 @@ function apbct_wp_validate_auth_cookie($cookie = '', $scheme = '')
$algo = function_exists('hash') ? 'sha256' : 'sha1';
$hash = hash_hmac($algo, $username . '|' . $expiration . '|' . $token, $key);
if ( hash_equals($hash, $hmac) && is_object($user) ) {
$sessions = get_user_meta($user->ID, 'session_tokens', true);
$sessions = is_array($sessions) ? current($sessions) : $sessions;
if ( is_array($sessions) ) {
if ( isset($sessions['expiration']) && is_int($sessions['expiration']) && $sessions['expiration'] > time() ) {
return $user->ID;
} else {
return false;
}
} else {
return false;
}
// The session of this very token must be active, not just any session of the user.
return WP_Session_Tokens::get_instance($user->ID)->verify($token) ? $user->ID : false;
} else {
return false;
}
Expand Down Expand Up @@ -412,16 +409,18 @@ function apbct_is_cli()
}

/**
* Checks if the user is logged in
* Checks WordPress authentication. Before pluggable functions load, no user is trusted.
*
* @return bool
*/
function apbct_is_user_logged_in()
{
$siteurl = get_site_option('siteurl');
$cookiehash = $siteurl ? md5($siteurl) : '';
if ( did_action('set_current_user') && function_exists('is_user_logged_in') ) {
return is_user_logged_in();
}

return count($_COOKIE) && isset($_COOKIE['wordpress_logged_in_' . $cookiehash]);
// Resolving the current user here would freeze it before themes/plugins add determine_current_user filters.
return function_exists('wp_validate_auth_cookie') && (bool)wp_validate_auth_cookie('', 'logged_in');
}

function apbct_is_post()
Expand Down Expand Up @@ -2297,15 +2296,42 @@ function apbct__is_wordpress_loopback_request()
}

/**
* Generates MD5 hash for email encoder pass key
* Generates MD5 hash for email encoder pass key.
* The key is public, so it only hints that the visitor may have passed the decoding check.
*
* @return string
*/
function apbct_get_email_encoder_pass_key()
{
global $apbct;

return md5(Helper::ipGet() . $apbct->api_key . 'email_encoder');
return md5(Helper::ipGet('real', false) . $apbct->api_key . 'email_encoder');
}

/**
* Remembers on the server side that the visitor passed the contact decoding check.
*
* @return void
*/
function apbct_email_encoder_set_passed()
{
AltSessions::set('apbct_email_encoder_verified', '1');
}

/**
* Checks whether contacts may be shown without encoding to the visitor who passed the decoding check.
*
* @return bool
*/
function apbct_is_email_encoder_passed()
{
// Cheap check first, so the sessions storage is queried only for visitors who decoded contacts.
if ( Cookie::get('apbct_email_encoder_passed') !== apbct_get_email_encoder_pass_key() ) {
return false;
}

// The verified mark is not in the list of values a visitor can set remotely.
return AltSessions::get('apbct_email_encoder_verified') === '1';
}

/**
Expand Down
2 changes: 1 addition & 1 deletion inc/cleantalk-public-validate.php
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ function ct_contact_form_validate()
}

// Do not execute anti-spam test for logged in users.
if ( defined('LOGGED_IN_COOKIE') && isset($_COOKIE[LOGGED_IN_COOKIE]) && $apbct->settings['data__protect_logged_in'] != 1 ) {
if ( $apbct->settings['data__protect_logged_in'] != 1 && apbct_is_user_logged_in() ) {
do_action('apbct_skipped_request', __FILE__ . ' -> ' . __FUNCTION__ . '():' . __LINE__, $_POST);

return null;
Expand Down
52 changes: 49 additions & 3 deletions lib/Cleantalk/ApbctWP/Antispam/ForceProtection.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Cleantalk\ApbctWP\ContactsEncoder\PlaceholderNonce;
use Cleantalk\ApbctWP\Helper;
use Cleantalk\ApbctWP\RequestParameters\RequestParameters;
use Cleantalk\ApbctWP\Variables\AltSessions;
use Cleantalk\Variables\Post;
use Cleantalk\Antispam\CleantalkRequest;
use Cleantalk\Antispam\Cleantalk;
Expand Down Expand Up @@ -109,7 +110,12 @@ public function checkBot()
),
));
}
RequestParameters::set('apbct_force_protection_check', TT::toString($api_response->allow));
$pass_token = '0';
if ( (int)$api_response->allow === 1 ) {
$expires = time() + DAY_IN_SECONDS;
$pass_token = $expires . '.' . $this->getPassSignature($expires);
}
RequestParameters::set('apbct_force_protection_check', $pass_token);

return json_encode(array(
'allow' => $api_response->allow,
Expand All @@ -126,11 +132,15 @@ public function checkBot()
*/
private function checkIframeForms($content)
{
$content = preg_replace_callback('#<iframe\b[^>]*>.*?<\/iframe>#is', function ($matches) {
$is_passed = null;
$content = preg_replace_callback('#<iframe\b[^>]*>.*?<\/iframe>#is', function ($matches) use (&$is_passed) {

$iframe = isset($matches[0]) ? $matches[0] : '';

if ( RequestParameters::get('apbct_force_protection_check') ) {
if ( $is_passed === null ) {
$is_passed = $this->hasValidPassToken();
}
if ( $is_passed ) {
return $iframe;
}

Expand All @@ -155,6 +165,42 @@ private function checkIframeForms($content)
return $content;
}

/**
* @return bool
*/
private function hasValidPassToken()
{
$token = RequestParameters::get('apbct_force_protection_check');
if (
! is_string($token) ||
! preg_match('/^([0-9]{1,10})\.([a-f0-9]{64})$/D', $token, $parts) ||
! isset($parts[1], $parts[2])
) {
return false;
}

$expires = (int)$parts[1];
if ( $expires <= time() || $expires > time() + DAY_IN_SECONDS ) {
return false;
}

return hash_equals($this->getPassSignature($expires), $parts[2]);
}

/**
* Bind a successful challenge to this site, visitor and expiration time.
*
* @param int $expires
* @return string
*/
private function getPassSignature($expires)
{
$data = 'apbct_force_protection_check|'
. get_home_url() . '|' . AltSessions::getID() . '|' . $expires;
Comment thread
AntonV1211 marked this conversation as resolved.
Resolved

return hash_hmac('sha256', $data, wp_salt('auth'));
}

/**
* Check external forms.
*
Expand Down
28 changes: 28 additions & 0 deletions lib/Cleantalk/ApbctWP/ContactsEncoder/ContactsEncoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,16 +46,41 @@ class ContactsEncoder extends \Cleantalk\Common\ContactsEncoder\ContactsEncoder
*/
private $comment;

/**
* @var Params
*/
private $params;

public function init($params)
{
parent::init($params);
$this->params = $params;
$this->exclusions = new ExclusionsService($params);
$this->shortcodes = new ShortCodesService($params);
$this->grid_builder_integration = new CEIntegrationGridBuilder();

$this->shortcodes->registerAll();

$this->registerHookHandler();

// The instance is created on plugin load, before WordPress authenticates the visitor.
add_action('set_current_user', array($this, 'syncLoggedInState'));
}

/**
* @return void
*/
public function syncLoggedInState()
{
$is_logged_in = apbct_is_user_logged_in();
if ( $is_logged_in === $this->is_logged_in ) {
return;
}

// Params are shared with the exclusion services.
$this->params->is_logged_in = $is_logged_in;
$this->is_logged_in = $is_logged_in;
$this->encoder->useSSL(! $is_logged_in);
}

/**
Expand Down Expand Up @@ -814,6 +839,9 @@ public function ajaxDecodeEmailHandler()
$response = $this->compileResponse($this->decoded_contacts_array, false);
wp_send_json_error($response);
}
if ( ! $this->is_logged_in && ! empty($this->decoded_contacts_array) && $this->is_encode_allowed ) {
apbct_email_encoder_set_passed();
}
$response = $this->compileResponse($this->decoded_contacts_array, $this->is_encode_allowed);
wp_send_json_success($response);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
namespace Cleantalk\ApbctWP\ContactsEncoder\Exclusions;

use Cleantalk\ApbctWP\State;
use Cleantalk\ApbctWP\Variables\Cookie;
use Cleantalk\ApbctWP\Variables\Post;
use Cleantalk\ApbctWP\Variables\Server;
use Cleantalk\Common\TT;
Expand Down Expand Up @@ -151,9 +150,7 @@ private function byServerVars()
*/
private function byDecoderPassedCookie()
{
$apbct_email_encoder_passed = Cookie::get('apbct_email_encoder_passed');

return $apbct_email_encoder_passed === apbct_get_email_encoder_pass_key();
return apbct_is_email_encoder_passed();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

use Cleantalk\ApbctWP\ContactsEncoder\ContactsEncoder;
use Cleantalk\ApbctWP\Escape;
use Cleantalk\ApbctWP\Variables\Cookie;
use Cleantalk\Common\ContactsEncoder\Dto\Params;
use Cleantalk\ApbctWP\ContactsEncoder\Exclusions\ExclusionsService;

Expand Down Expand Up @@ -68,7 +67,7 @@ public function callback($_atts, $content, $_tag)
return '';
}

if ( Cookie::get('apbct_email_encoder_passed') === apbct_get_email_encoder_pass_key() ) {
if ( apbct_is_email_encoder_passed() ) {
return (string)$content;
}

Expand Down
3 changes: 1 addition & 2 deletions lib/Cleantalk/ApbctWP/Variables/AltSessions.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,7 @@ class AltSessions
public static function getID()
{
// Accept-Language is omitted: XHR and document POST often send different headers.
$id = Helper::ipGet()
. Server::getString('HTTP_USER_AGENT');
$id = Helper::ipGet('real', false) . Server::getString('HTTP_USER_AGENT');

return substr(hash('sha256', $id), -16);
}
Expand Down
Loading
Loading