想法与洞见

探索 AI 优先开发、编码护栏和可处置架构。

你的依赖能读取环境变量。JavaScript允许它们这么做

Ambient Authority意味着Node.js进程中的任何代码都能触碰文件系统、网络和运行环境。本文将讲解如何通过lockdown、compartment和显式capability传递来剥除它。

一个被攻破的传递依赖就能外泄你的 、写入你的文件系统、打开出站连接。它不需要你的代码存在漏洞。它只需要存在于同一个进程里。在JavaScript中,每个模块默认继承运行时的全部权限。这就是Ambient Authority,而移除它是你能对Node.js应用做的最有效的安全升级之一。 Ambient…

网络邻近性不等于身份:服务如何在不依赖环境权威的情况下完成认证

大多数服务间信任来自网络位置,而非密码学证明。本文介绍能力令牌、mTLS 和 SPIFFE 如何让服务在不依赖环境权威的前提下完成认证,以及为何这做起来比想象中困难。

你的微服务共享同一个 VPC,因此彼此信任。这种信任就是环境权威:调用服务的权限不是由密码学证明授予的,而是由网络拓扑决定的。一旦攻击者突破边界,就会继承所有这些权限。 服务绝对可以在没有环境权威的情况下完成认证。更难回答的问题是:为什么你的平台让你觉得这是在背叛既有架构。…

你的依赖可以读取磁盘上的任何文件。cap-std 让它们先请求权限。

Rust 的标准库向每个依赖授予隐式的文件系统权限。cap-std 将其替换为基于权能的 API,强制代码在打开路径之前证明它有权访问该路径。

你依赖树中的任何一个 crate 都可以打开 、向你的 目录写入内容,或者枚举你项目中的每一个文件。Rust 的标准库不会请求许可。它假设任何能够调用 的代码都有权接触操作系统允许的任何路径。 cap-std 改变了这一假设。它是 Rust I/O module…

你的服务在不知不觉中成了帮凶:Confused Deputy 攻击是如何运作的

Confused deputy 攻击会诱骗一个拥有特权的服务,让它利用自己的权限攻击自己。以下是攻击者如何利用隐式信任发起攻击,以及为什么 capability-based security 才是解决方案。

你的服务持有一个云存储的 API key。用户发来一个请求,你的服务乖乖地把文件写了进去。文件落到了攻击者的 bucket 里,而不是你的,于是攻击者拿到了你的数据。你在不知不觉中成了帮凶。 这就是 confused deputy 攻击。攻击者自己无法向目标 bucket…

你的权限检查正在欺骗你

基于能力的安全模型用不可伪造的权限令牌取代零散的角色检查。以下是如何在 TypeScript 中实现它,同时不让代码变得难以阅读。

事后看来,每个权限漏洞都长得一样。调用栈深处的某个函数假设调用者已经检查了 。但实际上并没有。又或者新增了一个角色,然后你在 47 个文件里 grep ,祈祷自己没有漏掉任何一个。…

如果每个函数都必须获得许可,而不是默认拥有,会怎样?

大多数代码运行在 ambient authority 之下:每个函数都能触碰一切。Capability-based security 翻转了这一范式,强制函数只能接收显式、受限定范围的权限。

你的 函数可以访问数据库、支付网关和审计日志,仅仅因为它碰巧运行在同一个进程里。如果攻击者在某个 HTTP 处理程序中发现了注入漏洞,他就会继承这一切。这个函数从未主动请求这些权力,它只是凭借部署位置就默认拥有了它们。 这就是 ambient…

别再抛出你的类型检查器看不见的错误

抛出的异常会把失败路径从你的类型系统中隐藏起来。下面解释为什么显式返回错误能让代码更诚实,以及如何在不折磨自己的情况下采用这一模式。

你的函数签名说它返回一个 。事实并非如此。它返回一个 ,或者它会爆炸。类型系统根本不知道还有第二条分支。 这正是基于异常的错误处理最根本的不诚实之处。每一个…

Rust newtype 在编译期让非法状态无法表示,且零开销

一个单字段包装结构体就能在编译期捕获单位混淆和类型错用,不增加哪怕一个字节的开销。

把一个 用户 ID 传给期望 order ID 的函数,Rust 不会报错。两者都是 。编译器看到的是完全相同的类型,所以帮不了你。直到运行时你才会发现,通常是在生产环境,通常是在一次你以为安全的重构之后。 这正是 newtype 模式存在所要消灭的那类 bug。 newtype…

Switch 语句让 bug 逃过编译期检查

TypeScript 的 switch 语句会悄无声息地让你遗漏分支。下面介绍如何用穷尽性检查模式来替代它,让构建阶段而非运行时暴露错误。

你重构了一个 shape 类型,加了一个新变体,TypeScript 却一切正常。CI 通过了。部署也上线了。然后某个用户触发了一个返回 的运行时分支,你的应用在生产环境抛出了异常。 罪魁祸首几乎肯定是 语句。TypeScript 的 不提供穷尽性保证。给 union 增加一个新成员,所有基于该 union 的…

编译器应该捕获遗漏的状态分支,而不是你的 QA 团队

给状态机添加新状态很容易。记住更新每一个 switch 语句却不然。下面介绍如何让 TypeScript 在你遗忘某个 case 时拒绝编译。

状态机起初很干净。三个值,三个 switch 分支。然后重试逻辑添加了第四个状态。部分失败添加了第五个。你更新了 reducer,却漏掉了状态徽标组件、分析映射器和导出格式化器。 一切都能编译通过。两个冲刺周期后,当用户在你从未手动测试过的边界场景中遇到空白屏幕时,bug 才浮出水面。 TypeScript…