在小程序里做 SSH 密钥管理页:安全边界与体验的取舍
页面职责:一个"演示级危险操作"的样板
这个页面管理 SSH 密钥:卡片式列表展示(图标、名称、指纹、添加日期、删除按钮),右上角 FAB 弹出"添加密钥"弹窗(填名称 + 粘贴私钥),点卡片弹出"密钥详情"(展示名称/指纹/添加时间/私钥内容预览),支持复制指纹、删除(带红色确认)。
数据模型很简单:
{ id, name, fingerprint, createdAt, preview }
其中 preview 是整页安全设计的核心:私钥永远只存截断的预览。
安全设计一:私钥只存"前 60 字符 + 已隐藏"
导入密钥时,数据先被"打码"再入库:
const newKey = {
id: 'key-' + Date.now(),
name: f.name.trim(),
fingerprint: 'SHA256:' + this.generateFakeFingerprint(),
createdAt: this.getDateStr(),
preview: f.privateKey.substring(0, 60) + '...(已隐藏)'
}
app.globalData.sshKeys.push(newKey)
preview 只保留前 60 字符 + 文字后缀。这个设计的意图很清楚:详情页展示的是"内容预览"而不是"内容"——用户能看到私钥开头的 -----BEGIN OPENSSH PRIVATE KEY----- 格式,确认"这是我导入的那把钥匙",但看不到完整密钥体。在 UI 端,这块预览被渲染成深色终端风格、等宽字体,视觉上强化"这是敏感内容"的暗示。
这里有一个值得展开的取舍:为什么不一点不显示? 完全隐藏,用户无法确认导入的是哪把钥匙;完整显示,等于把私钥明文摊在手机屏幕上(截屏、窃屏风险)。截断预览是对"可辨识"和"不可泄露"的折中——用 60 字符换"确认感",用隐藏换"安全"。
安全设计二:只有指纹可复制,私钥不可复制
页面上唯一暴露的复制入口是指纹(wx.setClipboardData 复制 SHA256:…),私钥预览虽然可长按选中,但内容本身已经是打码的。
这又是一个刻意的边界:指纹的价值是"识别这把钥匙",可复制无风险;私钥的价值是"使用",可复制即泄露。"能做什么"被限制在"安全边界内最小可用集合"——读得到识别信息,拿不到执行信息。
配套的还有删除操作的危险 UX:
wx.showModal({
title: '删除密钥',
content: '确定要删除「' + key.name + '」吗?',
confirmText: '删除',
confirmColor: '#FF3D00', // 红色确认按钮
success: (res) => { if (res.confirm) { /* splice + 刷新 */ } }
})
二次确认 + 红色按钮。密钥删除是不可逆操作(删了就没有这把钥匙了),红色不是为了好看,是让"危险"在视觉上叫出来——这与页面里"危险操作区"用红框的 server-edit 页是同一套语言。
安全设计三:全项目统一的"不落真值"策略
SSH 密钥页不是孤例。对照 pages/ai-config:API Key 配置有显隐开关,且 mock 值本身就是打码的(sk-****…3a7b)。整个项目对敏感字段采用同一策略:能打码就打码、能隐藏就隐藏、绝不落真值。
这个策略在 demo 阶段的价值尤其大:mock 数据会进 git、会被人 clone,如果里面埋了真密钥,等于把凭据公开在仓库里。"数据本身就是打码的"让演示代码天然不会泄密——哪怕整页代码被搬走,泄露的也只是假数据。
诚实的另一面:demo 的痕迹
写完亮点,也要如实列出"看起来安全、实际是演示级"的地方:
- 指纹是假的:
generateFakeFingerprint()从 62 字符集随机拼 40 位——没有任何 SHA 计算,SHA256:前缀后面跟的是随机数,不是真实摘要;
generateFakeFingerprint() {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
let str = ''
for (let i = 0; i < 40; i++) str += chars[Math.floor(Math.random() * chars.length)]
return str
}
- 没有生成密钥功能:只有"导入粘贴",不能在本机生成新密钥对;
- 没有编辑功能,
preventTap() {}是个空占位方法; - 数据只存在
globalData里:内存态,没有落盘、没有setStorageSync、没有后端,重启即重置。
这些"未完成"不等于错误——它们恰好标出了安全设计在产品里要补的下一公里:真实指纹要由密钥算法计算、密钥要支持生成、删除前要提示"该密钥关联的服务器将无法连接"、数据要持久化并支持加密存储。当前的"安全边界"是交互层的边界,不是数据层的边界。
总结:把"安全"当设计约束,而不是事后贴标签
这个页面值得借鉴的不是某个具体技巧,而是把安全当成与体验并列的设计约束:
- 敏感信息不上屏:截断预览 + 只复制指纹,用"最小可用信息"换安全感;
- 危险操作叫出来:红色确认,让用户停一下;
- 数据本身打码:mock 阶段就杜绝泄密源头。
它同时提醒我:交互层的安全设计要配得上数据层的能力。当数据还住在内存 mock 里时,"只存预览"更多是产品姿态;等接上真实密钥库和真实后端,这个姿态才真正变成防线——而那一步,正是这个 demo 的下一站。
- 点赞
- 收藏
- 关注作者
评论(0)