API 变更与安全性的权衡 (2016)
API 变更与安全性的权衡 2016年2月17日
TLDR:大量的客户端逻辑需要在 API 变更和日益复杂的安全模型之间进行权衡。
问题
Jean-Jacques Dubray 最近发表的一篇文章 Why I No Longer Use MVC Frameworks 在 HackerNews 上引发了一场漫长而有趣的讨论,这场讨论让我意识到当前 Web 应用程序中大量客户端逻辑的趋势存在一个根本问题。
以下是 Jean-Jacques 在文章中提出的问题:
这些天我工作中遇到的最糟糕的事情就是为前端开发人员设计 API。对话总是不可避免地变成这样:
开发人员 – “这个屏幕有数据元素 x、y、z……你能创建一个 API,响应格式为 {x: , y:, z: } 吗?”
我 – “好吧”
我甚至不再争论了。项目最终会产生大量的 API,这些 API 与经常变化的屏幕相关联,而这些变化“设计上”需要在 API 中进行更改,然后你就会发现最终有很多 API,并且每个 API 都有许多形式因素和平台变体。
总结一下:如果你正在为前端设计网络 API 端点,你最终会调整和修改 API 以支持你的 UI 需求,这通常是临时的和混乱的。通过让本质上不断变化和“棘手”(即 UI)的东西来决定 API 的形状,你最终会不断调整它,试图跟上变化。
在本文的其余部分,我将把这个问题称为 API 变更。
解决方案
如果你致力于客户端,那么解决 API 变更问题的方法是增加客户端可用的 API 的表现力。好吧,那是什么意思?
这意味着你必须开始在客户端公开越来越多的通用数据访问和修改功能。 你可以在 GraphQL 中看到这一点,它用更少但更具表现力的端点取代了多个 REST 风格和临时的 API 端点。 这可以被认为是朝着类似 SQL(或你的数据存储的自然查询/修改语言)的方向发展。
通过增加端点的表现力,你作为 API 设计者,不再需要担心获得完全正确的 API。 相反,前端开发人员可以控制如何以及返回什么,或者修改什么,并且你的 API 保持稳定,因为 UI 需求会发生变化。
听起来很棒,对吧? 对。
但是等等……
解决方案的问题
这些越来越具有表现力的端点的问题在于,你不仅将它们交给了你的前端开发人员,还交给了潜在的恶意用户。 浏览器是我能想象到的最不安全的计算环境,你的前端开发人员可以做的任何事情,恶意用户也可以做。
考虑以下简单的 GraphQL 查询:
{
employee(id: 3500401) {
id,
name
}
}
任何用户发出此查询以查看给定员工的姓名都是完全合理的,并且你的 UI 开发人员可能会编写完全相同的代码。
但是,你给开发人员的东西,你也给了你的用户。 那么,如果一个恶意用户发现了 API(毕竟,检查 HTTP 请求并不是什么高科技),并修改查询为:
{
employee(id: 3500401) {
id,
name,
salary
}
}
哎呀。 你最好不要向他们展示这些信息!
现在,在这种情况下,唯一的解决方案是上下文相关的 字段级别 安全性。 在处理查询时,你必须知道是谁在提问,以及他们到底在要求什么,并且你必须在域模型中为每个字段维护该安全信息。
我的朋友们,这很复杂。
当我在评论中提出安全问题时,Peter Hunt 这样说:
它不属于规范,它属于实现。 但是,是的,参考实现 (graphql-js) 应该更新以演示访问控制。
当我读到这句话时,我真的笑了出来:这是一个重大问题,任何认为增加客户端表现力是解决 API 变更问题的The Answer™ 的人都需要对此有一个非常好的答案!
信任
再次强调,核心问题是,在将更具表现力的工具交给客户端 UI 开发人员的同时,你也在无意中让它们落入对抗性用户的手中。 因此,在你给开发人员多少东西以及这种权力最终会带来多大的安全隐患之间存在着根本的紧张关系。
在一个理想的世界中,你将为你的 UI 开发人员提供他们可能需要的一切,以高效地构建他们的 UI:一个开放且具有表现力的查询层,让他们可以调整查询的结构和返回数据,以便那些经常主导系统性能的热门、复杂的查询。
但是,如果我告诉你存在一个你可以做到这一点的地方呢?
这样的地方确实存在。
这个地方叫做……服务器端。
你看,在服务器端,代码是受信任的。 你可以为你的开发人员提供一个完全开放和灵活的数据访问和更新 API,因为你(在第一个近似值中)信任他们。 例如,给予他们结构化查询语言的强大功能是完全可以接受的,实际上,甚至没有争议,因为你 也 没有将该功能提供给最终用户。
解决方案的问题的解决方案
因此,如果你想完全避免 API 变更与安全复杂性的权衡,那么有一种很好的方法可以做到这一点:将事情移回服务器端。 当然,在不牺牲现代 Web 可用性的情况下执行此操作的一种方法是使用 intercooler.js 并在受信任的环境中在服务器上执行 HTML 渲染和域逻辑。
你还将从这种方法中获得许多其他好处:HATEOS without tears、你可能已经有近十年经验的编程模型等等。 也许最重要的是:它很简单,而且由于它很简单,你更有可能正确地完成困难的事情,例如安全性。
但即使你不购买我所销售的东西(免费的,因为我爱你),没关系:你仍然应该意识到,每次你增加提供给客户端开发人员的表现力时,都需要非常认真地思考。 某个 不 爱你的人也获得了该功能。
有备才能无患。