安全
Web 应用可能以多种方式被攻击或入侵。本文介绍每个 Shoplazza 应用都应遵循的安全实践。它们保护商家数据,其中大部分也是应用审核的检查项。
防范常见 Web 漏洞
你的应用必须防范常见的 Web 安全漏洞,包括但不限于 OWASP Top 10。OWASP Top 10 是一份标准的安全意识文档,列出了 Web 应用最严重的安全风险,并附有对应的防御速查表和参考资料。
对 Shoplazza 应用来说,重点关注:
- 注入:所有数据库访问都使用参数化查询或 ORM。来自 webhook 和 API 响应的数据同样要当作不可信输入处理,和表单输入一视同仁。
- 跨站脚本(XSS):商家或买家提供的内容在渲染进 HTML 前必须转义,并设置
Content-Security-Policy响应头限制脚本来源。 - 身份认证缺陷:绝不能把 URL 上的
shop参数当作身份凭证。店铺域名要从校验过的 Session Token 或验证过的 OAuth 回调中读取。
如果应用审核中发现漏洞,应用会被拒绝,修复后才能重新提交。想深入学习,可以使用 Web Security Academy——一个带交互实验环境的免费培训资源。
用 TLS 加密传输
客户端(如商家的浏览器)与应用服务器之间交换的所有数据都必须用传输层安全协议(TLS)加密,确保传输中的数据只有你的服务器能读取。应用必须通过 HTTPS 提供服务并配有效证书——自签名证书不被接受。
如果还没有 TLS 证书,可以从 Let's Encrypt 免费获取;任何受信任的证书颁发机构签发的证书都可以。安装后用 SSL Checker 验证配置是否正确。
设置 iframe 防护
嵌入式应用运行在 Shoplazza 后台的 iframe 内。如果你的应用可以被任意站点用 iframe 嵌套,攻击者就能在上面覆盖不可见元素,诱导已登录的商家点击他们并不想执行的操作——这就是点击劫持(clickjacking)。
防护方式是在每个返回 HTML 的响应上设置 Content Security Policy 的 frame-ancestors 指令,只允许当前使用你应用的店铺后台嵌套。店铺后台在各店铺自己的子域名上,所以这个值必须按请求动态设置:
Content-Security-Policy: frame-ancestors https://{shopdomain}.myshoplaza.com;
{shopdomain} 要从校验过的 Session Token 或 OAuth 上下文中取,绝不能来自未验证的查询参数。
如果你的应用不嵌入后台,直接禁止一切嵌套:
Content-Security-Policy: frame-ancestors 'none';
关于点击劫持的更多内容,参考 OWASP Clickjacking 或 MDN 的 frame-ancestors 文档。
安全地生成与校验令牌
你的应用签发的令牌
如果应用依赖自有令牌做用户认证,令牌必须随机生成且至少有 128 位熵(仅在令牌长度受硬性限制时允许 64 位)。使用语言自带的加密级随机源:
对公开可访问的令牌(例如出现在 URL 参数中的):
- 过期时间不超过 7 天。
- 防止令牌被第三方泄露或收录:用
noindexmeta 标签阻止搜索引擎收录,并在所有接受令牌的 URL 上返回Referrer-Policy响应头,值为origin-when-cross-origin或更推荐的no-referrer。
Shoplazza 签发的令牌
后端在信任 Session Token 前必须逐个校验——用 CLIENT_SECRET 检查 HS256 签名并拒绝过期令牌。完整步骤和代码见 Session Token 校验。
如果应用存储自有用户凭证,只能存加盐的密码哈希,绝不能存明文密码,做法参考 OWASP Password Storage Cheat Sheet。另外绝不能收集 Shoplazza 账号凭证——公开应用必须通过 OAuth 认证。
校验 Webhook 签名
任何知道你 webhook 地址的人都能向它发送伪造请求。处理任何 webhook 前,先校验 Shoplazza 附在每次推送上的 HMAC 签名,校验失败返回 401。算法和示例代码见签名验证。
应用的 shared secret 同理:它是这些消息的签名密钥,不能出现在客户端代码、公开仓库和日志里。
不暴露多余的网络服务
除非是应用正常运行必需的,避免把任何服务暴露到公网。MySQL、Redis、Memcached、Elasticsearch 这类服务绝不应从公网可达——把它们绑定到内网接口或放在防火墙之后。
应用审核时,Shoplazza 会用 Nmap 扫描应用主机的开放端口。如果发现预期外的端口,你需要解释端口上运行什么服务、为什么必须公开、做了哪些安全措施——或者直接关闭它们。