在小程序里做 SSH 密钥管理页:安全边界与体验的取舍

举报
哦啦啦啦啦 发表于 2026/10/01 14:41:36 2026/10/01
【摘要】 页面职责:一个"演示级危险操作"的样板这个页面管理 SSH 密钥:卡片式列表展示(图标、名称、指纹、添加日期、删除按钮),右上角 FAB 弹出"添加密钥"弹窗(填名称 + 粘贴私钥),点卡片弹出"密钥详情"(展示名称/指纹/添加时间/私钥内容预览),支持复制指纹、删除(带红色确认)。数据模型很简单:{ id, name, fingerprint, createdAt, preview }其中...

页面职责:一个"演示级危险操作"的样板

这个页面管理 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 的痕迹

写完亮点,也要如实列出"看起来安全、实际是演示级"的地方:

  1. 指纹是假的: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
}
  1. 没有生成密钥功能:只有"导入粘贴",不能在本机生成新密钥对;
  2. 没有编辑功能,preventTap() {} 是个空占位方法;
  3. 数据只存在 globalData 里:内存态,没有落盘、没有 setStorageSync、没有后端,重启即重置。

这些"未完成"不等于错误——它们恰好标出了安全设计在产品里要补的下一公里:真实指纹要由密钥算法计算、密钥要支持生成、删除前要提示"该密钥关联的服务器将无法连接"、数据要持久化并支持加密存储。当前的"安全边界"是交互层的边界,不是数据层的边界。

总结:把"安全"当设计约束,而不是事后贴标签

这个页面值得借鉴的不是某个具体技巧,而是把安全当成与体验并列的设计约束:

  • 敏感信息不上屏:截断预览 + 只复制指纹,用"最小可用信息"换安全感;
  • 危险操作叫出来:红色确认,让用户停一下;
  • 数据本身打码:mock 阶段就杜绝泄密源头。

它同时提醒我:交互层的安全设计要配得上数据层的能力。当数据还住在内存 mock 里时,"只存预览"更多是产品姿态;等接上真实密钥库和真实后端,这个姿态才真正变成防线——而那一步,正是这个 demo 的下一站。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。