加固-06(P0-05): 密码全部以 bcrypt 哈希入库,旧格式(明文/MD5)兼容并惰性迁移
背景:sq_user/sq_agent 密码明文入库、sq_admin 存 MD5;订单表 sq_trade.pass 明文在途;
找回密码 API 会把明文密码直接发邮件;日志记录明文密码。数据库泄露 = 全部账号密码泄露。
1. function/function_core.php 新增 3 个 helper(updateset 之后):
- wq_is_hashed_password($v):判断存储值是否为新 bcrypt 哈希(60 长度 $2y$ 前缀)
- wq_hash_password($plain):password_hash(PASSWORD_BCRYPT)
- wq_verify_password($plain,$stored,$legacy):兼容 新 bcrypt / 旧明文(sq_user/sq_agent) / 旧 md5(sq_admin),
哈希比对一律 hash_equals,恒定时间比较防时序侧信道
2. 所有落库点改为 bcrypt:
- 用户开户漏斗 function/function_auth.php auth_add 入口幂等补哈希(上游已哈希则原样保留)
- 代理开通漏斗 function/trade.inc.php type=4 代理落库前幂等补哈希
- 下单点订单密码哈希入库:ajax.php(320/388/781)、agent/ajax.php(439) —— 订单表不再有明文在途密码
- admin/ajax.php:addagent、addadmin、changeagent(mod=password)、changepassword 新密码
- agent/ajax.php:开下级代理、changeuser(mod=password)、changeagent(mod=password)、changepassword 新密码
- api.php:ChangePassword 写库
- api/Communication.php 与 function/communication.php:用户登录校验成功后,旧明文惰性升级为 bcrypt
3. 所有校验点改为取行 + PHP 校验(不再 WHERE password=明文):
- admin 登录(旧 MD5 兼容,成功即写回 bcrypt,session 与 DB 存同一新哈希保住 re-check 机制)
- agent 密码登录(旧明文兼容,惰性迁移);agent token 登录 session 改存 DB 存储值
- 根 ajax.php 续费 kt 密码校验(379/773)、卡密找回密码(1002)
- api.php GetUserInfo 内联校验、getuserinfo() 校验(均带惰性迁移)
4. 找回密码改造(api.php RetrievePassword):
密码已不可逆,原"邮件下发原密码"改为"重置为一次性随机新密码(bin2hex(random_bytes(6)))+ 哈希落库 + 仅邮件投递一次"
5. 日志/回传去明文:
- admin 登录失败日志不再记录明文密码(改记账号)
- admin getagentinfo 不再回传密码值(掩码 ********,经确认 UI 无回写风险)
6. 表结构:sq_admin.password varchar(33) -> varchar(255)(bcrypt 60 字符装不下)
- wenquan_auth.sql 同步更新(新装库直接可用)
- 已有库需执行:ALTER TABLE sq_admin MODIFY password varchar(255) NOT NULL;(本次本地库已执行)
回归(40 项全 PASS,8082 内置服务器 + 8081 nginx 双通道冒烟):
- API(c=api) 旧明文用户登录成功即升级 bcrypt;再登录幂等不错哈希;错密码 -205 被拒
- admin 旧 MD5 登录即迁移 bcrypt;再登录/错密码/日志无明文/checklogin 会话重校验均通过
- admin 新增管理员、改密(旧密码失效/新密码生效/恢复基线)
- admin 新增代理直接 bcrypt;getagentinfo 掩码;changeagent 改密哈希入库
- 旧明文代理登录即迁移 bcrypt;checklogin 通过;代理改密旧失效新生效
- admin 手工开通(funnel)明文 POST 直接存 bcrypt;API 登录新开通用户成功
- 注:测试应用 free 必须为 0(free=1 时 Login 入口直接放行,属产品设计,不走密码校验)
已知行为变化(记录):用户/代理列表按"密码"关键词搜索不再命中(存储已哈希,搜索本身即泄露向量,收紧);
列表中密码列显示 60 位哈希串而非明文;改密会使该账号现有会话失效需重新登录(与旧行为一致)。
api.php 为 2024 树中的旧入口(已被 api/Communication.php 取代),仍按同口径同步修了 4 处密码点防复活踩坑。
This commit is contained in:
+22
-10
@@ -34,17 +34,20 @@ if ($_GET['mod'] === 'login'){
|
||||
if ($verification !== true) {
|
||||
die(json_encode(array('code' => '-88', 'msg' => '请先进行人机验证')));
|
||||
}
|
||||
if (!$result = $db->select_first_row('sq_admin','*',array('username'=>$_POST['username'],'password'=>md5($_POST['password'])),'AND')){
|
||||
//$db->insert_back_id('sq_log_system',array('time'=>time(),'ip'=>get_real_ip(),'msg'=>'管理员 '.$_POST['username'].'使用密码'.$_POST['password'].'登陆失败:密码错误或账号不存在','type'=>'danger'));
|
||||
$db->posterror('管理员登录尝试失败,账号['.$_POST['username'].']密码['.$_POST['password'].']','success');
|
||||
// P0-05: 按用户名取出后用 PHP 校验(兼容新 bcrypt / 旧 md5),成功即写回 bcrypt(惰性迁移);
|
||||
// 失败日志不再记录明文密码(原 posterror 会把密码写入 sq_log_system)
|
||||
$result = $db->select_first_row('sq_admin','*',array('username'=>$_POST['username']),'AND');
|
||||
if (!$result || !wq_verify_password($_POST['password'], $result['password'], 'md5')){
|
||||
$db->posterror('管理员登录尝试失败,账号['.$_POST['username'].']','danger');
|
||||
die(json_encode(array('code' => -3, 'msg' => '输入的账号密码错误!'.$db->geterror())));
|
||||
}else{
|
||||
$_SESSION['admin_username'] = $_POST['username'];
|
||||
$_SESSION['admin_id'] = $result['ID'];
|
||||
$_SESSION['admin_qq'] = $result['qq'];
|
||||
$_SESSION['admin_password'] = md5($_POST['password']);
|
||||
$newhash = wq_hash_password($_POST['password']);
|
||||
$_SESSION['admin_password'] = $newhash;
|
||||
$_SESSION['admin_HTTP_USER_AGENT'] = $_SERVER['HTTP_USER_AGENT'];
|
||||
$db->update('sq_admin',array('username'=>$_SESSION['admin_username']),'AND',array('loginip'=>get_real_ip(),'logintime'=>time()));
|
||||
$db->update('sq_admin',array('ID'=>$result['ID']),'AND',array('password'=>$newhash,'loginip'=>get_real_ip(),'logintime'=>time()));
|
||||
// $db->insert_back_id('sq_log_system',array('time'=>time(),'ip'=>get_real_ip(),'msg'=>'['.get_real_ip().']管理员 '.$_POST['username'].'使用密码登陆成功!','type'=>'success'));
|
||||
$db->posterror('管理员 '.$_POST['username'].'使用密码登录后台成功','success');
|
||||
die(json_encode(array('code' => '1', 'msg' => '登陆成功,正在跳转!')));
|
||||
@@ -491,6 +494,11 @@ switch ($_GET['mod']){
|
||||
|
||||
}
|
||||
|
||||
// P0-05: 用户密码改密以 bcrypt 哈希入库
|
||||
if ($_POST['mod'] == 'password') {
|
||||
$_POST['value'] = wq_hash_password($_POST['value']);
|
||||
}
|
||||
|
||||
if (!$db->update('sq_user',array('ID'=>$_POST['userid']),'AND',array($_POST['mod']=>$_POST['value']))){
|
||||
die('修改失败'.$db->geterror());
|
||||
}else{
|
||||
@@ -624,7 +632,7 @@ switch ($_GET['mod']){
|
||||
if ($db->select_first_row('sq_agent','*',array('username'=>$_POST['agent_name']),'AND') != false){
|
||||
die('该代理账号已经存在,请尝试其他用户名');
|
||||
}
|
||||
if(!$db->insert_back_id('sq_agent',array('username'=>$_POST['agent_name'],'password'=>$_POST['agent_pass'],'money'=>$_POST['agent_money'],'levelid'=>$_POST['agent_leval'],'begintime'=>time(),'status'=>'1','loginip'=>'-','qq'=>$_POST['agent_qq']))){
|
||||
if(!$db->insert_back_id('sq_agent',array('username'=>$_POST['agent_name'],'password'=>wq_hash_password($_POST['agent_pass']),'money'=>$_POST['agent_money'],'levelid'=>$_POST['agent_leval'],'begintime'=>time(),'status'=>'1','loginip'=>'-','qq'=>$_POST['agent_qq']))){
|
||||
die('添加失败:'.$db->geterror());
|
||||
}else{
|
||||
die('代理添加成功!');
|
||||
@@ -674,11 +682,12 @@ switch ($_GET['mod']){
|
||||
break;
|
||||
case 'getagentinfo':
|
||||
$result = $db->select_first_row('sq_agent','*',array('ID'=>$_POST['id']),'AND');
|
||||
// P0-05: 不再把密码(哈希或明文)回传前端;密码修改走 changeagent 单独入口
|
||||
die(json_encode(array(
|
||||
'agent_leval'=>$result['levelid'],
|
||||
'agent_money'=>$result['money'],
|
||||
'agent_name'=>$result['username'],
|
||||
'agent_pass'=>$result['password']
|
||||
'agent_pass'=>'********'
|
||||
|
||||
)));
|
||||
break;
|
||||
@@ -695,6 +704,8 @@ switch ($_GET['mod']){
|
||||
die('代理ID不能为空');
|
||||
}
|
||||
if ($_POST['mod'] == 'status') $_POST['value'] = textbooltonum($_POST['value']);
|
||||
// P0-05: 代理密码统一以 bcrypt 哈希入库
|
||||
if ($_POST['mod'] == 'password') $_POST['value'] = wq_hash_password($_POST['value']);
|
||||
if (!$db->update('sq_agent',array('ID'=>$_POST['aid']),'AND',array($_POST['mod']=>$_POST['value']))){
|
||||
die('修改失败'.$db->geterror());
|
||||
}else{
|
||||
@@ -762,7 +773,7 @@ switch ($_GET['mod']){
|
||||
if (empty($_POST['addusername']) || empty($_POST['adduserpass']) || empty($_POST['adduserqq'])){
|
||||
die('数据不能为空,请检查');
|
||||
}
|
||||
$db->insert_back_id('sq_admin',array('username'=>$_POST['addusername'],'password'=>md5($_POST['adduserpass']),'qq'=>$_POST['adduserqq']));
|
||||
$db->insert_back_id('sq_admin',array('username'=>$_POST['addusername'],'password'=>wq_hash_password($_POST['adduserpass']),'qq'=>$_POST['adduserqq']));
|
||||
die('管理员新增成功,如果需要删除请进数据库中的表sq_admin进行删除');
|
||||
break;
|
||||
case 'sendtestmail':
|
||||
@@ -1050,11 +1061,12 @@ switch ($_GET['mod']){
|
||||
if (!$agentinfo = $db->select_first_row('sq_admin','*',array('username'=>$_SESSION['admin_username']),'AND')){
|
||||
die('管理员信息获取失败');
|
||||
}
|
||||
if (md5($_POST['oldpass']) !== $agentinfo['password']){
|
||||
// P0-05: 兼容校验旧 md5 / 新 bcrypt;新密码以 bcrypt 入库
|
||||
if (!wq_verify_password($_POST['oldpass'], $agentinfo['password'], 'md5')){
|
||||
die('输入的原密码错误,无法修改!');
|
||||
}
|
||||
|
||||
if(!$db->update('sq_admin',array('username'=>$_SESSION['admin_username']),'AND',array('password'=>md5($_POST['newpass'])))){
|
||||
if(!$db->update('sq_admin',array('username'=>$_SESSION['admin_username']),'AND',array('password'=>wq_hash_password($_POST['newpass'])))){
|
||||
die('密码修改失败,服务器内部发生错误');
|
||||
}else{
|
||||
die('管理密码修改成功,请使用新密码进行登录!');
|
||||
|
||||
Reference in New Issue
Block a user