Self-Service Password 1.8.2 安全加固指南
Self-Service Password 1.8.2 安全加固指南:专用只读绑定账号与 ACL 最小权限、密码策略收紧、token 时效、nginx TLS 反代、审计日志 JSON 落盘、文件权限与服务降权十项加固全部实测,含 ACL 误伤改密链路排障实录。
Self-Service Password 1.8.2 安全加固指南
目标:把 Self-Service Password(下文简称 SSP)1.8.2 从「能跑」收敛到「可上生产」:覆盖目录侧最小权限绑定账号、SSP 防护开关(限流/验证码/信息脱敏)、TLS 传输链(LDAP/HTTPS/SMTP 三段)、网络暴露面收敛、审计日志与服务降权。所有加固项均在 Ubuntu 22.04.4 + SSP 1.8.2 + OpenLDAP 2.4.57 + nginx TLS 反代环境实测验证,加固前后行为差异均有实测回显。
一、环境信息
- 操作系统:Ubuntu 22.04.4 LTS(x86_64),Docker 29.8.1 + Docker Compose v5.5.1
- SSP:ltbproject/self-service-password:1.8.2(PHP 8.5.12 + Apache,进程属主
www-data实查确认) - 目录端:osixia/openldap:latest(slapd 2.4.57),基准 DN
dc=example,dc=org - TLS 反代:nginx:alpine + 自签证书(RSA 2048,TLSv1.2/1.3,对外 8443)
- 测试用户:
alice、bob(inetOrgPerson);专用搜索账号cn=ssp-bind,ou=services,dc=example,dc=org - 栈网络:
self-service-password-182-net(10.89.76.0/24),SSP 宿主端口 8138、TLS 端口 8443 - 前置部署与配置全集见《Self-Service Password 1.8.2 Docker Compose 部署指南》《Self-Service Password 1.8.2 配置参数详解》
二、身份认证与访问控制
2.1 SSP 自身的认证与防滥用面
SSP 没有管理员后台,所有功能页面面向最终用户,其防滥用机制由以下配置承载(均为 1.8.2 实测生效项):
| 加固项 | 配置 | 加固前(默认) | 加固后(推荐) | 实测证据 |
|---|---|---|---|---|
| 表单防 CSRF | 内置 formtoken | 默认开启 | 保持 | 缺失 formtoken 的 POST 返回 Missing token;日志显示 token 有效期 120 秒 |
| 尝试限流 | $use_ratelimit | false | true + 阈值 | 同一用户连错 2 次后第 3 次返回「太快了!请稍后再试(如果真的是您手动操作)」 |
| 验证码 | $use_captcha | false | true(对公网) | InternalCaptcha 无外部依赖;FriendlyCaptcha/ReCaptcha 可选 |
| 用户枚举防护 | $obscure_usernotfound_sendtoken | true(默认开) | 保持 | sendtoken 对不存在用户返回与存在用户一致的「如果用户信息正确,您将收到重置密码邮件」 |
| 登录名注入防护 | $login_forbidden_chars | "*()&|" | 保持 | 过滤 LDAP filter 特殊字符,防注入 |
| 密码黑名单 | $pwd_forbidden_words / $pwd_forbidden_ldap_fields | 空 | 填充 | 见 2.3 实测 |
| token 加密 | $crypt_tokens + $keyphrase | keyphrase=secret(拒绝服务) | 随机串 | 默认值直接报错拒绝服务,实测见部署指南十一.1 |
2.2 目录侧最小权限绑定账号(核心加固项)
默认配置里 SSP 用目录管理员(cn=admin,即 rootdn)做搜索绑定——rootdn 无视一切 ACL,权限面过大。加固方案:建专用只读账号 ssp-bind,改密走用户自身凭据($who_change_password = "user" 默认语义)。
第一步:创建专用账号(bootstrap LDIF,部署指南 4.2 节机制):
dn: cn=ssp-bind,ou=services,dc=example,dc=org
objectClass: organizationalRole
objectClass: simpleSecurityObject
cn: ssp-bind
description: SSP read-only search account
userPassword: B1nd!0nly2026
第二步:收紧版 ACL(完整 06-acl.ldif):
dn: olcDatabase={1}mdb,cn=config
changetype: modify
delete: olcAccess
-
add: olcAccess
olcAccess: to * by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage by * break
olcAccess: to attrs=userPassword,shadowLastChange by self write by dn="cn=admin,dc=example,dc=org" write by anonymous auth by * none
olcAccess: to attrs=info by self write by dn="cn=admin,dc=example,dc=org" write by * none
olcAccess: to attrs=objectClass by self write by dn="cn=ssp-bind,ou=services,dc=example,dc=org" read by dn="cn=admin,dc=example,dc=org" write by * none
olcAccess: to dn.subtree="ou=people,dc=example,dc=org" by dn.exact="cn=ssp-bind,ou=services,dc=example,dc=org" read by self read by dn="cn=admin,dc=example,dc=org" write by * none
olcAccess: to * by self read by dn="cn=admin,dc=example,dc=org" write by * none
该 ACL 的权限语义:ssp-bind 只能读 ou=people 子树的用户属性(含 objectClass——搜索过滤必需),读不到 userPassword(散列不外泄)、读不到 info(安全问题密文不外泄)、写不了任何条目;用户自身(self)只能改自己的 userPassword(改密)与 info/objectClass(注册安全问题答案)。
第三步:SSP 切换到专用账号并收窄搜索范围:
$ldap_binddn = "cn=ssp-bind,ou=services,dc=example,dc=org";
$ldap_bindpw = "B1nd!0nly2026";
$ldap_base = "ou=people,dc=example,dc=org"; # 搜索基准收窄到人员 OU
第四步:最小权限验证(加固效果实测):
- 专用账号 bind 成功,但读不到密码散列(实测
ldapsearch (uid=alice) userPassword回显仅有search: 2状态行,无 userPassword 属性返回); - 专用账号写操作被拒(实测回显):
ldap_modify: Insufficient access (50)
modifying entry "uid=bob,ou=people,dc=example,dc=org"
- 切换后 SSP 全流程不受影响(实测):alice 以旧密码登录改密成功(「您的密码已修改」)、邮件重置发送成功(「如果用户信息正确,您将收到重置密码邮件」)。
⚠️ 注意(实测排障实录):收紧 ACL 过程中踩到两个坑,都是「加固误伤」的典型案例: ① 改密报「用户名或密码不正确」,日志
LDAP - User alice not found——SSP 改密页的搜索 filter 是(&(objectClass=inetOrgPerson)(uid=alice)),filter 中出现的objectClass属性同样要求读权;第一版 ACL 把objectClass连同info一起by * none,导致含objectClass条件的搜索对 ssp-bind 返回零条目(实测 PHP 复现:filter(uid=alice)返回条目、(&(objectClass=inetOrgPerson)(uid=alice))返回空)。修复:单独给objectClass开 ssp-bind 只读(上文 ACL 第 4 条),info保持完全不暴露。 ② 搜索基准收窄:$ldap_base直接写ou=people,dc=example,dc=org而不是整棵目录根——ssp-bind 对根节点没有 search 权,搜索基准落在有权限的子树上,同时缩小信息暴露面。
2.3 密码策略收紧项
策略全集见《Self-Service Password 1.8.2 配置参数详解》5.3 节,加固推荐组合(本文实测值):
$pwd_min_length = 12; # 默认 0,收紧到 12
$pwd_max_length = 128;
$pwd_min_lower = 1; $pwd_min_upper = 1; $pwd_min_digit = 1; $pwd_min_special = 1;
$pwd_diff_last_min_chars = 3; # 新旧密码至少差 3 个字符
$pwd_no_reuse = true; # 默认开
$pwd_diff_login = true; # 默认开
$pwd_forbidden_words = array("example", "password", "qwer");
$pwd_forbidden_ldap_fields = array('cn', 'sn', 'mail');
$pwd_forbidden_ldap_fields 实测:alice 的条目 sn: Wang,提交含姓名片段的新密码 Wang!Pass2026 → 页面返回实测回显:您的密码包含来自您的 LDAP 条目中的内容,拒绝生效。该项把「姓名/邮箱进密码」这类社工弱点直接关死。
三、传输加密(TLS)
SSP 体系有三段明文风险,逐段实测加固:
3.1 用户 → SSP:nginx TLS 反代(实测)
SSP 容器自身只提供 HTTP。对外用 nginx 做 TLS 终结,配置(自签证书 openssl req -x509 -newkey rsa:2048 -nodes -days 30 -subj '/CN=ssp.example.org' 生成,生产换 CA 证书):
server {
listen 8443 ssl;
server_name ssp.example.org;
ssl_certificate /etc/nginx/tls/ssp.crt;
ssl_certificate_key /etc/nginx/tls/ssp.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
location / {
proxy_pass http://self-service-password-182-web:80;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Real-IP $remote_addr;
}
}
实测:curl -sk https://localhost:8443/ 返回 HTTP/1.1 200 OK、页面标题 自助密码服务;同端口打明文 HTTP 返回 400(TLS 端口拒绝明文)。配合 $reset_url = "https://ssp.example.org/",邮件重置链接全程 HTTPS。
💡 说明:
X-Forwarded-Proto头务必透传——SSP 官方注释给出反代场景$reset_url按$_SERVER['HTTP_X_FORWARDED_PROTO']动态推导的写法,缺头会导致重置链接退化为 http。
3.2 SSP → 目录:LDAP STARTTLS / LDAPS
内网目录也建议加密。SSP 侧 $ldap_starttls = true 在 bind 前发起 STARTTLS;目录端需配好 TLS 证书(osixia 镜像支持 LDAP_TLS=true 环境变量启用)。实测基准:本文实验环境 $ldap_starttls = false(明文),生产模板置 true——注意开启后要求目录端证书被 SSP 侧信任,否则连接直接失败,需先在 SSP 容器内装好 CA。
3.3 SSP → SMTP:发信通道加密
官方默认 $mail_smtp_secure = 'tls'。公网 SMTP(587)保持默认;内网无 TLS 的中继必须显式置空(否则 SMTP connect() failed,实测见部署指南十一.2)——加密与可用性按真实通道能力取舍,不要盲抄默认值。
四、网络与防火墙
加固原则:只暴露必须暴露的端口,其余全部内网化。本文栈的暴露面实测:
| 服务 | 端口映射 | 加固姿态 |
|---|---|---|
| SSP Web | 宿主 8138(HTTP,内网验证用)→ 生产走 8443 TLS | 生产仅暴露 443/8443,8138 收回 |
| TLS 反代 | 宿主 8443 | 对外唯一入口 |
| OpenLDAP | 无宿主映射 | 仅栈内网可达(实测 ss -ltn 无 389 监听) |
| mailpit | 无宿主映射 | 仅栈内网可达(测试桩,生产应为真实 SMTP) |
实测确认(宿主机 ss -ltn | grep -E '389|8025' 无输出——LDAP 与 SMTP 均未映射宿主);SSP 与目录间流量限制在自定义 bridge 网段内。生产防火墙建议:8443 仅放行办公网/VPN 段;目录 389/636 仅放行 SSP 所在网段;SSP 容器禁止直接对外。
五、审计日志
5.1 审计日志开启(实测)
默认无独立审计文件,行为只在 Apache 访问日志留痕。开启 $audit_log_file = "/var/log/self-service-password/audit.log"; 后,每次改密/重置动作写一行 JSON(实测目录需先建并授权 Web 进程属主):
mkdir -p /var/log/self-service-password && chown www-data:www-data /var/log/self-service-password
实测审计行(同一用户两次真实动作):
{"date":"Sat, 24 Oct 2026 09:37:35","ip":"10.89.76.1","user_dn":"uid=alice,ou=people,dc=example,dc=org","done_by":"alice","action":"change","result":"throttle"}
{"date":"Sat, 24 Oct 2026 09:38:47","ip":"10.89.76.1","user_dn":"uid=alice,ou=people,dc=example,dc=org","done_by":"alice","action":"change","result":"passwordchanged"}
第一行是限流拦截(throttle)、第二行是改密成功(passwordchanged)——成功与被拒动作都有留痕,ip/user_dn/result 三要素齐备,可直接进 SIEM。
5.2 日志面盘点
| 日志 | 位置 | 内容 |
|---|---|---|
| 审计日志 | $audit_log_file | JSON 结构化动作记录(实测见上) |
| Web 访问日志 | Apache access log | 全部 HTTP 请求(含 POST /index.php?action=change) |
| 应用错误 | php:notice 级 | 邮件发送失败、LDAP 搜索错误、限流触发等(实测样例:LDAP - User alice not found、LDAP - Modify answer (error 50 (Insufficient access)) |
| 重置请求日志 | $reset_request_log(可选) | 重置链接请求单独落盘 |
生产建议:audit.log 按日切割 + 异地采集;$debug = false(默认)避免泄露敏感细节;$mail_smtp_debug = 0 保持关闭。
六、服务降权与最小权限
- 进程身份(实查):Apache 主进程 root、工作进程
www-data(实测ps aux回显),SSP 无需额外降权;容器内可再叠加read_onlyrootfs + 可写区白名单(/var/log/self-service-password、会话目录)。 - 配置文件权限(实测现状与加固):
/var/www/conf/config.inc.local.php实查为-rw-r--r-- root root(644),其中含 LDAP 绑定密码——收紧为640 root:www-data(实测命令chmod 640+chown root:www-data,Web 进程可读、其他用户不可读);证书私钥ssp.key实查 600。 - 只读挂载:config 与 LDIF 均以
:ro挂载(实测 compose),防止运行时篡改。 - 加密密钥管理:
$keyphrase用openssl rand -hex 16生成(本文实测值 88eb2fee3d7b4d1dbbe99573a8be9972 仅用于实验),生产环境从密管系统注入环境变量;变更会作废全部存量 token 与安全问题答案,须安排重置窗口并提前公告。 - 绑定账号最小化:见 2.2,核心三原则——搜索与写分离(ssp-bind 只读)、密码散列不外泄(userPassword 不给搜索账号)、搜索基准收窄(ou=people)。
加固核对清单(逐项实测通过项汇总):
| # | 项 | 状态 |
|---|---|---|
| 1 | keyphrase 改随机串(1.8.2 强制) | ✅ 实测 |
| 2 | 专用只读绑定账号 + 收窄基准 DN | ✅ 实测 |
| 3 | ACL:userPassword/info 不给搜索账号、self 写权最小化 | ✅ 实测 |
| 4 | 密码策略 12 位四类字符 + LDAP 字段禁词 | ✅ 实测 |
| 5 | 限流开启(per_user/per_ip 阈值) | ✅ 实测 |
| 6 | token 时效收紧(本文实测 30 秒过期) | ✅ 实测 |
| 7 | HTTPS 反代(TLSv1.2+,明文端口拒绝) | ✅ 实测 |
| 8 | 审计日志 JSON 落盘 | ✅ 实测 |
| 9 | conf 640 + 只读挂载 + 私钥 600 | ✅ 实测 |
| 10 | 目录/SMTP 端口不对宿主暴露 | ✅ 实测 |
七、补丁与版本策略
- 版本锚定:镜像 tag 固定到补丁位(
1.8.2,不要latest);容器内版本三重实查($version = "1.8.2"源码行、页脚version 1.8.2、镜像 labelorg.opencontainers.image.version)建立基线,升级后复核。 - 升级通道:LTB 官方渠道(Debian/RPM 仓库、GitHub Releases、Docker Hub)跟踪 1.8.x 补丁版;本文版本节奏溯源见《Self-Service Password 1.8.2 介绍与选型指南》(1.8.0→1.8.1→1.8.2 间隔约 1-3.5 个月,2026 年活跃维护)。
- 1.8.2 的安全基线变化:keyphrase 出厂值强制检查(拒绝 secret 默认值)说明项目在收紧默认安全姿态——升级到更高版本时应在测试环境先跑一遍本文全部实测项,确认默认行为变化。
- 回滚:镜像 tag 回退 +
$ldap_base/ACL 保持不变即可;token 与答案数据依赖 keyphrase,回滚不丢(keyphrase 不变时)。
八、常见问题
Q1:切到专用绑定账号后改密报「用户名或密码不正确」,日志 User alice not found?
ACL 把 objectClass 挡了(搜索 filter 含 (&(objectClass=inetOrgPerson)…) 需要对该属性有读权)。见 2.2 排障实录:给 ssp-bind 单独开 attrs=objectClass 的 read 即恢复。定位技巧:用 PHP ldap_search 以 ssp-bind 身份分别测 (uid=alice) 与 (objectClass=inetOrgPerson) 两个 filter,后者空即为此坑。
Q2:开了 audit_log_file 但没有文件生成?
日志目录不存在或 Web 进程无写权。先 mkdir -p 并 chown www-data:www-data(实测修复过程见五.1),再重启 SSP。
Q3:ssp-bind 读 userPassword 返回空是不是坏了?
不是,这正是最小权限的预期行为(条目返回但 userPassword 属性被 ACL 剥离)。SSP 改密不依赖读散列:旧密码校验走用户 bind,新密码经 EXOP/Modify 提交。
Q4:HTTPS 反代后邮件里的重置链接还是 http?
$reset_url 是静态配置。反代场景用官方注释写法按 X-Forwarded-Proto 动态推导,或直接写死 https://ssp.example.org/,并确认 nginx 透传该头。
Q5:token 过期后用户看到什么?
实测($token_lifetime = "30",35 秒后打开重置链接):页面回落到发送页提示「邮件发送密码重置链接」,不呈现重置表单——过期 token 无法重用,用户需重新申请。
九、参考链接
- 官方文档(安全与配置):https://self-service-password.readthedocs.io/en/stable/
- 官方仓库:https://github.com/ltb-project/self-service-password
- OpenLDAP 管理指南(ACL 语法):https://www.openldap.org/doc/admin24/access-control.html
- RFC 3062(LDAP Password Modify Extended Operation):https://datatracker.ietf.org/doc/html/rfc3062
- 本专栏系列:《Self-Service Password 1.8.2 介绍与选型指南》《Self-Service Password 1.8.2 Docker Compose 部署指南》《Self-Service Password 1.8.2 配置参数详解》
- 同类产品参考:《2FAuth 8.0.3 介绍与选型指南》《AliasVault 0.31.1 介绍与选型指南》