fix(user): 修复密码哈希通过免登录接口暴露问题
- 在 User 实体的 password 和 payPassword 字段添加@JsonProperty(WRITE_ONLY)注解 - 保证密码字段不再通过 HTTP 接口响应体序列化输出 - 保持反序列化能力,注册、改密及批量导入接口不受影响 - RabbitMQ 消息同步时显式补回密码字段,确保 MQ 报文不变 - 新增 UserCredentialSerializationTest,覆盖 HTTP 响应、反序列化和 MQ 同步报文场景 - 文档更新,说明已修复接口列表及仍需决策的免登录接口安全隐患 - 将 server-api 版本升级至 2.0
This commit is contained in:
@@ -205,7 +205,7 @@ open-platform:
|
||||
> `SecurityConfig` 里 GET 的 `/**` 白名单摘掉,再按真实流量补显式白名单,
|
||||
> 影响面较大,建议单独排期。
|
||||
|
||||
### 6.1 用户密码哈希可通过免登录接口读取(未修复,优先级高)
|
||||
### 6.1 用户密码哈希可通过免登录接口读取(已修复)
|
||||
|
||||
`GET /api/system/user/getByUserId/{userId}` 在 `SecurityConfig` 的 GET 白名单里,
|
||||
实测**无需任何凭证**,只带一个 `tenantId` 请求头即可取到用户实体,
|
||||
@@ -220,15 +220,43 @@ curl -H 'tenantId: <任意租户ID>' 'https://server.websoft.top/api/system/user
|
||||
且 `UserMapper` 使用 `SELECT a.*`;同时该接口无 `@PreAuthorize` 又落在 GET 白名单里。
|
||||
`GET /api/system/user/withoutAuth` 属于同一族(免登录 + 返回实体)。
|
||||
|
||||
最小修复是在实体上把这两个字段设为只写:
|
||||
修复方式是在实体上把这两个字段设为只写:
|
||||
|
||||
```java
|
||||
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
|
||||
private String password;
|
||||
```
|
||||
|
||||
这样只影响序列化(不再输出),不影响任何依赖反序列化写入密码的流程。
|
||||
本次未改动,因为会变更既有内部接口的响应结构,需要你确认后单独提交。
|
||||
只影响序列化(不再输出),不影响任何依赖反序列化写入密码的流程。
|
||||
`password` 与 `payPassword` 都已加上该注解,因此所有返回 `User` 实体的接口一并收敛,
|
||||
包括 `getByUserId`、`withoutAuth`、`getByPhone`、`getByUnionid`、`list`、`page` 等。
|
||||
|
||||
> **同步报文已显式保留**:`RabbitMQSyncProducer.sendUserSyncMessage` 把 User 转成 Map 时
|
||||
> 用的是同一个 ObjectMapper,实体改成只写后密码会从报文里消失。为避免悄悄改变
|
||||
> websopy 侧收到的内容,生产者在转换后显式补回 `password` / `payPassword`。
|
||||
> 是否继续在 MQ 报文里带密码哈希,需要 websopy 侧确认后另行决定。
|
||||
> `UserCredentialSerializationTest` 用三个用例锁住了「HTTP 不输出、反序列化可写入、MQ 报文不变」。
|
||||
|
||||
### 6.2 同一批免登录接口仍存在的问题(未修复,需决策)
|
||||
|
||||
修掉密码后,这一族接口仍然免登录返回完整用户实体,且租户由 `tenantId` 请求头指定:
|
||||
|
||||
| 接口 | 问题 |
|
||||
| --- | --- |
|
||||
| `GET /api/system/user/getByUserId/{userId}` | 免登录返回用户详情,含 `idCard`(完整 18 位身份证号)、手机号、邮箱、余额 |
|
||||
| `GET /api/system/user/withoutAuth` | 免登录,一次返回该租户**全部**用户(不受 page/limit 限制,实测租户 5 返回 163 条 × 107 字段) |
|
||||
| `GET /api/system/user/getByPhone/{phone}` | 免登录按手机号查用户,可被枚举(`mp-react-nextjs` 在用) |
|
||||
| `GET /api/system/user/getByUnionid/{unionid}` | 免登录 |
|
||||
| `PUT /api/system/user/updateWithoutLogin` | **完全没有校验**,任何人都能改用户资料 |
|
||||
| `POST /api/system/user/batchBackUserId` | **完全没有校验**,可批量建用户(`mp-react-nextjs` 在用) |
|
||||
|
||||
另有 `updateUserBalanceWithoutLogin`、`addUserBalanceWithoutLogin`、`getUserWithoutLogin`、
|
||||
`updateUserOfficeOpenidWithoutLogin` 使用硬编码常量 `authCode == "1700083"` 作为唯一凭证,
|
||||
该常量对所有租户相同且写在源码里,等同于弱口令。
|
||||
|
||||
`id_card` 列存的是**完整 18 位身份证号**(库里 3173 条有值),与实体上的
|
||||
「身份证号(脱敏)」注释不符,需要单独决定是脱敏还是仅对管理员可见——会直接影响
|
||||
租户后台的实名审核页面,因此未擅自改动。
|
||||
|
||||
## 7. 如何新增一个开放接口
|
||||
|
||||
|
||||
Reference in New Issue
Block a user