安全
你将把开票、住客资料和客户地址交给我们。本页说明哪些已经完成,并在最后列出还缺什么——因为第二份清单比第一份更能说明我们是什么样的公司。
隔离
大多数平台把所有客户放在同样的表中,靠一列来区分。只要有一条仓促写成、漏了过滤条件的查询,一位客户就能看到另一位客户的数据。在这里,这样的查询无处可查。
常见做法
WHERE tenant_id = ?
所有客户放在同样的表中。区分靠的是一个过滤条件,需要有人记得在每条查询中、永远都写上。只忘一次就足够出问题。
这里
USE ajja_tenant_…
每位客户都有自己的数据库。即使查询漏了过滤条件,返回的仍然只是属于你的内容,因为在查询存在之前,连接就已经指向了你这一侧。
ajja_tenant_<slug>
DROP DATABASE
你是谁 · 你订阅了什么 · 有哪些模块
账户与访问
这些检查每一项都在服务器上运行。没有一项取决于浏览器决定显示或隐藏什么。
使用你已有的应用进行双重验证。只有在你用该密钥生成的验证码确认通过之后,密钥才会生效,这样就不会有人因为二维码扫错而被锁在门外。你会获得 8 个恢复码,它们和密钥都在数据库中加密保存。
在输入正确的密码与手机上的验证码之间,有一张临时凭证,有效期五分钟。超过时间就从头开始——不会留下一个半途的登录等着被利用。
登录同时按邮箱和按地址设置上限:同一组合每分钟五次尝试,同一地址每分钟二十次。如果只统计邮箱,陌生人就可以故意失败,把你的账户锁住。双重验证、注册,以及公开的入住登记和预订表单同样设有上限。已认证的 API 限制为每位用户每分钟 120 次请求,而不是按地址计算——同一办公室的两位同事不会共用对方的额度。
隐藏菜单项并不是安全措施。每个地址都要经过两项独立的检查——该模块在此客户下已启用,以及你的角色可以看到该模块——即使有人手工输入地址,也会返回 403。
一个有效的令牌,如果指向另一位客户的名称,什么也读取不到。而且返回的是 404,而不是 403:试探客户名称的人无法由此得知哪些客户存在。
住客和业主通过带有专属密钥的链接进入,该链接只打开一项内容,并且可以设置有效期。没有可被盗用的账户,也没有可在别处重复使用的密码。
一次请求,从头到尾
这是你按下按钮到结果出现之间发生的事情。顺序在应用启动时就已固定,而不是取决于每条路由当初是怎么写的——因为一项来得太晚的检查,已经让请求开始工作了。
自有域名、子域名或请求头会指明请求属于哪位客户。名称未知,到此为止。
已暂停的工作区不提供服务,连读取也不行。
数据库连接切换为该客户自己的数据库。此后的一切,包括失误,都只能触及这一侧。
令牌会被验证。没有有效令牌,就没有第五步。
每分钟 120 次,按用户计算。超过后,服务器会表示到此为止。
令牌所属的用户和请求所属的客户必须一致。正是这一关联,阻止了一个合法的令牌去读取隔壁的“家”。
模块必须在该客户下已启用,并且对你的角色可见。两项检查,而不是一项。
只有通过这七步之后,地址中的数字才会变成记录——而且总是来自第三步所选定的数据库。
会记录什么
一个什么都不记录的系统,会迫使你只能相信我们的话。我们更希望你能自己核实。
操作人 · 操作 · 对象 · 前/后
重要的管理操作——暂停某位客户、修改某个套餐的价格、停用某张优惠券——会记录是谁做的、针对什么,以及操作前的值和操作后的值。这是每次使用时有意写入的:机器自行完成的事情,例如一笔付款自动确认,不是任何人的操作,不在这份清单之内。
tenant.impersonated
客服可以进入你的账户来解决问题。这会连同姓名和时间一起被记录,并且在此期间,屏幕顶部会有一条提示栏告诉你这件事正在发生。它不是静默进行的。
类型 · 内容 · 状态 · 错误
收到的每个付款事件都会保存其类型、内容、状态,以及失败时的错误信息。同一事件重复到达时会被识别,不会被计算两次。
不含请求正文
出现故障时,我们保存错误信息、文件、行号和出错的地址。请求正文不会被保存——排查错误不需要留下你本来要保存的那张发票。
数据属于你
一个让人难以离开的平台,等于承认它没有别的办法留住你。
一次请求,一个文件
无违约金
包含的内容
我们尚未具备的功能
其中没有任何一项是承诺过或写入合同的。列在这里,是为了让你知道今天买到的是什么。
导出已经完成。只删除某一个人的数据而保留其余,目前仍需手工处理——唯一的自动删除是针对整个客户的。