Serverless 函数的输入不再只来自一个 HTTP 请求。对象存储事件、消息服务、电子邮件、API 网关以及其他云服务,都可能把数据传给同一个函数。不同来源的协议、编码和字段结构并不一致,攻击者只要找到其中一个校验薄弱的入口,就可能把恶意内容带入后续处理流程,并进一步触发不安全依赖、过度权限或运行时数据泄露。

多源输入为什么会放大供应链风险
多源输入带来的问题,不只是“参数变多”这么简单。HTTP 请求通常有较明确的请求头、身份信息和参数位置,而来自消息服务、对象存储或其他函数的事件,可能使用完全不同的字段和编码方式。开发者如果只在入口函数中校验一种事件格式,其他路径就可能绕过原有的安全假设。
风险通常沿着下面这条路径扩散:外部事件进入函数后,未经充分验证的数据被解析、拼接或传递给依赖库;依赖库、构建组件或运行时扩展再处理这些数据;函数本身拥有的云资源权限,最终决定攻击者能够读取、修改或调用哪些资源。供应链攻击不一定直接修改业务代码,也可能通过被污染的开源组件、构建产物或第三方接入组件,在函数执行时获得影响业务的机会。
例如,文件名、对象路径、消息字段或请求参数如果被直接拼接到命令、查询语句或文件操作中,可能造成注入风险。即使业务代码没有明显漏洞,未经身份校验的事件源也可能伪造事件,诱导函数处理攻击者准备的内容。仅依靠传统的网络边界或单一入口防护,无法覆盖这些非 HTTP 数据源。
因此,防护重点应从“函数能否被调用”进一步延伸到三个问题:输入是否属于预期来源,依赖和构建产物是否可信,函数被攻破后权限和运行范围是否受控。
方案一:在交付链路建立输入与依赖准入门槛
第一套方案适合希望在现有 Serverless 发布流程中快速落地的团队,核心是把不可信输入和不可信依赖挡在函数部署之前。
为每类事件建立独立的输入契约
不要把所有来源的数据都转换成一个宽松的通用对象,再交给业务逻辑处理。应当按照来源分别定义允许的事件类型、字段结构、数据类型和处理边界。HTTP 请求、对象存储通知、消息事件和函数间调用,都应有各自的校验逻辑。
校验顺序也很重要。函数首先应确认事件来源和事件类型,再检查必要字段、字段类型和数据长度,最后才进入业务处理。对于文件名、对象路径、外部 URL、命令参数和查询条件等可能影响解释器或系统操作的字段,应采用允许列表或结构化参数传递,避免把原始输入直接拼接到命令和表达式中。
输入验证不能只做格式判断。还要检查事件是否重复、是否超出业务允许的时间范围,以及关联资源是否确实存在于预期范围内。对于来自其他云服务的事件,应验证其身份或来源凭据;无法确认来源的消息,不应直接触发高权限操作。
可以将入口处理拆成几个明确阶段:
- 识别事件来源,拒绝未知事件类型。
- 验证事件身份、必要字段和数据结构。
- 对路径、文件名、标识符等高风险字段执行规范化和允许列表校验。
- 将验证后的数据转换为内部对象,禁止业务代码继续使用未经验证的原始事件。
- 对失败事件进行隔离和记录,避免反复重试放大资源消耗。
这种做法的价值在于,每个数据源都有清晰的信任边界。即使某个事件源的格式发生变化,也不会悄然绕过所有函数共用的宽松解析逻辑。
对依赖和构建产物执行签名校验
依赖签名的重点不是“给依赖文件加一个标记”,而是让发布流程能够确认依赖来自预期来源,并且在下载、构建和部署过程中没有被替换。
团队应先确定可信的依赖来源和发布主体,再对依赖清单、构建产物及其元数据进行签名。部署前验证签名和完整性;验证失败时停止发布,而不是带着警告继续部署。对于无法确认来源、临时下载或未经审查的组件,应进入人工评估流程,不能直接打入函数包或镜像。
依赖管理还需要关注“间接依赖”。函数代码直接引用的库并不是完整的供应链,底层组件、构建插件和运行时扩展同样可能进入最终产物。每次依赖变更都应记录变更内容、来源和审核结果,并保留能够追溯到具体构建过程的产物信息。
签名校验应放在发布链路的强制位置,而不是只作为开发者本地的检查。一个可执行的流程是:依赖解析完成后生成清单,构建阶段固定清单并生成产物,发布阶段验证依赖和产物签名,运行前再次确认部署对象与已审核版本一致。这样可以减少“源码看起来没变,但部署包已经被替换”的风险。
用最小权限限制供应链攻击后的影响
输入验证和依赖签名解决的是“什么能够进入函数”,最小权限解决的是“函数被利用后还能做什么”。
每个函数都应根据实际业务动作单独分配权限。例如,只负责读取对象的函数不应同时拥有删除和写入权限;只处理消息的函数不应默认访问全部数据存储;用于转换文件的函数,也不应拥有修改账号权限或调用无关管理接口的能力。
权限设计应以资源和动作拆分,而不是以“开发方便”为理由授予较大的通用角色。部署身份、构建身份和运行身份也应分开,避免构建流程被入侵后直接获得生产数据访问权。对于跨函数调用,应明确调用方、被调用方和允许的操作范围,不能因为函数都属于同一项目就默认互信。
权限检查完成后,还要回看函数的实际代码路径:输入中哪些字段会影响资源标识,哪些依赖会发起外部请求,哪些异常处理会把敏感数据写入日志。只要权限覆盖了这些路径之外的资源,就应继续收缩授权范围。
方案二:把镜像扫描和运行时监控接入发布与处置流程
如果函数使用容器镜像或包含较多运行时依赖,第二套方案应重点覆盖镜像进入生产前的检查,以及函数运行期间的异常行为发现。它不能替代输入验证和权限控制,但可以在供应链组件漏检时提供第二道防线。

