| @@ -52,9 +52,9 @@ | ||
| 52 | 52 | } |
| 53 | 53 | |
| 54 | 54 | if ( $ticket_id && $auth_code ) { |
| 55 | 55 | $ticket = new WPSC_Ticket( $ticket_id ); |
| 56 | - self::$url_auth = $ticket->auth_code == $auth_code ? true : false; | |
| 56 | + self::$url_auth = ( $ticket->auth_code && hash_equals( (string) $ticket->auth_code, $auth_code ) ); | |
| 57 | 57 | } |
| 58 | 58 | } |
| 59 | 59 | |
| 60 | 60 | ob_start();?> |
| @@ -208,9 +208,9 @@ | ||
| 208 | 208 | /* translators: %1$s: customer name */ |
| 209 | 209 | printf( esc_attr__( 'Logged-in as %1$s', 'supportcandy' ), '<strong>' . esc_attr( $current_user->customer->name ) . '</strong>' ); |
| 210 | 210 | ?> |
| 211 | 211 | </span> |
| 212 | - <a class="wpsc-link" href="javascript:wpsc_user_logout(this, '<?php echo esc_attr( wp_create_nonce( 'wpsc_user_logout' ) ); ?>');"><?php esc_attr_e( 'Log out', 'supportcandy' ); ?></a> | |
| 212 | + <span class="wpsc-link" onclick="wpsc_user_logout(this, '<?php echo esc_attr( wp_create_nonce( 'wpsc_user_logout' ) ); ?>');"><?php esc_attr_e( 'Log out', 'supportcandy' ); ?></span> | |
| 213 | 213 | </div> |
| 214 | 214 | <?php |
| 215 | 215 | endif |
| 216 | 216 | ?> |
| @@ -275,9 +275,9 @@ | ||
| 275 | 275 | */ |
| 276 | 276 | public static function get_authenticate_open_ticket() { |
| 277 | 277 | |
| 278 | 278 | if ( check_ajax_referer( 'wpsc_authenticate_open_ticket', '_ajax_nonce', false ) != 1 ) { |
| 279 | - wp_send_json_error( 'Unauthorised request!', 401 ); | |
| 279 | + wp_send_json_error( 'Unauthorized request!', 401 ); | |
| 280 | 280 | } |
| 281 | 281 | |
| 282 | 282 | $ticket_id = isset( $_POST['ticket_id'] ) ? intval( $_POST['ticket_id'] ) : 0; |
| 283 | 283 | $email_address = isset( $_POST['email_address'] ) ? sanitize_text_field( wp_unslash( $_POST['email_address'] ) ) : ''; |
| @@ -389,10 +389,20 @@ | ||
| 389 | 389 | * @return void |
| 390 | 390 | */ |
| 391 | 391 | public static function confirm_open_ticket_auth() { |
| 392 | 392 | |
| 393 | + // Rate limiting (mirrors WPSC_Current_User::confirm_guest_login()). | |
| 394 | + $ip_address = WPSC_DF_IP_Address::get_current_user_ip(); | |
| 395 | + $attempt_key = 'wpsc_otp_attempts_' . md5( $ip_address ); | |
| 396 | + $attempts = get_transient( $attempt_key ); | |
| 397 | + $attempts = $attempts ? $attempts : 1; | |
| 398 | + | |
| 399 | + if ( $attempts >= 5 ) { | |
| 400 | + wp_send_json_error( 'Too many attempts. Please try again later.', 429 ); | |
| 401 | + } | |
| 402 | + | |
| 393 | 403 | if ( check_ajax_referer( 'wpsc_confirm_open_ticket_auth', '_ajax_nonce', false ) != 1 ) { |
| 394 | - wp_send_json_error( 'Unauthorised request!', 401 ); | |
| 404 | + wp_send_json_error( 'Unauthorized request!', 401 ); | |
| 395 | 405 | } |
| 396 | 406 | |
| 397 | 407 | $page_settings = get_option( 'wpsc-gs-page-settings' ); |
| 398 | 408 | |
| @@ -411,11 +421,30 @@ | ||
| 411 | 421 | wp_send_json_error( 'Bad request', 400 ); |
| 412 | 422 | } |
| 413 | 423 | |
| 414 | 424 | if ( ! $otp->is_valid( $verification_otp ) ) { |
| 425 | + | |
| 426 | + // Increment attempt counter. | |
| 427 | + ++$attempts; | |
| 428 | + set_transient( $attempt_key, $attempts, 300 ); // 5 minute lockout. | |
| 429 | + | |
| 430 | + // Add per-OTP attempt tracking. | |
| 431 | + $otp_attempt_key = 'wpsc_otp_' . $id . '_attempts'; | |
| 432 | + $otp_attempts = get_transient( $otp_attempt_key ); | |
| 433 | + $otp_attempts = $otp_attempts ? $otp_attempts + 1 : 1; | |
| 434 | + set_transient( $otp_attempt_key, $otp_attempts, 600 ); | |
| 435 | + | |
| 436 | + if ( $otp_attempts >= 3 ) { | |
| 437 | + WPSC_Email_OTP::destroy( $otp ); | |
| 438 | + wp_send_json_error( 'OTP has been invalidated due to too many failed attempts', 403 ); | |
| 439 | + } | |
| 440 | + | |
| 415 | 441 | echo wp_json_encode( array( 'isSuccess' => 0 ) ); |
| 416 | 442 | wp_die(); |
| 417 | 443 | } |
| 444 | + | |
| 445 | + // Clear rate limiting on success. | |
| 446 | + delete_transient( $attempt_key ); | |
| 418 | 447 | |
| 419 | 448 | $data = json_decode( $otp->data, true ); |
| 420 | 449 | $data['auth_token'] = WPSC_Functions::get_random_string( 100 ); |
| 421 | 450 | $data['auth_type'] = 'open-ticket'; |