wpForo 3.1.1 — first_name and last_name silently discarded on registration
Summary
In wpforo_do_hook_user_register(), the $wpfreg array is intersected with a four-key allowlist (user_login, user_email, user_pass1, user_pass2) before Members::update() is called. This silently strips any other fields posted via wpfreg[] — including first_name, last_name, and user_url, all of which are listed as save targets in Members::update_user_fields() at lines 948–957. The downstream save block then sees those keys as missing and skips the corresponding update_user_meta() calls. The registration completes successfully and the user is created, but with empty first_name and last_name in wp_usermeta.
This appears to be a security hardening (the inline comment mentions blocking attackers from pivoting userid via wpfreg[]) but the allowlist is overly restrictive — the userid is already forced on the next line, so widening the allowlist to include the standard WP user fields wpForo Core itself already saves doesn't reopen any attack surface.
Reproduction
- Install wpForo Core 3.1.1 + wpForo User Custom Fields (any recent version).
- Configure the registration form to include
first_nameandlast_name(default UCF setup does this). - Register a new user via the forum's
/sign-up/page, filling in First Name =FirstTestand Last Name =LastTest. - Check the new user's meta:
wp user meta list <id> | grep -E 'first_name|last_name'.
Expected: first_name = FirstTest, last_name = LastTest.
Actual: both empty.
Root cause
At includes/hooks.php ~line 2210:
$data['wpfreg'] = array_intersect_key( $wpfreg, array_flip( [
'user_login',
'user_email',
'user_pass1',
'user_pass2',
] ) );
The intersection drops first_name and last_name (and user_url) before WPF()->member->update() is called at line 2234, so Members::update_user_fields() never sees them.
Verified fix
Adding the three standard WP user fields back to the allowlist resolves the issue and re-saves them correctly via the existing update_user_meta() calls in update_user_fields():
$data['wpfreg'] = array_intersect_key( $wpfreg, array_flip( [
'user_login',
'user_email',
'user_pass1',
'user_pass2',
'first_name',
'last_name',
'user_url',
] ) );
The $data['wpfreg']['userid'] = (int) $userid; line immediately below this still forces the trusted userid, so the security goal of the original change is preserved — the allowlist just needs to permit the additional standard WP user fields that Members::update_user_fields() already handles.
Logged evidence
Instrumented Members::create() and Members::update_user_fields() with error_log() calls, registered a user, then captured the $_POST, $data at entry to create(), $user_fields after the wpfreg split, and $data at the save gate. The trace shows first_name / last_name present in $_POST['wpfreg'] and in the post-split $user_fields, then absent from $data by the time it reaches the save block — confirming the allowlist intersection as the strip point. Re-running the same test after adding the three keys to the allowlist resulted in both fields landing in wp_usermeta as expected.
Notes
- The strip is silent — no error, notice, or admin alert. The registration appears successful from every visible angle.
- Sites using wpForo User Custom Fields to add other custom fields on the registration form aren't affected by those fields being lost, because UCF custom fields arrive under
$_POST['data']rather than$_POST['wpfreg']and are handled separately. The strip only affects the standard WP user fields that arrive underwpfreg[].