容器镜像扫描的落地步骤
镜像扫描不应只在镜像构建完成后临时执行一次。更可靠的方式是把扫描放入构建和发布两个阶段,并让结果真正影响部署决策。
首先,对基础镜像、应用依赖、运行时扩展和最终生成的镜像层进行检查,确认其中是否存在已知漏洞、异常组件、未经批准的来源或不必要的系统工具。扫描范围不能只覆盖业务代码目录,因为供应链风险可能藏在基础层和间接依赖中。
其次,为风险结果建立处置规则。需要立即阻断发布的情况包括:依赖签名无法验证、镜像来源不在可信范围、发现与函数业务无关的高风险组件,或镜像中存在无法解释的新增文件。对于暂时无法修复的风险,应记录例外原因、影响范围和补偿控制,不能用“扫描已通过”掩盖未处理的问题。
再次,对生产中使用的镜像保留版本和构建记录。部署前核对镜像摘要或等价的完整性标识,确保运行对象就是经过扫描和审核的产物。镜像重新构建、基础层变化或依赖升级后,应重新扫描,不能因为函数代码没有变化就跳过检查。
最后,清理镜像中的非必要内容。调试文件、临时脚本、测试凭据和不参与业务执行的工具,都会增加攻击者利用供应链缺陷后的可操作空间。镜像越接近实际运行所需的最小内容,后续运行时监控也越容易识别异常行为。
函数运行时监控应关注什么
运行时监控的目标不是收集越多日志越好,而是观察函数是否偏离了它应有的行为模型。对每个函数至少应建立以下基线:
- 正常调用来源和事件类型;
- 常规访问的云资源及操作范围;
- 正常的外部网络访问对象;
- 依赖加载、文件写入和进程行为;
- 错误、重试和调用量的变化趋势。
当函数突然访问未授权资源、连接不熟悉的外部地址、加载未出现在构建清单中的组件,或出现异常的文件写入和进程行为时,应触发告警并关联到具体版本和部署记录。对于多源输入函数,还要把事件来源、校验结果和处理链路放在同一条审计记录中,否则发生问题时很难判断是哪个入口把恶意数据带了进来。
日志本身也可能成为泄露渠道。不要记录完整令牌、密钥、个人敏感数据或未经处理的原始事件内容。调试信息在开发阶段有用,但进入生产环境前应清理;必要时只记录字段名称、校验结果、事件标识和处置状态,既保留排查线索,又避免把攻击者输入和敏感数据原样写入日志。
让告警能够触发实际处置
监控只有连接处置动作,才能形成防线。对确认异常的函数版本,可以先停止继续发布,再撤回到最近的可信版本;对疑似被污染的依赖或镜像,应冻结其继续流转,并保留副本供审查。若异常涉及凭据或跨服务权限,还应立即收缩相关权限并更换受影响的访问凭据。
处置过程中不要只删除一个函数实例。Serverless 函数可能由多个事件源触发,攻击者也可能通过其他入口重复提交相同数据。应同时检查事件源身份、依赖清单、镜像版本、运行身份权限和相关日志,确认没有其他函数使用同一问题组件或过宽权限。
发布前后的最小检查清单
在现有 Serverless 平台上搭建防线,可以先从一组不依赖具体厂商名称的检查开始:
- 每个事件源是否有独立的来源验证和数据结构校验;
- 未知事件类型、异常字段和超出边界的数据是否会被拒绝;
- 高风险字段是否经过规范化,并避免直接拼接到命令、查询和路径操作中;
- 直接依赖、间接依赖、基础镜像和运行时扩展是否都能追溯来源;
- 依赖和构建产物签名验证失败时,发布流程是否会停止;
- 函数运行身份是否只拥有完成当前任务所需的资源权限;
- 镜像扫描结果是否会影响发布,而不是只生成无人处理的报告;
- 运行时是否记录事件来源、函数版本、资源访问和异常行为;
- 日志中是否可能出现令牌、密钥、敏感数据或未经处理的原始输入;
- 发生异常时,团队是否能撤回版本、收缩权限并定位受影响的数据源。
多源输入的核心风险,不在于事件来源数量本身,而在于不同来源是否被错误地当成同一种可信数据处理。把输入验证放在每个入口,把依赖签名和镜像扫描放进发布闸门,再用最小权限和运行时监控限制失陷后的影响范围,才能让供应链防护从代码检查延伸到函数实际执行的全过程。

上海市青浦区 1F
多源输入这点确实容易被忽视,只防 HTTP 不够用了。