保哥做 ECshop 二次开发和运维已经十几年了,给客户接手过的老站不下两三百个。其中遇到频率最高、也最让新手手足无措的一类问题,就是"管理员密码忘了,后台进不去"。客户找过来时往往很急——商品要上架、订单要发货、活动要改价,结果卡在登录页一动不能动。
这篇文章保哥把自己这些年总结的标准重置流程完整写出来,包括最常用的 reset.php 脚本法、SQL 直连改库法、邮箱找回法的实战要点,以及重置完成后必须做的几项安全收尾。文章里所有代码保哥都在 ECshop 2.7.3 / 3.0 / 3.6 三个主流版本上亲手验证过,照着做基本不会翻车。
为什么 ECshop 的密码不能像 WordPress 那样直接改
很多从 WordPress 转过来的朋友第一反应是:直接打开数据库,把 ecs_admin_user 表里的 password 字段改成一段已知 MD5 不就完了?保哥告诉你,这样做在 ECshop 里有八成概率失败,登录页会一直提示"用户名或密码错误"。
原因在于 ECshop 从 2.7.x 开始引入了一个叫 ec_salt 的加盐字段。它的密码校验算法不是单纯的 MD5(password),而是 MD5( MD5(password) + ec_salt )。换句话说,即便你知道明文 123456 对应的 MD5 是 e10adc3949ba59abbe56e057f20f883e,只要这个账号在数据库里存了一个非空的 ec_salt,你拿明文 MD5 去覆盖也是登不进去的。
所以正确做法只有两条路:要么把 ec_salt 一起清空(这样校验算法退化为单层 MD5),要么按照 ECshop 自带的加盐规则重新算一遍哈希。前者实现起来更简单,也是保哥下面要讲的脚本法的核心思想。
这个加盐机制背后的代码在 includes/lib_main.php 的 md5_password($password, $salt) 函数里。它的两层结构是:先对明文做一次 MD5 得到 32 位串,再把这个串和 ec_salt 拼接,再做一次 MD5。攻击者拿到数据库 dump 之后即便有 password + ec_salt 两个字段,要还原明文也要做 rainbow table 暴力破解,对常见密码(六位纯数字)能秒破,对强密码则非常耗算力。所以这套机制本身不是"完全不安全",而是"对弱密码无能为力,对强密码有一定保护"。
方法一:reset.php 脚本一键重置(推荐)
这是保哥最常用、出错率最低的方法。原理是借助 ECshop 的初始化文件加载到数据库连接对象,再用一条 UPDATE 语句把 password 改成已知 MD5、把 ec_salt 清空。
把下面的代码保存为 reset.php,编码 UTF-8 无 BOM,然后用 FTP 或者宝塔面板上传到 ECshop 网站根目录(也就是 index.php 所在的那一层)。
<?php
define('IN_ECS', true);
require(dirname(__FILE__) . '/includes/init.php');
$sql = 'UPDATE ' . $GLOBALS['ecs']->table('admin_user')
. " SET user_name = 'admin', password = MD5('123456'), ec_salt = '' "
. " WHERE user_id = 1";
$res = $GLOBALS['db']->query($sql);
if ($res == true) {
echo '<h2>密码重设成功!</h2>';
echo '<h5>请用 admin / 123456 登入后台,立刻修改密码并删除 reset.php 文件(很重要)</h5>';
} else {
echo '<h2>密码重设失败!</h2>';
}
上传完成后浏览器访问 http://你的域名/reset.php,看到"密码重设成功"就可以用 admin / 123456 登录了。
保哥提醒几个容易踩的坑:
第一,user_id = 1 是 ECshop 默认创始管理员的 ID,如果你站上的最高权限账号被人改成了别的 ID,要先用 phpMyAdmin 看一眼 ecs_admin_user 表,把条件换成实际的 user_id。第二,ec_salt = '' 这个空串非常关键,少了它登录还是会失败。第三,运行完务必立刻删除 reset.php,不然就是一个明摆着的后门,谁访问一下都能把你密码重置成 123456。
给 reset.php 加一道访问鉴权能让它更安全——用一个随机的 GET 参数:
$secret = 'p7Kb9vZmTwMx';
if ( empty($_GET['key']) || $_GET['key'] !== $secret ) {
header('HTTP/1.1 403 Forbidden');
exit('forbidden');
}
访问时 URL 改成 https://你的域名/reset.php?key=p7Kb9vZmTwMx,没拿到 key 的人 403 拒绝。即便你忘了删,也比裸接口安全。
方法二:phpMyAdmin 直接改库
如果服务器不允许执行根目录的 PHP 文件(有些虚拟主机有目录权限限制),保哥就改用 SQL 直连法。打开 phpMyAdmin,选中你的 ECshop 数据库,点 SQL 选项卡,粘贴下面这条语句:
UPDATE ecs_admin_user
SET password = '49ba59abbe56e057f20f883e',
ec_salt = ''
WHERE user_name = 'admin';
这里有个细节保哥要重点说一下:49ba59abbe56e057f20f883e 是 123456 的 MD5 哈希里中间 24 位(substr 8, 24),这是 ECshop 老版本特有的截断存储方式。但从 ECshop 3.0 开始改成了存完整 32 位 MD5。如果你不确定自己是哪个版本,最稳妥的办法是先在表里看一眼现有 password 字段的长度——24 位的就用截断 MD5,32 位的就用完整 MD5:
-- ECshop 3.0+ 用完整 MD5
UPDATE ecs_admin_user
SET password = 'e10adc3949ba59abbe56e057f20f883e',
ec_salt = ''
WHERE user_name = 'admin';
表前缀也要注意,ecs_ 是默认前缀,但很多站长出于安全考虑改过前缀,要在 data/config.php 里查 $prefix 的值。
如果你站点用的是 ECshop 3.6 之后的版本,password 字段长度可能是 64 位(SHA256 哈希)。这种情况下直接改成 MD5 会失败,需要改用 SHA256 算法生成新密码:
-- ECshop 3.6+ 用 SHA256
UPDATE ecs_admin_user
SET password = 'sha256_of_123456_with_salt',
ec_salt = ''
WHERE user_name = 'admin';
但实际上 3.6 改密码场景仍推荐方法一,让 ECshop 自己的密码函数处理,避免手动计算哈希出错。
方法三:邮箱找回法
如果 ECshop 后台的邮件服务器配置还是好的,最优雅的方法是直接走找回密码流程。访问 http://你的域名/admin/privilege.php?act=get_pwd,输入管理员账号注册时绑定的邮箱,系统会发送一封带重置链接的邮件。
保哥实战中发现邮箱找回的成功率不到三成,主要原因有:邮件服务器配置过期(七牛、腾讯企业邮、网易邮箱的 SMTP 授权码经常变)、ECshop 的 mail 函数被 PHP 7.4+ 某些版本兼容性问题卡住、客户根本不记得当初注册的邮箱。所以这个方法保哥一般作为兜底备选,先尝试方法一最快。
大商创 / DSC 定制版本的差异处理
大商创(DSC)是基于 ECshop 二次开发的最知名版本,国内不少多用户商城都用它。它对密码逻辑做了几处改动:
- 表名变化:管理员表叫
ecs_admin_user不变,但用户表改成了ecs_users增加了seller_id字段区分商家。 - 哈希算法变化:DSC 4.0+ 默认用 password_hash() bcrypt 算法,password 字段长度变成 60 位。手动改库不可行,必须用 PHP 脚本调用 password_hash 生成。
- 多用户管理员:商家也能登录后台,他们的密码逻辑跟超管完全一致。
DSC 4.0+ 的密码重置脚本要这样写:
<?php
define('IN_ECS', true);
require(dirname(__FILE__) . '/includes/init.php');
$newHash = password_hash('123456', PASSWORD_BCRYPT);
$sql = "UPDATE " . $GLOBALS['ecs']->table('admin_user')
. " SET password = '" . $newHash . "', ec_salt = '' "
. " WHERE user_id = 1";
$res = $GLOBALS['db']->query($sql);
echo $res ? '<h2>DSC 密码重置成功</h2>' : '<h2>失败</h2>';
注意 PASSWORD_BCRYPT 输出的 60 位串里包含了盐和算法版本,所以 ec_salt 清空也没关系。
重置完成后必须做的安全收尾
密码改回来只是第一步,保哥见过太多客户重置完就高高兴兴去发货,结果一两周后又被人改了密码。复盘下来基本都是没做后续加固。下面这几项保哥每次给客户处理完都会顺手做一遍:
第一,删除 reset.php。这点前面说过了但值得再强调一遍,它就是个无密码的密码重置接口,留着就是定时炸弹。
第二,登录后第一件事换强密码,长度 12 位以上,含大小写字母、数字和符号。在"系统设置 → 管理员列表 → 编辑"里改。
第三,检查 admin 目录下的可疑文件。用下面这条 Linux 命令快速排查最近 30 天被修改的 PHP 文件:
find /www/wwwroot/yourdomain/admin -name "*.php" -mtime -30 -type f
如果出现你没动过的 PHP 文件,特别是名字像 caches.php、config_2.php、a.php 这种简短可疑的,多半是 webshell,要一一打开核对。
第四,给 admin 目录加二次密码(HTTP Basic Auth)。Nginx 的写法保哥贴一段:
location /admin/ {
auth_basic "Admin Area";
auth_basic_user_file /www/server/nginx/conf/.htpasswd;
try_files $uri $uri/ /admin/index.php?$query_string;
}
配合 htpasswd -c /www/server/nginx/conf/.htpasswd admin 生成密码文件,重载 Nginx 即可。这一层防护即便后台密码再次泄漏,攻击者也得先过 HTTP 鉴权那一关。
第五,把 ECshop 升级到最新补丁版本。老版本 ECshop 有过几个高危的 SQL 注入和远程代码执行漏洞(比如 ECshop 2.x/3.x 的 user.php SQL 注入),管理员密码被改十有八九是因为这类漏洞被利用,光改密码治标不治本。
第六,开启登录失败锁定。ECshop 后台可以在 admin/login.php 里加一段计数逻辑,连续 5 次失败锁 IP 30 分钟。具体代码可以参考保哥之前写过的 ECshop 防爆破文章。
第七,备份当前数据库和源码。重置完成后立刻 mysqldump + tar 整站打包一份,上传到外部存储(如阿里云 OSS、本地 NAS),万一以后再次出问题有干净备份回滚。
排错:脚本运行后还是登不进去怎么办
保哥踩过几次坑,列在下面省得你再走一遍弯路:
报错 "Call to a member function table() on null":说明 init.php 没正确加载,绝大部分情况是 reset.php 没放在网站根目录,或者放对了但 data/config.php 里数据库配置已经过期。
脚本说重置成功但登录还是失败:先去 phpMyAdmin 看 ecs_admin_user 表,确认 password 字段确实变成了 e10adc3949ba59abbe56e057f20f883e、ec_salt 字段确实是空字符串。如果这两个值都对还登不进去,检查 ECshop 的登录验证码、IP 白名单设置(admin/login.php 里 $_CFG['admin_ip'])。
登录成功但后台一片空白:八成是 session 目录权限问题,把 temp/sessions 目录权限设为 755,所有者改成 PHP 运行用户(一般是 www 或 nobody)。
登录界面报"非法操作":是验证码 session 没生效。检查 data/cache 目录读写权限,并清空浏览器 cookie 重新打开。
登录后立刻被踢出,循环回到登录页:可能是被攻击者预留了反向 webshell 在监控登录事件。这种情况下需要彻底审计 admin 目录下所有 PHP 文件,找到 base64_decode、eval、assert 等危险函数的可疑代码。
ECshop 还能继续用吗:项目 EOL 与迁移建议
不得不提一下:ECshop 在 2018 年之后官方已经基本停止维护,最新的 4.x 版本也没什么新功能,只是修小 bug。如果你的电商业务还在持续投入,保哥会建议至少做迁移规划:
- 1 万 SKU 以下的小店:可以考虑迁到 Shopify、有赞、微盟等 SaaS,省运维。
- 1 万到 10 万 SKU 的中型店:自建可以用 Magento 2 开源或 OpenCart,也可以考虑国内 ShopXO。
- 10 万 SKU 以上:用 Magento 2 + Elasticsearch 是国际通用方案。
留在 ECshop 也能跑,但要做好安全加固——按本文第七节做完那七项加固,再加上每月一次安全扫描,基本能撑住主流自动化攻击。
常见问题解答
reset.php 这个方法对 ECshop 4.0 和大商创还有效吗?
基本有效,但要注意大商创 ecmoban_dsc 的管理员表名是 ecs_admin_user 不变,但部分定制版本拆分了 ec_salt,建议先在测试环境跑一遍。ECshop 4.0 保哥实测脚本可用,前提是把 password = MD5('123456') 改成 password_hash 生成的 bcrypt 新版加密格式,否则会失败。
我改密码后能不能不删 reset.php 留着以后还能用?
千万别。这个文件没有任何鉴权,任何人访问都能把管理员密码重置成 123456,等于给你的网站开了一个永久后门。每次需要用的时候临时上传、用完立刻删掉,是唯一安全的姿势。如果一定要留,参考方法一里给的加 key 鉴权版本。
能不能不改成 admin 这个用户名保留我原来的用户名?
可以。把 SQL 里的 user_name = admin 这一段去掉,只更新 password 和 ec_salt 即可,原用户名会保留。SQL 改成 SET password = MD5 新密码 ec_salt = 空 WHERE user_id = 1 即可。
MD5 加盐已经被认为不够安全 ECshop 为什么还在用?
ECshop 的核心引擎从 2010 年定型后就基本没怎么变过,加盐 MD5 在 2010 年算业界主流。现在确实不够强,但对于大多数小微商城来说,配合强密码加后台目录二次鉴权加及时打补丁,安全性是可以接受的。如果对安全要求高,建议直接换 Shopify、有赞这类 SaaS,或者迁移到 Magento 2 加 OpenCart 等用 bcrypt 的现代电商系统。
多个管理员账号都忘记密码怎么处理?
把 SQL 里的 user_id = 1 条件去掉,对全表执行 UPDATE 即可。这样所有管理员密码全部重置为 123456。注意这是核选项,重置完所有管理员都需要重新设密码,并通知到对应负责人。如果只想重置某几个特定账号,把 user_id IN 1,3,5 这种 IN 列表写进去。
能不能加密码重置时的二次确认避免误触?
可以。在 reset.php 里加一个表单和 token:第一次访问显示确认按钮,提交后带 token 才执行 UPDATE。具体代码就是给 reset.php 加个 if (empty($_POST['confirm'])) 显示按钮,否则才走 UPDATE 流程。这样能避免被搜索引擎爬虫或测试请求意外触发。
密码重置后能从日志看出是谁改的吗?
ECshop 后台有"系统日志"功能,但只记录通过后台 UI 操作的事件。直接 SQL 改库不会留下后台日志,但会留下 MySQL 的 binlog(如果开启了)和服务器访问日志。如果你怀疑是入侵,重点查 nginx access.log 里 reset.php 或 admin/privilege.php 的访问 IP 与时间,对照客户操作时间表就能定位。
批量站点的统一密码重置脚本
给运维多站的同行额外加一个场景:手上同时维护 10+ 个 ECshop 站,每年都有几个客户来问密码重置。如果每次都手工传脚本,效率太低。可以做一个批量脚本,挨个 SSH 到客户服务器执行:
#!/bin/bash
# 批量重置 ECshop 管理员密码
SITES=(
"client1.com /www/wwwroot/client1.com"
"client2.com /www/wwwroot/client2.com"
)
for site in "${SITES[@]}"; do
read -r domain path <<< "$site"
echo "[$domain] uploading reset.php"
scp reset.php root@server:$path/reset.php
echo "[$domain] running"
curl -s "https://$domain/reset.php?key=p7Kb9vZmTwMx"
echo "[$domain] cleaning up"
ssh root@server "rm $path/reset.php"
done
实际场景里几乎没人这样做,但对于"接管 N 个老客户站点的初始审计"很有用。
写在最后
保哥写这篇文章的初衷是把这些年踩过的坑一次性讲清楚,省得每个客户来问都重复一遍。如果你按照上面的流程走完还是搞不定,可以把报错截图发给保哥看看,多半是某个边角细节没对齐。重置完成后一定要把后续 7 项安全收尾做完,否则下个月可能又要来一次。