security: 全面安全加固 v2.1
严重修复: - C1: JWT密钥生成64字节随机字符串替代硬编码占位符 - C2: 数据库密码改为随机强密码,移除宿主机端口映射 - C3: 管理员默认密码改为强密码 高危修复: - H1: 移除数据库54326端口映射,仅内部网络访问 - H3: 添加X-Frame-Options/CSP/XSS-Protection等安全头 - H4: 前端所有innerHTML拼接处添加escapeHtml转义防XSS - H5: SMTP密码在API响应中脱敏显示 - H6: 债务列表接口添加用户隔离,普通用户只能看自己的数据 - H7: 债务详情/计划/记录接口添加归属校验 - H8: 账户级暴力破解锁定(5次失败锁定5分钟) - H9: 速率限制数据增加过期清理机制防内存泄漏 中危修复: - M1: 注册/重置密码接口添加后端密码强度校验(最少8位) - M4: 添加全局异常处理器,500错误不再暴露堆栈 - M5: Docker容器改为非root用户运行 - M6: 添加.dockerignore排除pyc和敏感文件 - M7: 管理员操作写入审计日志 - M8: CORS限制为实际使用的HTTP方法和头 其他: - Nginx隐藏版本号(server_tokens off) - 管理员角色修改添加枚举校验 - 密码重置验证码接口返回统一错误信息
This commit is contained in:
@@ -271,7 +271,7 @@ let allGroups=[];
|
||||
let allUsers=[];
|
||||
let currentGroupId=null;
|
||||
|
||||
const $=id=>document.getElementById(id);
|
||||
const $=id=>document.getElementById(id);function escapeHtml(s){if(!s)return'';const d=document.createElement('div');d.textContent=s;return d.innerHTML;}
|
||||
|
||||
async function apiFetch(path,opts={}){
|
||||
const headers={'Content-Type':'application/json',...opts.headers};
|
||||
@@ -313,11 +313,11 @@ async function loadGroups(){
|
||||
const tb=$('groupTable');
|
||||
if(!allGroups.length){tb.innerHTML='<tr><td colspan="4" class="empty">暂无组</td></tr>';return;}
|
||||
tb.innerHTML=allGroups.map(g=>`<tr>
|
||||
<td><strong>${g.name}</strong></td>
|
||||
<td>${g.description||'-'}</td>
|
||||
<td><strong>${escapeHtml(g.name)}</strong></td>
|
||||
<td>${escapeHtml(g.description||'-')}</td>
|
||||
<td>${g.member_count}人</td>
|
||||
<td>
|
||||
<button class="btn-sm btn-enable" onclick="showMembers(${g.id},'${g.name}')">管理成员</button>
|
||||
<button class="btn-sm btn-enable" onclick="showMembers(${g.id},'${escapeHtml(g.name)}')">管理成员</button>
|
||||
<button class="btn-sm btn-danger" onclick="deleteGroup(${g.id})">删除</button>
|
||||
</td>
|
||||
</tr>`).join('');
|
||||
@@ -344,11 +344,11 @@ async function loadUsers(){
|
||||
if(u.status==='pending')actions=`<button class="btn-sm btn-approve" onclick="approveUser(${u.id})">通过</button>`;
|
||||
if(u.status==='active')actions=`<button class="btn-sm btn-disable" onclick="setStatus(${u.id},'disabled')">禁用</button>`;
|
||||
if(u.status==='disabled')actions+=` <button class="btn-sm btn-enable" onclick="setStatus(${u.id},'active')">启用</button> <button class="btn-sm btn-restore" onclick="setStatus(${u.id},'pending')">恢复待审</button>`;
|
||||
actions+=` <button class="btn-sm btn-enable" onclick="showEditUser(${u.id},'${u.account}','${u.nickname||''}','${u.real_email||''}','${u.role}')">编辑</button>`;
|
||||
actions+=` <button class="btn-sm btn-enable" onclick="sendResetLink(${u.id},'${u.account}')">改密</button>`;
|
||||
actions+=` <button class="btn-sm btn-danger" onclick="deleteUser(${u.id},'${u.account}')">删除</button>`;
|
||||
actions+=` <button class="btn-sm btn-enable" onclick="showEditUser(${u.id},'${escapeHtml(u.account)}','${u.nickname||''}','${u.real_email||''}','${u.role}')">编辑</button>`;
|
||||
actions+=` <button class="btn-sm btn-enable" onclick="sendResetLink(${u.id},'${escapeHtml(u.account)}')">改密</button>`;
|
||||
actions+=` <button class="btn-sm btn-danger" onclick="deleteUser(${u.id},'${escapeHtml(u.account)}')">删除</button>`;
|
||||
}
|
||||
return`<tr><td>${u.account}</td><td>${u.nickname}</td><td>${emailDisplay}</td><td>${roleBadge}</td><td>${groupBadge}</td><td>${u.debt_count}笔</td><td>${statusBadge}</td><td>${actions}</td></tr>`;
|
||||
return`<tr><td>${escapeHtml(u.account)}</td><td>${escapeHtml(u.nickname)}</td><td>${emailDisplay}</td><td>${roleBadge}</td><td>${groupBadge}</td><td>${u.debt_count}笔</td><td>${statusBadge}</td><td>${actions}</td></tr>`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
@@ -419,7 +419,7 @@ async function showMembers(gid,gname){
|
||||
const members=allUsers.filter(u=>u.group_id===gid);
|
||||
$('memberList').innerHTML=members.length?members.map(u=>`<span style="display:inline-block;padding:3px 8px;margin:2px;background:#eef2f7;border-radius:4px;font-size:12px">${u.nickname||u.account} <a href="#" onclick="removeMember(${u.id});return false" style="color:#dc2626;margin-left:4px">×</a></span>`).join(''):'<span style="font-size:12px;color:#94a3b8">暂无成员</span>';
|
||||
const unassigned=allUsers.filter(u=>!u.group_id&&u.role!=='admin');
|
||||
$('memberSelect').innerHTML=unassigned.map(u=>`<option value="${u.id}">${u.nickname||u.account} (${u.account})</option>`).join('');
|
||||
$('memberSelect').innerHTML=unassigned.map(u=>`<option value="${u.id}">${u.nickname||u.account} (${escapeHtml(u.account)})</option>`).join('');
|
||||
$('memberModal').classList.add('show');
|
||||
}
|
||||
function closeMemberModal(){$('memberModal').classList.remove('show');currentGroupId=null;}
|
||||
|
||||
Reference in New Issue
Block a user