| @@ -23,10 +23,11 @@ | ||
| 23 | 23 | * |
| 24 | 24 | * Handles the donation form process. |
| 25 | 25 | * |
| 26 | 26 | * @access private |
| 27 | - * @since 4.16.6 Bail early when the form ID is not a give_forms post or is a Visual Form Builder (v3) form. | |
| 28 | - * @since 3.16.1 Use give_maybe_safe_unserialize() on $user_info data | |
| 27 | + * @since 4.16.7.2 Reject serialized data in name fields before storing donation data. | |
| 28 | + * @since 4.16.6 Bail early when the form ID is not a give_forms post or is a Visual Form Builder (v3) form. | |
| 29 | + * @since 3.16.1 Use give_maybe_safe_unserialize() on $user_info data | |
| 29 | 30 | * @since 1.0 |
| 30 | 31 | * |
| 31 | 32 | * @throws ReflectionException Exception Handling. |
| 32 | 33 | * |
| @@ -162,8 +163,21 @@ | ||
| 162 | 163 | 'last_name' => $user['user_last'], |
| 163 | 164 | 'address' => $user['address'], |
| 164 | 165 | ]; |
| 165 | 166 | |
| 167 | + // Reject serialized data in name fields. | |
| 168 | + $serialized_keys = array_filter( | |
| 169 | + $user_info, | |
| 170 | + static function ( $value ) { | |
| 171 | + return is_string( $value ) && \Give\Helpers\Utils::isSerialized( $value ); | |
| 172 | + } | |
| 173 | + ); | |
| 174 | + | |
| 175 | + if ( ! empty( $serialized_keys ) ) { | |
| 176 | + give_set_error( 'give_serialized_user_info', esc_html__( 'Name fields cannot contain serialized data.', 'give' ) ); | |
| 177 | + return; | |
| 178 | + } | |
| 179 | + | |
| 166 | 180 | $auth_key = defined( 'AUTH_KEY' ) ? AUTH_KEY : ''; |
| 167 | 181 | |
| 168 | 182 | // Donation form ID. |
| 169 | 183 | $form_id = isset( $post_data['give-form-id'] ) ? absint( $post_data['give-form-id'] ) : 0; |
| @@ -196,9 +210,9 @@ | ||
| 196 | 210 | $purchase_key |
| 197 | 211 | ); |
| 198 | 212 | |
| 199 | 213 | // Setup donation information. |
| 200 | - $user_info = array_map('\Give\Helpers\Utils::maybeSafeUnserialize', stripslashes_deep( $user_info )); | |
| 214 | + $user_info = stripslashes_deep( $user_info ); | |
| 201 | 215 | $donation_data = [ |
| 202 | 216 | 'price' => $price, |
| 203 | 217 | 'purchase_key' => $purchase_key, |
| 204 | 218 | 'user_email' => $user['user_email'], |
| @@ -323,8 +337,9 @@ | ||
| 323 | 337 | /** |
| 324 | 338 | * Process the checkout login form |
| 325 | 339 | * |
| 326 | 340 | * @access private |
| 341 | + * @since 4.16.7 Require a valid nonce before processing the login form. | |
| 327 | 342 | * @since 1.0 |
| 328 | 343 | * |
| 329 | 344 | * @return void |
| 330 | 345 | */ |
| @@ -329,12 +344,23 @@ | ||
| 329 | 344 | * @return void |
| 330 | 345 | */ |
| 331 | 346 | function give_process_form_login() { |
| 332 | 347 | |
| 333 | - $is_ajax = ! empty( $_POST['give_ajax'] ) ? give_clean( $_POST['give_ajax'] ) : 0; // WPCS: input var ok, sanitization ok, CSRF ok. | |
| 334 | - $referrer = wp_get_referer(); | |
| 335 | - $user_data = give_donation_form_validate_user_login(); | |
| 348 | + $is_ajax = ! empty( $_POST['give_ajax'] ) ? give_clean( $_POST['give_ajax'] ) : 0; // WPCS: input var ok, sanitization ok, CSRF ok. | |
| 349 | + $referrer = wp_get_referer(); | |
| 336 | 350 | |
| 351 | + // Default to no user until the login form is validated. | |
| 352 | + $user_data = [ | |
| 353 | + 'user_id' => - 1, | |
| 354 | + ]; | |
| 355 | + | |
| 356 | + // Require a valid nonce before processing the login form. | |
| 357 | + if ( empty( $_POST['give_login_nonce'] ) || ! wp_verify_nonce( $_POST['give_login_nonce'], 'give-login-nonce' ) ) { | |
| 358 | + give_set_error( 'invalid_nonce', __( 'Your session has expired. Please reload the page and try again.', 'give' ) ); | |
| 359 | + } else { | |
| 360 | + $user_data = give_donation_form_validate_user_login(); | |
| 361 | + } | |
| 362 | + | |
| 337 | 363 | if ( give_get_errors() || $user_data['user_id'] < 1 ) { |
| 338 | 364 | if ( $is_ajax ) { |
| 339 | 365 | /** |
| 340 | 366 | * Fires when AJAX sends back errors from the donation form. |
| @@ -345,8 +371,9 @@ | ||
| 345 | 371 | do_action( 'give_ajax_donation_errors' ); |
| 346 | 372 | $message = ob_get_contents(); |
| 347 | 373 | ob_end_clean(); |
| 348 | 374 | wp_send_json_error( $message ); |
| 375 | + return; | |
| 349 | 376 | } else { |
| 350 | 377 | wp_safe_redirect( $referrer ); |
| 351 | 378 | exit; |
| 352 | 379 | } |
| @@ -860,8 +887,9 @@ | ||
| 860 | 887 | /** |
| 861 | 888 | * Donation Form Validate Logged In User. |
| 862 | 889 | * |
| 863 | 890 | * @access private |
| 891 | + * @since 4.16.7.2 Sanitize first and last name values when falling back to stored user data. | |
| 864 | 892 | * @since 1.0 |
| 865 | 893 | * |
| 866 | 894 | * @return array |
| 867 | 895 | */ |
| @@ -895,13 +923,13 @@ | ||
| 895 | 923 | 'user_email' => ! empty( $post_data['give_email'] ) |
| 896 | 924 | ? sanitize_email( $post_data['give_email'] ) |
| 897 | 925 | : $user_data->user_email, |
| 898 | 926 | 'user_first' => ! empty( $post_data['give_first'] ) |
| 899 | - ? $post_data['give_first'] | |
| 900 | - : $user_data->first_name, | |
| 927 | + ? give_clean( $post_data['give_first'] ) | |
| 928 | + : give_clean( $user_data->first_name ), | |
| 901 | 929 | 'user_last' => ! empty( $post_data['give_last'] ) |
| 902 | - ? $post_data['give_last'] | |
| 903 | - : $user_data->last_name, | |
| 930 | + ? give_clean( $post_data['give_last'] ) | |
| 931 | + : give_clean( $user_data->last_name ), | |
| 904 | 932 | ]; |
| 905 | 933 | |
| 906 | 934 | // Validate essential form fields. |
| 907 | 935 | give_donation_form_validate_name_fields( $post_data ); |
| @@ -997,8 +1025,9 @@ | ||
| 997 | 1025 | /** |
| 998 | 1026 | * Donation Form Validate User Login |
| 999 | 1027 | * |
| 1000 | 1028 | * @access private |
| 1029 | + * @since 4.16.7 Authenticate via wp_authenticate() and return a single generic error. | |
| 1001 | 1030 | * @since 1.0 |
| 1002 | 1031 | * |
| 1003 | 1032 | * @return array |
| 1004 | 1033 | */ |
| @@ -1020,61 +1049,55 @@ | ||
| 1020 | 1049 | return $valid_user_data; |
| 1021 | 1050 | } |
| 1022 | 1051 | |
| 1023 | 1052 | $give_user_login = strip_tags( $post_data['give_user_login'] ); |
| 1024 | - if ( is_email( $give_user_login ) ) { | |
| 1025 | - // Get the user data by email. | |
| 1026 | - $user_data = get_user_by( 'email', $give_user_login ); | |
| 1027 | - } else { | |
| 1028 | - // Get the user data by login. | |
| 1029 | - $user_data = get_user_by( 'login', $give_user_login ); | |
| 1053 | + | |
| 1054 | + // Bailout, if Password is empty. | |
| 1055 | + if ( empty( $post_data['give_user_pass'] ) ) { | |
| 1056 | + give_set_error( 'password_empty', __( 'Enter a password.', 'give' ) ); | |
| 1057 | + return $valid_user_data; | |
| 1030 | 1058 | } |
| 1031 | 1059 | |
| 1032 | - // Check if user exists. | |
| 1033 | - if ( $user_data ) { | |
| 1060 | + // Authenticate through WordPress's login machinery so its authentication | |
| 1061 | + // hooks, password checks, and failed-login actions all apply. | |
| 1062 | + $user_data = wp_authenticate( $give_user_login, $post_data['give_user_pass'] ); | |
| 1034 | 1063 | |
| 1035 | - // Get password. | |
| 1036 | - $user_pass = ! empty( $post_data['give_user_pass'] ) ? $post_data['give_user_pass'] : false; | |
| 1064 | + if ( is_wp_error( $user_data ) ) { | |
| 1037 | 1065 | |
| 1038 | - // Check user_pass. | |
| 1039 | - if ( $user_pass ) { | |
| 1066 | + $core_auth_error_codes = [ | |
| 1067 | + 'incorrect_password', | |
| 1068 | + 'invalid_username', | |
| 1069 | + 'invalid_email', | |
| 1070 | + 'empty_username', | |
| 1071 | + 'empty_password', | |
| 1072 | + ]; | |
| 1040 | 1073 | |
| 1041 | - // Check if password is valid. | |
| 1042 | - if ( ! wp_check_password( $user_pass, $user_data->user_pass, $user_data->ID ) ) { | |
| 1074 | + if ( in_array( $user_data->get_error_code(), $core_auth_error_codes, true ) ) { | |
| 1075 | + // A single generic message for an unknown login and a wrong password. | |
| 1076 | + $error_message = __( 'The login/password does not match or is incorrect.', 'give' ); | |
| 1077 | + } else { | |
| 1078 | + // Any other error comes from an authentication hook; surface its message. | |
| 1079 | + $error_message = wp_strip_all_tags( $user_data->get_error_message() ); | |
| 1043 | 1080 | |
| 1044 | - $current_page_url = site_url() . '/' . get_page_uri(); | |
| 1081 | + if ( '' === $error_message ) { | |
| 1082 | + $error_message = __( 'The login/password does not match or is incorrect.', 'give' ); | |
| 1083 | + } | |
| 1084 | + } | |
| 1045 | 1085 | |
| 1046 | - // Incorrect password. | |
| 1047 | - give_set_error( | |
| 1048 | - 'password_incorrect', | |
| 1049 | - sprintf( | |
| 1050 | - '%1$s <a href="%2$s">%3$s</a>', | |
| 1051 | - __( 'The password you entered is incorrect.', 'give' ), | |
| 1052 | - wp_lostpassword_url( $current_page_url ), | |
| 1053 | - __( 'Reset Password', 'give' ) | |
| 1054 | - ) | |
| 1055 | - ); | |
| 1086 | + give_set_error( 'invalid_credentials', $error_message ); | |
| 1056 | 1087 | |
| 1057 | - } else { | |
| 1088 | + return $valid_user_data; | |
| 1089 | + } | |
| 1058 | 1090 | |
| 1059 | - // Repopulate the valid user data array. | |
| 1060 | - $valid_user_data = [ | |
| 1061 | - 'user_id' => $user_data->ID, | |
| 1062 | - 'user_login' => $user_data->user_login, | |
| 1063 | - 'user_email' => $user_data->user_email, | |
| 1064 | - 'user_first' => $user_data->first_name, | |
| 1065 | - 'user_last' => $user_data->last_name, | |
| 1066 | - 'user_pass' => $user_pass, | |
| 1067 | - ]; | |
| 1068 | - } | |
| 1069 | - } else { | |
| 1070 | - // Empty password. | |
| 1071 | - give_set_error( 'password_empty', __( 'Enter a password.', 'give' ) ); | |
| 1072 | - } | |
| 1073 | - } else { | |
| 1074 | - // No username. | |
| 1075 | - give_set_error( 'username_incorrect', __( 'The username you entered does not exist.', 'give' ) ); | |
| 1076 | - } // End if(). | |
| 1091 | + // Repopulate the valid user data array. | |
| 1092 | + $valid_user_data = [ | |
| 1093 | + 'user_id' => $user_data->ID, | |
| 1094 | + 'user_login' => $user_data->user_login, | |
| 1095 | + 'user_email' => $user_data->user_email, | |
| 1096 | + 'user_first' => $user_data->first_name, | |
| 1097 | + 'user_last' => $user_data->last_name, | |
| 1098 | + 'user_pass' => $post_data['give_user_pass'], | |
| 1099 | + ]; | |
| 1077 | 1100 | |
| 1078 | 1101 | return $valid_user_data; |
| 1079 | 1102 | } |
| 1080 | 1103 | |
| @@ -1665,11 +1688,12 @@ | ||
| 1665 | 1688 | * Validates and checks if name fields are valid or not. |
| 1666 | 1689 | * |
| 1667 | 1690 | * @param array $post_data List of post data. |
| 1668 | 1691 | * |
| 1669 | - * @since 3.16.5 Check if "give_title" is set to prevent PHP warnings | |
| 1670 | - * @since 3.16.4 Add additional validation for company name field | |
| 1671 | - * @since 3.16.3 Add additional validations for name title prefix field | |
| 1692 | + * @since 4.16.7.2 Validate last name field even when omitted. | |
| 1693 | + * @since 3.16.5 Check if "give_title" is set to prevent PHP warnings | |
| 1694 | + * @since 3.16.4 Add additional validation for company name field | |
| 1695 | + * @since 3.16.3 Add additional validations for name title prefix field | |
| 1672 | 1696 | * @since 2.1 |
| 1673 | 1697 | * |
| 1674 | 1698 | * @return void |
| 1675 | 1699 | */ |
| @@ -1689,11 +1713,18 @@ | ||
| 1689 | 1713 | give_set_error( 'invalid_name_title', esc_html__( 'The name title prefix field is not valid.', 'give' ) ); |
| 1690 | 1714 | } |
| 1691 | 1715 | |
| 1692 | 1716 | $is_alpha_first_name = ( ! is_email( $post_data['give_first'] ) && ! preg_match( '~[0-9]~', $post_data['give_first'] ) ); |
| 1693 | - $is_alpha_last_name = ( ! is_email( $post_data['give_last'] ) && ! preg_match( '~[0-9]~', $post_data['give_last'] ) ); | |
| 1717 | + | |
| 1718 | + $lastName = isset( $post_data['give_last'] ) ? $post_data['give_last'] : ''; | |
| 1719 | + $is_alpha_last_name = ( ! is_email( $lastName ) && ! preg_match( '~[0-9]~', $lastName ) ); | |
| 1720 | + | |
| 1694 | 1721 | $is_alpha_title = ( isset($post_data['give_title']) && ! is_email( $post_data['give_title'] ) && ! preg_match( '~[0-9]~', $post_data['give_title'] ) ); |
| 1695 | 1722 | |
| 1696 | - if (!$is_alpha_first_name || ( ! empty( $post_data['give_last'] ) && ! $is_alpha_last_name) || ( ! empty( $post_data['give_title'] ) && ! $is_alpha_title) ) { | |
| 1723 | + if ( ! $is_alpha_first_name || ( ! empty( $lastName ) && ! $is_alpha_last_name ) || ( ! empty( $post_data['give_title'] ) && ! $is_alpha_title ) ) { | |
| 1697 | 1724 | give_set_error( 'invalid_name', esc_html__( 'The First Name and Last Name fields cannot contain an email address or numbers.', 'give' ) ); |
| 1725 | + } | |
| 1726 | + | |
| 1727 | + if ( give_is_last_name_required( $formId ) && empty( $lastName ) ) { | |
| 1728 | + give_set_error( 'invalid_last_name', esc_html__( 'Please enter your last name.', 'give' ) ); | |
| 1698 | 1729 | } |
| 1699 | 1730 | } |