PHP注册表单手机号/邮箱分支密码校验失效问题排查
PHP注册功能邮箱分支密码校验失效问题
问题背景
- 基于PHP开发支持手机号、邮箱双模式的注册表单,核心校验逻辑通过IF/ELSE分支实现,两个分支设计上使用完全一致的密码长度校验规则(要求密码长度5~59位)
- 异常表现:手机号注册分支的密码校验可正常触发,不符合长度要求的密码会被拦截;但邮箱注册分支下同规则的密码校验完全失效,仅输入1位字符的密码也可顺利完成注册
- 已知正常逻辑:邮箱分支下的姓名长度拦截、邮箱已注册查重功能均可正常运行,问题为教程示例代码的原生缺陷,非二次开发引入的错误
问题相关原代码
require 'connect/DB.php'; require 'core/load.php'; if( isset($_POST['first-name']) && !empty($_POST['first-name'])) { $upFirst = $_POST['first-name']; $upLast = $_POST['last-name']; $upEmailMobile = $_POST['email-mobile']; $upPassword = $_POST['up-password']; $birthDay = $_POST['birth-day']; $birthMonth = $_POST['birth-month']; $birthYear = $_POST['birth-year']; if(!empty($_POST['gen'])) { $upgen = $_POST['gen']; } $birth = ''.$birthYear.'-'.$birthMonth.'-'.$birthDay.''; if(empty($upFirst) or empty($upLast) or empty($upEmailMobile) or empty($upgen)) { $error = 'All fields are required'; } else { $first_name = $loadFromUser->checkInput($upFirst); $last_name = $loadFromUser->checkInput($upLast); $email_mobile = $loadFromUser->checkInput($upEmailMobile); $password = $loadFromUser->checkInput($upPassword); $screenName = ''.$first_name.'_'.$last_name.''; if(DB::query('SELECT screenName FROM users WHERE screenName = :screenName', array(':screenName' => $screenName ))) { $screenRand = rand(); $userLink = ''.$screenName.''.$screenRand.''; } else { $userLink = $screenName; } if(!preg_match("/^[_a-z0-9-]+(\.[_a-z0-9]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,3})$/",$email_mobile)) { if(!preg_match("/^[0-9]{11}$/", $email_mobile)) { $error = 'Email id or Mobile number is not correct. Please try again.'; } else { $mob = strlen((string)$email_mobile); if($mob > 11 || $mob < 11) { $error = 'Mobile number is not valid'; } else if(strlen($password) <5 || strlen($password) >= 60) { $error = 'Password is not correct'; } else { if(DB::query('SELECT mobile FROM users WHERE mobile=:mobile', array(':mobile'=>$email_mobile))) { $error = 'Mobile number is already in use.'; } else { $user_id=$loadFromUser->create('users', array('first_name'=>$first_name,'last_name'=>$last_name, 'mobile' => $email_mobile, 'password'=>password_hash($password, PASSWORD_BCRYPT),'screenName'=>$screenName,'userLink'=>$userLink, 'birthday'=>$birth, 'gender'=>$upgen)); $loadFromUser->create('profile', array('userId'=>$user_id, 'birthday'=> $birth, 'firstName' => $first_name, 'lastName'=>$last_name, 'profilePic'=>'assets/image/defaultProfile.png','coverPic'=>'assets/image/defaultCover.png', 'gender'=>$upgen)); $tstrong = true; $token = bin2hex(openssl_random_pseudo_bytes(64, $tstrong)); $loadFromUser->create('token', array('token'=>sha1($token), 'user_id'=>$user_id)); setcookie('FBID', $token, time()+60*60*24*7, '/', NULL, NULL, true); header('Location: index.php'); } } } } else { // 存在缺陷的邮箱分支校验逻辑 if(!filter_var($email_mobile)) { $error = "Invalid Email Format"; } else if(strlen($first_name) > 20) { $error = "Name must be between 2-20 character"; } else if(strlen($password) <5 || strlen($password) >= 60) { $error = "The password is either too short or too long"; } else { if((filter_var($email_mobile,FILTER_VALIDATE_EMAIL)) && $loadFromUser->checkEmail($email_mobile) === true) { $error = "Email is already in use"; } else { $user_id = $loadFromUser->create('users', array('first_name'=>$first_name,'last_name'=>$last_name, 'email' => $email_mobile, 'password'=>password_hash($password, PASSWORD_BCRYPT),'screenName'=>$screenName,'userLink'=>$userLink, 'birthday'=>$birth, 'gender'=>$upgen)); $loadFromUser->create('profile', array('userId'=>$user_id, 'birthday'=>$birth, 'firstName' => $first_name, 'lastName'=>$last_name, 'profilePic'=>'assets/image/defaultProfile.png','coverPic'=>'assets/image/defaultCover.png', 'gender'=>$upgen)); $tstrong = true; $token = bin2hex(openssl_random_pseudo_bytes(64, $tstrong)); $loadFromUser->create('token', array('token'=>sha1($token), 'user_id'=>$user_id)); setcookie('FBID', $token, time()+60*60*24*7, '/', NULL, NULL, true); header('Location: index.php'); } } } } } ?>
根因说明
- 核心问题:邮箱分支内第一个格式校验
if(!filter_var($email_mobile))存在用法错误。filter_var()做邮箱格式校验时必须传入第二个参数FILTER_VALIDATE_EMAIL指定校验规则,漏传参数时函数会默认使用「原样返回输入值」的无过滤规则,导致该判断的返回值完全不符合预期,破坏了if-elseif的链式判断逻辑,后续的密码长度校验分支被直接跳过。 - 附加问题:姓名长度校验仅判断了长度大于20的非法场景,没有覆盖长度小于2的情况,和提示文案要求的2-20位长度规则不符。
- 正则不规范:原代码使用^作为正则分隔符、手机号正则缺少结尾锚定符,存在匹配绕过的风险。
修复方法
将邮箱分支的校验逻辑替换为如下代码即可,其余逻辑保持不变:
else { // 补全filter_var的校验规则参数 if(!filter_var($email_mobile, FILTER_VALIDATE_EMAIL)) { $error = "Invalid Email Format"; } // 补全姓名长度下限校验 else if(strlen($first_name) > 20 || strlen($first_name) < 2) { $error = "Name must be between 2-20 character"; } else if(strlen($password) <5 || strlen($password) >= 60) { $error = "The password is either too short or too long"; } else { // 移除重复的邮箱格式校验,仅保留查重逻辑 if($loadFromUser->checkEmail($email_mobile) === true) { $error = "Email is already in use"; } else { $user_id = $loadFromUser->create('users', array('first_name'=>$first_name,'last_name'=>$last_name, 'email' => $email_mobile, 'password'=>password_hash($password, PASSWORD_BCRYPT),'screenName'=>$screenName,'userLink'=>$userLink, 'birthday'=>$birth, 'gender'=>$upgen)); $loadFromUser->create('profile', array('userId'=>$user_id, 'birthday'=>$birth, 'firstName' => $first_name, 'lastName'=>$last_name, 'profilePic'=>'assets/image/defaultProfile.png','coverPic'=>'assets/image/defaultCover.png', 'gender'=>$upgen)); $tstrong = true; $token = bin2hex(openssl_random_pseudo_bytes(64, $tstrong)); $loadFromUser->create('token', array('token'=>sha1($token), 'user_id'=>$user_id)); setcookie('FBID', $token, time()+60*60*24*7, '/', NULL, NULL, true); header('Location: index.php'); } } }
内容的提问来源于stack exchange,提问作者Josh
相关产品推荐
相关产品推荐

