Skip to content

Commit 9be26cb

Browse files
authored
Merge pull request #40 from tawk/fix/secure-mode-session-security
Fix/secure mode session security
2 parents f8c759c + 6a0c040 commit 9be26cb

File tree

5 files changed

+114
-46
lines changed

5 files changed

+114
-46
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"description": "Wordpress plugin for tawk.to",
44
"type": "project",
55
"license": "GPL-3.0",
6-
"version": "0.9.0",
6+
"version": "0.9.1",
77
"require": {
88
"tawk/url-utils": "2.0.1"
99
},

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "tawk-wordpress",
3-
"version": "0.9.0",
3+
"version": "0.9.1",
44
"description": "tawk.to wordpress plugin",
55
"main": "index.js",
66
"directories": {

tawkto/readme.txt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Tags: tawk.to,tawk,free live chat,ai chat,chat widget
44
Requires at least: 2.7
55
Requires PHP: 5.6
66
Tested up to: 6.7
7-
Stable tag: 0.9.0
7+
Stable tag: 0.9.1
88
License: GPLv3
99
License URI: https://www.gnu.org/licenses/gpl-3.0.html
1010

@@ -71,7 +71,11 @@ Note: You will need a free tawk.to account: [Create one for free here!](https://
7171

7272
== Changelog ==
7373

74-
= 0.9.0
74+
= 0.9.1 =
75+
* Add configuration versioning
76+
* Improve security for Secure Mode
77+
78+
= 0.9.0 =
7579
* Add support for Secure Mode
7680

7781
= 0.8.7 =

tawkto/tawkto.php

Lines changed: 104 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* Plugin Name: Tawk.to Live Chat
77
* Plugin URI: https://www.tawk.to
88
* Description: Embeds Tawk.to live chat widget to your site
9-
* Version: 0.9.0
9+
* Version: 0.9.1
1010
* Author: Tawkto
1111
* Text Domain: tawk-to-live-chat
1212
* License: GPLv3
@@ -27,12 +27,12 @@ class TawkTo_Settings {
2727
const TAWK_VISIBILITY_OPTIONS = 'tawkto-visibility-options';
2828
const TAWK_PRIVACY_OPTIONS = 'tawkto-privacy-options';
2929
const TAWK_SECURITY_OPTIONS = 'tawkto-security-options';
30+
const TAWK_CONFIG_VERSION = 'tawkto-config-version';
3031
const TAWK_ACTION_SET_WIDGET = 'tawkto-set-widget';
3132
const TAWK_ACTION_REMOVE_WIDGET = 'tawkto-remove-widget';
3233
const CIPHER = 'AES-256-CBC';
3334
const CIPHER_IV_LENGTH = 16;
3435
const NO_CHANGE = 'nochange';
35-
const TAWK_API_KEY = 'tawkto-js-api-key';
3636

3737
/**
3838
* @var $plugin_ver Plugin version
@@ -132,6 +132,7 @@ public function admin_init() {
132132
register_setting( 'tawk_options', self::TAWK_VISIBILITY_OPTIONS, array( &$this, 'validate_visibility_options' ) );
133133
register_setting( 'tawk_options', self::TAWK_PRIVACY_OPTIONS, array( &$this, 'validate_privacy_options' ) );
134134
register_setting( 'tawk_options', self::TAWK_SECURITY_OPTIONS, array( &$this, 'validate_security_options' ) );
135+
register_setting( 'tawk_options', self::TAWK_CONFIG_VERSION, array( &$this, 'update_config_version' ) );
135136
}
136137

137138
/**
@@ -326,6 +327,15 @@ public function validate_security_options( $input ) {
326327
return $security;
327328
}
328329

330+
/**
331+
* Updates the config version
332+
*
333+
* @return int
334+
*/
335+
public function update_config_version() {
336+
return get_option( self::TAWK_CONFIG_VERSION, 0 ) + 1;
337+
}
338+
329339
/**
330340
* Adds the tawk.to plugin settings in the admin menu.
331341
*/
@@ -418,20 +428,20 @@ private static function validate_js_api_key( &$fields ) {
418428
return;
419429
}
420430

421-
delete_transient( self::TAWK_API_KEY );
422-
423431
if ( '' === $fields['js_api_key'] ) {
424432
return;
425433
}
426434

427-
try {
428-
if ( 40 !== strlen( $fields['js_api_key'] ) ) {
429-
throw new Exception( 'Invalid key. Please provide value with 40 characters' );
430-
}
435+
$fields['js_api_key'] = trim( $fields['js_api_key'] );
431436

437+
if ( 40 !== strlen( $fields['js_api_key'] ) ) {
438+
self::show_tawk_options_error( 'Invalid API key' );
439+
}
440+
441+
try {
432442
$fields['js_api_key'] = self::get_encrypted_data( $fields['js_api_key'] );
433443
} catch ( Exception $e ) {
434-
self::show_tawk_options_error( 'Javascript API Key: ' . $e->getMessage() );
444+
self::show_tawk_options_error( 'Error saving Javascript API Key' );
435445

436446
unset( $fields['js_api_key'] );
437447
}
@@ -524,12 +534,12 @@ private static function get_encrypted_data( $data ) {
524534
* @param string $data - Data to be decrypted.
525535
* @return string
526536
*/
527-
private static function get_decrypted_data( $data ) {
537+
public static function get_decrypted_data( $data ) {
528538
// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
529539
$decoded_data = base64_decode( $data );
530540

531541
if ( false === $decoded_data ) {
532-
return '';
542+
return null;
533543
}
534544

535545
$iv = substr( $decoded_data, 0, self::CIPHER_IV_LENGTH );
@@ -538,35 +548,12 @@ private static function get_decrypted_data( $data ) {
538548
$decrypted_data = openssl_decrypt( $encrypted_data, self::CIPHER, SECURE_AUTH_KEY, 0, $iv );
539549

540550
if ( false === $decrypted_data ) {
541-
return '';
551+
return null;
542552
}
543553

544554
return $decrypted_data;
545555
}
546556

547-
/**
548-
* Retrieves JS API Key
549-
*
550-
* @return string
551-
*/
552-
public static function get_js_api_key() {
553-
if ( ! empty( get_transient( self::TAWK_API_KEY ) ) ) {
554-
return get_transient( self::TAWK_API_KEY );
555-
}
556-
557-
$security = get_option( self::TAWK_SECURITY_OPTIONS );
558-
559-
if ( ! isset( $security['js_api_key'] ) ) {
560-
return '';
561-
}
562-
563-
$key = self::get_decrypted_data( $security['js_api_key'] );
564-
565-
set_transient( self::TAWK_API_KEY, $key, 60 * 60 );
566-
567-
return $key;
568-
}
569-
570557
/**
571558
* Adds settings error
572559
*
@@ -599,6 +586,7 @@ private static function show_tawk_options_error( $message ) {
599586
*/
600587
class TawkTo {
601588
const PLUGIN_VERSION_VARIABLE = 'tawkto-version';
589+
const TAWK_VISITOR_SESSION = 'tawkto-visitor-session';
602590

603591
/**
604592
* @var $plugin_version Plugin version
@@ -613,6 +601,41 @@ class TawkTo {
613601
public function __construct() {
614602
$tawkto_settings = new TawkTo_Settings();
615603
add_shortcode( 'tawkto', array( $this, 'shortcode_print_embed_code' ) );
604+
605+
add_action( 'init', array( $this, 'start_session' ) );
606+
}
607+
608+
609+
/**
610+
* Starts user session
611+
*
612+
* @return void
613+
*/
614+
public function start_session() {
615+
$privacy = get_option( TawkTo_Settings::TAWK_PRIVACY_OPTIONS );
616+
617+
if ( empty( $privacy['enable_visitor_recognition'] ) ) {
618+
return;
619+
}
620+
621+
$security = get_option( TawkTo_Settings::TAWK_SECURITY_OPTIONS );
622+
623+
if ( empty( $security['js_api_key'] ) ) {
624+
return;
625+
}
626+
627+
if ( session_status() === PHP_SESSION_NONE ) {
628+
session_start();
629+
630+
// If user is not logged in, remove the visitor session and close the session.
631+
// Session cannot be updated if it is not started.
632+
if ( ! is_user_logged_in() ) {
633+
if ( isset( $_SESSION[ self::TAWK_VISITOR_SESSION ] ) ) {
634+
unset( $_SESSION[ self::TAWK_VISITOR_SESSION ] );
635+
}
636+
session_write_close();
637+
}
638+
}
616639
}
617640

618641
/**
@@ -658,9 +681,8 @@ public static function deactivate() {
658681
delete_option( TawkTo_Settings::TAWK_VISIBILITY_OPTIONS );
659682
delete_option( TawkTo_Settings::TAWK_PRIVACY_OPTIONS );
660683
delete_option( TawkTo_Settings::TAWK_SECURITY_OPTIONS );
684+
delete_option( TawkTo_Settings::TAWK_CONFIG_VERSION );
661685
delete_option( self::PLUGIN_VERSION_VARIABLE );
662-
663-
delete_transient( TawkTo_Settings::TAWK_API_KEY );
664686
}
665687

666688
/**
@@ -683,16 +705,58 @@ public function get_current_customer_details() {
683705
'email' => $current_user->user_email,
684706
);
685707

686-
$js_api_key = TawkTo_Settings::get_js_api_key();
687-
if ( ! empty( $user_info['email'] ) && ! empty( $js_api_key ) ) {
688-
$user_info['hash'] = hash_hmac( 'sha256', $user_info['email'], $js_api_key );
708+
$hash = self::get_visitor_hash( $user_info['email'] );
709+
if ( null !== $hash ) {
710+
$user_info['hash'] = $hash;
689711
}
690712

691713
return wp_json_encode( $user_info );
692714
}
693715
return null;
694716
}
695717

718+
/**
719+
* Retrieves visitor hash
720+
*
721+
* @param string $email - Visitor email address.
722+
* @return string
723+
*/
724+
public static function get_visitor_hash( $email ) {
725+
$security = get_option( TawkTo_Settings::TAWK_SECURITY_OPTIONS );
726+
727+
if ( empty( $security['js_api_key'] ) ) {
728+
return null;
729+
}
730+
731+
$config_version = get_option( TawkTo_Settings::TAWK_CONFIG_VERSION, 0 );
732+
733+
if ( isset( $_SESSION[ self::TAWK_VISITOR_SESSION ] ) ) {
734+
$current_session = $_SESSION[ self::TAWK_VISITOR_SESSION ];
735+
736+
if ( isset( $current_session['hash'] ) &&
737+
$current_session['email'] === $email &&
738+
$current_session['config_version'] === $config_version ) {
739+
return $current_session['hash'];
740+
}
741+
}
742+
743+
$key = TawkTo_Settings::get_decrypted_data( $security['js_api_key'] );
744+
745+
if ( null === $key ) {
746+
return null;
747+
}
748+
749+
$hash = hash_hmac( 'sha256', $email, $key );
750+
751+
$_SESSION[ self::TAWK_VISITOR_SESSION ] = array(
752+
'hash' => $hash,
753+
'email' => $email,
754+
'config_version' => $config_version,
755+
);
756+
757+
return $hash;
758+
}
759+
696760
/**
697761
* Creates the embed code
698762
*/

tawkto/templates/widget.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
?>
1010

11-
<!--Start of Tawk.to Script (0.9.0)-->
11+
<!--Start of Tawk.to Script (0.9.1)-->
1212
<script id="tawk-script" type="text/javascript">
1313
var Tawk_API = Tawk_API || {};
1414
<?php if ( isset( $customer_details ) && $enable_visitor_recognition ) : ?>
@@ -24,5 +24,5 @@
2424
s0.parentNode.insertBefore( s1, s0 );
2525
})();
2626
</script>
27-
<!--End of Tawk.to Script (0.9.0)-->
27+
<!--End of Tawk.to Script (0.9.1)-->
2828

0 commit comments

Comments
 (0)