Files
wenquanauth/function/function_auth.php
T
youpaishidifu 24264cf320 加固-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 处密码点防复活踩坑。
2026-10-10 12:25:52 +08:00

300 lines
12 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* Created by PhpStorm.
* User: 温泉
* Date: 2019/2/9
* Time: 15:50
*/
/**
* 授权操作
* @access public
* @param string $username 用户名、卡密
* @param string $password 密码
* @param string $ip 用户IP、绑定的IP
* @param int $balance 新增授权点数或秒数,-1为永久
* @param string $uqq 用户QQ
* @param string $mail 用户邮箱
* @param int $appid 应用ID
* @param int $agentid 代理ID
* @param string $newkey 返回的卡密
* @param string $tips 提示
* @return int 1为新开成功,2为续费成功,3为卡密新开成功
*/
function auth_add($username,$password,$ip,$balance,$uqq,$mail,$appid,$origin,$oid,$aid,&$newkey,&$tips,$tradeinfo = array()){
include_once __DIR__ . '/function_core.php';
include_once __DIR__ . '/function_app.php';
global $db,$G,$MailTips;
// P0-05: 密码以 bcrypt 哈希入库。幂等保护:若上游(订单创建点)已哈希则原样保留,
// 旧格式明文订单(升级前产生)在此处补哈希
if (!wq_is_hashed_password($password)) {
$password = wq_hash_password($password);
}
$appinfo = app_idgetinfo($appid);
if ($appinfo['usetype']=='dqsj'){
if ($balance!='-1'){
$balance=$balance*60;
}
}
if ($appinfo['logintype'] == 'zhmm') {
//账号密码授权方式
if (!$userinfo = $db->select_first_row('sq_user', '*', array('username' => $username, 'appid' => $appid), 'AND')) {
$T['title'] = '您的授权账号已新开成功!';
$T['content'][] = '应用名称:'.$appinfo['appname'];
$T['content'][] = '注册时间:'.Get_Date();
if ($appinfo['usetype']=='dqsj'){
if ($balance!='-1'){
$balance=time()+$balance;
$T['content'][] = '到期时间:'.Get_Date($balance);
}else{
$T['content'][] = '到期时间:永久授权';
}
}else{
if ($balance == '-1'){
$T['content'][] = '开通余额:无限使用';
}else{
$T['content'][] = '开通余额:'.$balance;
}
}
$db->insert_back_id('sq_user', array(
'username' => $username,
'password' =>$password,
'mail' => $mail,
'uqq' => $uqq,
'rtime' => time(),
'status' => '1',
'appid' => $appid,
'aid' => $aid,
'balance'=>$balance,
'origin' => $origin,
'oid'=>$oid
)
);
$T['content'][] = '授权来源:'.GetOriginText($origin);
$T['content'][] = '联系方式:QQ '.$uqq;
$T['content'][] = '授权账号:'.$username;
$T['content'][] = '用户邮箱:'.$mail;
$MailTips[] = $T;
SendTipsMail($mail,$MailTips,$backinfo);
return 1;
}
$T['title'] = '您的授权账号已续费成功!';
$T['content'][] = '应用名称:'.$appinfo['appname'];
$T['content'][] = '注册时间:'.Get_Date($userinfo['rtime']);
if ($balance != '-1') {
if ($appinfo['usetype'] == 'dqsj') {
if ($userinfo['balance'] < time()) {
$usernewinfo['balance'] = time() + $balance;
} else {
$usernewinfo['balance'] = $userinfo['balance'] + $balance;
}
$T['content'][] = '到期时间:'.Get_Date($usernewinfo['balance']);
} else if ($appinfo['usetype'] == 'kcye') {
$usernewinfo['balance'] = $userinfo['balance'] +$balance;
$T['content'][] = '授权余额:'.Get_Date($usernewinfo['balance']);
} else {
$tips='应用使用方式设置异常';
return -3;
}
} else {
$usernewinfo['balance'] = '-1';
$T['content'][] = $appinfo['usetype']=='dqsj' ? '到期时间:永久授权' : '开通余额:无限使用';
}
$T['content'][] = '授权来源:'.GetOriginText($origin);
$T['content'][] = '联系方式:QQ '.$uqq;
$T['content'][] = '授权账号:'.$userinfo['username'];
$T['content'][] = '用户邮箱:'.$userinfo['mail'];
$MailTips[]= $T;
$db->update('sq_user', array('ID'=>$userinfo['ID']), 'AND', $usernewinfo);
SendTipsMail($userinfo['mail'],$MailTips,$backinfo);
return 2;
} else if ($appinfo['logintype'] == 'kmsq') {
//卡密登录方式
if (empty($username)) {
//没有卡密新开授权
$T['title'] = '您的授权卡密已新开成功!';
$T['content'][] = '应用名称:'.$appinfo['appname'];
$T['content'][] = '注册时间:'.Get_Date();
if ($appinfo['usetype']=='dqsj'){
if ($balance!='-1'){
$balance=time()+$balance;
$T['content'][] = '到期时间:'.Get_Date($balance);
}else{
$T['content'][] = '到期时间:永久使用';
}
}else{
if ($balance == '-1'){
$T['content'][] = '开通余额:无限使用';
}else{
$T['content'][] = '开通余额:'.$balance;
}
}
$newkey = rand_str(32);
while ($info = $db->select_first_row('sq_user', 'ID', array('username' => $newkey, 'appid' => $appinfo['ID']), 'AND')) {
$newkey = rand_str(32);
}
//echo $newkey;
$db->insert_back_id('sq_user', array(
'username' => $newkey,
'mail' => $mail,
'rtime' => time(),
'balance' => $balance,
'status' => 1,
'origin' => $origin,
'oid'=>$oid,
'appid' => $appid,
'aid' => $aid,
));
$T['content'][] = '授权来源:'.GetOriginText($origin);
$T['content'][] = '联系方式:QQ '.$uqq;
$T['content'][] = '授权卡密:'.$newkey;
$T['content'][] = '用户邮箱:'.$mail;
$MailTips[] = $T;
SendTipsMail($mail,$MailTips,$backinfo);
return 3;
}
//续费授权
if (!$userinfo = $db->select_first_row('sq_user', '*', array('username' => $username, 'appid' => $appid), 'AND')) {
$tips = '授权卡密不存在';
return -2;
}
$T['title'] = '您的授权卡密已续费成功!';
$T['content'][] = '应用名称:'.$appinfo['appname'];
$T['content'][] = '注册时间:'.Get_Date($userinfo['rtime']);
if ($balance != '-1') {
if ($appinfo['usetype'] == 'dqsj') {
if ($userinfo['balance'] < time()) {
$usernewinfo['balance'] = time() + $balance;
} else {
$usernewinfo['balance'] = $userinfo['balance'] + $balance;
}
$T['content'][] = '到期时间:'.Get_Date($usernewinfo['balance']);
} else if ($appinfo['usetype'] == 'kcye') {
$usernewinfo['balance'] = $userinfo['balance'] +$balance;
$T['content'][] = '授权余额:'.Get_Date($usernewinfo['balance']);
} else {
$tips='应用使用方式设置异常';
return -3;
}
} else {
$usernewinfo['balance'] = '-1';
$T['content'][] = $appinfo['usetype']=='dqsj' ? '到期时间:永久授权' : '开通余额:无限使用';
}
$T['content'][] = '授权来源:'.GetOriginText($origin);
$T['content'][] = '联系方式:QQ '.$uqq;
$T['content'][] = '授权卡密:'.$userinfo['username'];
$T['content'][] = '用户邮箱:'.$userinfo['mail'];
$MailTips[]= $T;
$db->update('sq_user', array('username' => $username, 'appid' => $appinfo['ID']), 'AND', $usernewinfo);
SendTipsMail($userinfo['mail'],$MailTips,$backinfo);
return 2;
} else if ($appinfo['logintype'] == 'jcbd') {
$chect = json_decode($username,true);
if ($appinfo['bindip'] == '1'){
$search['lip'] = $chect['lip'];
$T['content'][] = '绑定IP:'.$chect['lip'];
}
if ($appinfo['bindqq'] == '1'){
$search['rqq'] = $chect['rqq'];
$T['content'][] = '绑定QQ:'.$chect['rqq'];
}
if ($appinfo['bindmac'] == '1'){
$search['mac'] = $chect['mac'];
$T['content'][] = '绑定设备:'.$chect['mac'];
}
$search['appid'] = $appid;
if (!isset($chect['lip'])){
$chect['lip'] = '';
}
if (!isset($chect['rqq'])){
$chect['rqq'] = '';
}
if (!isset($chect['mac'])){
$chect['mac'] = '';
}
if (!$userinfo = $db->select_first_row('sq_user', '*', $search, 'AND')) {
$T['title'] = '您的授权绑定已新开成功!';
$T['content'][] = '应用名称:'.$appinfo['appname'];
$T['content'][] = '注册时间:'.Get_Date();
if ($appinfo['usetype']=='dqsj'){
if ($balance!='-1'){
$balance=time()+$balance;
$T['content'][] = '到期时间:'.Get_Date($balance);
}else{
$T['content'][] = '到期时间:永久使用';
}
}else{
if ($balance == '-1'){
$T['content'][] = '开通余额:无限使用';
}else{
$T['content'][] = '开通余额:'.$balance;
}
}
$db->insert_back_id('sq_user', array(
'mail' => $mail,
'rtime' => time(),
'balance' => $balance,
'status' => 1,
'appid' => $appid,
'uqq' => $uqq,
'mac' => $chect['mac'],
'rqq' => $chect['rqq'],
'rip' => $ip,
'lip' => $chect['lip'],
'aid' => $aid,
'origin' => $origin,
'oid'=>$oid,
));
$T['content'][] = '授权来源:'.GetOriginText($origin);
$T['content'][] = '联系方式:QQ '.$uqq;
$T['content'][] = '用户邮箱:'.$mail;
$MailTips[] = $T;
SendTipsMail($mail,$MailTips,$backinfo);
return 1;
}
$T['title'] = '您的授权卡密已续费成功!';
$T['content'][] = '应用名称:'.$appinfo['appname'];
$T['content'][] = '注册时间:'.Get_Date($userinfo['rtime']);
if ($balance != '-1') {
if ($appinfo['usetype'] == 'dqsj') {
if ($userinfo['balance'] < time()) {
$usernewinfo['balance'] = time() + $balance;
} else {
$usernewinfo['balance'] = $userinfo['balance'] + $balance;
}
$T['content'][] = '到期时间:'.Get_Date($usernewinfo['balance']);
} else if ($appinfo['usetype'] == 'kcye') {
$usernewinfo['balance'] = $userinfo['balance'] +$balance;
$T['content'][] = '授权余额:'.Get_Date($usernewinfo['balance']);
} else {
$tips='应用使用方式设置异常';
return -3;
}
} else {
$usernewinfo['balance'] = '-1';
$T['content'][] = $appinfo['usetype']=='dqsj' ? '到期时间:永久授权' : '开通余额:无限使用';
}
$T['content'][] = '授权来源:'.GetOriginText($origin);
$T['content'][] = '联系方式:QQ '.$uqq;
$T['content'][] = '用户邮箱:'.$userinfo['mail'];
$MailTips[]= $T;
$db->update('sq_user', $search, 'AND', $usernewinfo);
SendTipsMail($userinfo['mail'],$MailTips,$backinfo);
return 2;
}
$tips = '未知授权请求?';
return -2;
}