首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Serverless 真不是“免运维”:函数隔离、依赖漏洞和权限坑,一个都不能少

Serverless 真不是“免运维”:函数隔离、依赖漏洞和权限坑,一个都不能少

原创
作者头像
Echo_Wish
发布2026-08-14 08:10:37
发布2026-08-14 08:10:37
1050
举报

Serverless 真不是“免运维”:函数隔离、依赖漏洞和权限坑,一个都不能少

作者:Echo_Wish

很多人第一次接触 Serverless,会有一种特别舒服的感觉:

“代码丢上去就能跑,不用买服务器,不用维护操作系统,也不用半夜起来处理机器宕机。”

听起来确实很香。

但问题也恰恰出在这里。

Serverless 不是没有安全问题,而是安全问题换了个地方。

以前我们做安全,天天盯着服务器、容器、端口、防火墙;到了 Serverless,攻击面开始往函数代码、第三方依赖、身份权限、事件入口这些地方转移。

尤其是下面三个坑,非常容易被忽略:

函数隔离不等于绝对安全;依赖安装不等于依赖可信;函数能调用资源不等于应该拥有全部权限。

这篇文章,我们就聊聊 Serverless 安全里最容易踩的三个坑,以及到底应该怎么做。


一、先别把 Serverless 想得太美:函数也可能成为攻击入口

传统应用一般是:

代码语言:text
复制
用户
 ↓
Nginx
 ↓
Web服务
 ↓
数据库

Serverless 则可能变成:

代码语言:text
复制
用户
 ↓
API Gateway
 ↓
Function A
 ↓
Function B
 ↓
数据库 / OSS / MQ / Redis

看起来组件少了,实际上调用链可能更长。

例如一个订单查询函数:

代码语言:python
复制
def get_order(event):
    order_id = event["order_id"]

    return db.query(
        f"SELECT * FROM orders WHERE id = {order_id}"
    )

很多人看到这里第一反应:

“函数这么短,应该没什么安全问题吧?”

恰恰相反。

这里直接就可能存在 SQL 注入。

攻击者传入:

代码语言:text
复制
1 OR 1=1

最后拼出来:

代码语言:sql
复制
SELECT * FROM orders WHERE id = 1 OR 1=1

Serverless 本身并不会自动帮你解决业务代码里的漏洞。

所以我的一个观点是:

Serverless 解决的是“服务器怎么运维”,不是“代码怎么安全”。

这两个事情一定要分开看。


二、函数隔离:别以为“一个函数一个容器”就万事大吉

Serverless 最大的安全基础之一,就是函数隔离

一般来说,云平台会通过容器、沙箱、运行时等机制,让不同函数实例之间尽量隔离。

比如:

代码语言:text
复制
Function A
┌──────────────┐
│ Runtime      │
│ Code         │
│ Memory       │
└──────────────┘

Function B
┌──────────────┐
│ Runtime      │
│ Code         │
│ Memory       │
└──────────────┘

理论上:

代码语言:text
复制
A 不能直接访问 B 的内存
A 不能直接读取 B 的代码
A 不能直接访问 B 的临时文件

但是,这里有一个非常容易被忽略的问题:

隔离 ≠ 安全。

因为真正危险的东西,往往不是函数之间直接“串门”,而是函数本身拥有的资源访问能力。

例如:

代码语言:text
复制
攻击者
   ↓
订单查询函数
   ↓
函数身份
   ↓
OSS
   ↓
所有业务文件

假设这个函数只是负责:

代码语言:text
复制
查询订单

结果你给它的身份权限是:

代码语言:text
复制
OSS:
  读
  写
  删除
  全部 Bucket

那这个函数一旦被攻破,攻击者拿到的就不只是“查询订单”的能力。

而是:

这个函数身份能够访问的所有资源。

这才是 Serverless 安全里非常核心的问题。


三、最小权限:函数需要什么,就给什么

如果只能给 Serverless 安全总结一句话,我会选择:

函数需要什么权限,就给什么权限,不需要的权限一个都别给。

这就是最小权限原则。

比如我们有一个图片处理函数:

代码语言:text
复制
Upload Image
      ↓
Image Function
      ↓
读取 OSS
      ↓
压缩图片
      ↓
写入 OSS

那么它可能只需要:

代码语言:text
复制
bucket:image-source
    GetObject

bucket:image-result
    PutObject

而不是:

代码语言:text
复制
OSS:
    *

更不要直接:

代码语言:text
复制
Resource:
    *
Action:
    *

因为这相当于告诉云平台:

“这个函数什么都能干。”

一旦函数出现漏洞,攻击者就会非常开心。


四、权限不是越大越方便,而是越大越危险

很多开发人员有一个很典型的习惯:

接口报:

代码语言:text
复制
AccessDenied

怎么办?

加权限。

又报:

代码语言:text
复制
AccessDenied

继续加。

最后:

代码语言:json
复制
{
  "Action": "*",
  "Resource": "*"
}

好了。

系统不报错了。

开发人员:

“搞定!”

安全人员:

“你这不是搞定,你这是把门拆了。”

😂

这其实是企业里非常常见的权限管理问题。

正确的思路应该是:

代码语言:text
复制
函数
 ↓
明确业务动作
 ↓
明确资源
 ↓
明确操作
 ↓
配置最小权限

例如:

代码语言:json
复制
{
  "Effect": "Allow",
  "Action": [
    "oss:GetObject"
  ],
  "Resource": [
    "arn:cloud:oss:::order-images/*"
  ]
}

而不是:

代码语言:json
复制
{
  "Effect": "Allow",
  "Action": "*",
  "Resource": "*"
}

前者是:

“我允许你拿订单图片。”

后者是:

“你想干什么随便。”

两者安全级别完全不是一个概念。


五、真正容易被忽略的,是第三方依赖

Serverless 还有一个非常现实的问题:

依赖。

我们写 Python:

代码语言:python
复制
import requests
import pandas
import numpy

Node.js:

代码语言:javascript
复制
const express = require("express");
const axios = require("axios");

Java:

代码语言:xml
复制
<dependency>
    <groupId>xxx</groupId>
    <artifactId>xxx</artifactId>
</dependency>

开发的时候非常爽。

一句:

代码语言:bash
复制
pip install xxx

或者:

代码语言:bash
复制
npm install xxx

依赖就进来了。

但问题来了:

你真的知道这个依赖里面有什么吗?


六、依赖风险,有时候比业务代码漏洞更麻烦

假设我们的项目:

代码语言:text
复制
order-function
├── app.py
├── requirements.txt
└── third-party packages

requirements:

代码语言:text
复制
requests
pandas
numpy
xxx-package

表面上只有几个依赖。

实际上可能是:

代码语言:text
复制
你的代码
 ↓
requests
 ↓
urllib3
 ↓
其他依赖
 ↓
其他依赖

这就是所谓的依赖链

你真正部署进去的东西,可能远远超过你写的那几百行代码。

所以 Serverless 的供应链安全非常重要。


七、别只扫描自己的代码,也要扫描依赖

例如 Python 项目,可以在 CI/CD 中加入依赖检查:

代码语言:bash
复制
pip-audit

Node.js:

代码语言:bash
复制
npm audit

也可以使用 SCA 工具对整个依赖树进行扫描。

一个比较合理的 CI 流程应该是:

代码语言:text
复制
开发
 ↓
代码提交
 ↓
SAST
 ↓
依赖扫描
 ↓
Secrets扫描
 ↓
镜像/构建产物扫描
 ↓
安全门禁
 ↓
部署

而不是:

代码语言:text
复制
开发
 ↓
git push
 ↓
直接上线

后面再发现:

代码语言:text
复制
“卧槽,这个依赖有高危漏洞。”

那就晚了。


八、依赖版本不要写得太随意

例如:

代码语言:text
复制
requests

这种写法太宽松。

今天安装一个版本:

代码语言:text
复制
2.x

过几天重新构建:

代码语言:text
复制
另一个版本

最终:

代码语言:text
复制
开发环境 ≠ 测试环境 ≠ 生产环境

更合理的方式是锁定版本。

例如:

代码语言:text
复制
requests==2.32.4

或者使用 lock 文件:

代码语言:text
复制
package-lock.json
poetry.lock
uv.lock

这样至少可以保证:

今天构建出来的东西,和明天构建出来的东西尽可能一致。

这对于 Serverless 特别重要。

因为 Serverless 经常会:

代码语言:text
复制
代码修改
 ↓
重新构建
 ↓
重新发布

构建频率越高,依赖漂移的风险就越明显。


九、Secrets千万别直接写进函数代码

这个坑我见过太多次。

比如:

代码语言:python
复制
DB_HOST = "10.10.10.20"
DB_USER = "admin"
DB_PASSWORD = "123456"

然后:

代码语言:python
复制
conn = connect(
    DB_HOST,
    DB_USER,
    DB_PASSWORD
)

开发环境觉得:

“反正代码在公司内网。”

问题是:

Serverless 的代码包、日志、Git仓库、构建系统、开发人员电脑,任何一个环节泄露,都可能导致凭证泄露。

更合理的是使用 Secret Manager:

代码语言:python
复制
db_password = get_secret("prod/order/db/password")

函数只拿到:

代码语言:text
复制
运行时需要的 Secret

而不是:

代码语言:text
复制
所有环境的密码

这其实又回到了一个核心思想:

权限最小化,不只是 IAM 权限最小化,凭证也应该最小化。


十、环境变量也不是“保险箱”

很多人会说:

代码语言:text
复制
DB_PASSWORD = xxxxx

放环境变量里不就行了?

比写死在代码里好,但千万不要把环境变量当成专业的密钥管理系统。

例如:

代码语言:python
复制
print(os.environ)

这一下就可能把一堆敏感配置打进日志。

尤其是 Serverless。

函数执行频率可能非常高:

代码语言:text
复制
1分钟
 ↓
1000次调用
 ↓
日志
 ↓
日志平台
 ↓
长期保存

一次错误日志,很可能变成长期安全事故。

所以生产环境应该尽量使用:

代码语言:text
复制
Secret Manager
KMS
密钥轮换
短期凭证

而不是:

代码语言:text
复制
代码里写密码

或者:

代码语言:text
复制
日志里打印密码

十一、Serverless 最容易犯的另一个错误:函数“万能化”

有些团队喜欢设计一个超级函数:

代码语言:text
复制
CommonFunction

然后:

代码语言:text
复制
查询订单
创建订单
删除订单
操作库存
发送消息
修改用户
导出数据

全部塞进去。

看起来:

“复用率很高。”

实际上安全边界已经糊了。

一个函数承担的业务越多,它需要的权限就越多。

最后可能变成:

代码语言:text
复制
Function
 ├── DB Read
 ├── DB Write
 ├── OSS Read
 ├── OSS Write
 ├── MQ Send
 ├── MQ Consume
 ├── Redis
 └── Admin API

这时候只要函数出现一个漏洞:

代码语言:text
复制
漏洞
 ↓
万能函数
 ↓
万能权限
 ↓
整个系统遭殃

所以我更推荐:

函数小一点,职责单一点,权限窄一点。

例如:

代码语言:text
复制
OrderQueryFunction
        ↓
DB Read

OrderCreateFunction
        ↓
DB Write

ImageProcessFunction
        ↓
OSS Read/Write

NotificationFunction
        ↓
MQ Send

这样即使某个函数被打穿:

代码语言:text
复制
ImageProcessFunction
        ↓
只能访问图片

而不是:

代码语言:text
复制
ImageProcessFunction
        ↓
整个生产系统

十二、真正靠谱的 Serverless 安全模型

如果让我设计一套企业级 Serverless 安全体系,我不会只盯着函数代码。

我会把它拆成五层。

代码语言:text
复制
┌──────────────────────────┐
│       API / Event        │
│   身份认证 + 限流 + WAF   │
├──────────────────────────┤
│         Function         │
│   隔离 + 输入校验 + 防注入 │
├──────────────────────────┤
│        Dependency        │
│   SCA + Lock + 漏洞扫描   │
├──────────────────────────┤
│        IAM / Secret      │
│   最小权限 + 密钥管理      │
├──────────────────────────┤
│       Data / Infra       │
│   加密 + 审计 + 监控       │
└──────────────────────────┘

然后再配合:

代码语言:text
复制
代码扫描
+
依赖扫描
+
Secret扫描
+
权限审计
+
运行时监控
+
日志审计

形成完整闭环。


十三、最后再聊一个容易被忽略的问题:安全监控

Serverless 有个天然特点:

函数生命周期短。

传统服务器:

代码语言:text
复制
服务器
 └── 跑30天

Serverless:

代码语言:text
复制
实例启动
 ↓
执行
 ↓
结束

所以传统那种:

代码语言:text
复制
登录服务器
top
ps
netstat

在 Serverless 世界里就不太适用了。

你需要更多依赖:

代码语言:text
复制
调用日志
审计日志
Tracing
Metrics
Security Event

比如突然发现:

代码语言:text
复制
正常:
API调用 1000次/分钟

异常:
API调用 80000次/分钟

或者:

代码语言:text
复制
正常:
Function → OSS:GetObject

异常:
Function → OSS:DeleteObject
Function → IAM
Function → UserAdmin

这时候就应该触发安全告警。

所以:

Serverless 不是不需要运维,而是从“盯服务器”变成了“盯行为”。


十四、写在最后:Serverless真正危险的不是函数,而是“默认信任”

Serverless 很容易让我们产生一种错觉:

“云厂商都帮我隔离好了,安全应该不用太操心。”

这是非常危险的想法。

云厂商负责的是:

代码语言:text
复制
基础设施安全
运行环境安全
平台安全

但你的:

代码语言:text
复制
代码
依赖
权限
密钥
业务逻辑
数据

依然需要自己负责。

我自己比较认同一句话:

Serverless 不是把安全问题消灭了,而是把安全边界从“服务器”移动到了“函数”。

所以真正成熟的 Serverless 安全,不是把所有权限都关掉,也不是给函数套一堆安全产品。

而是做好三件最朴素的事情:

第一,函数要隔离

一个函数干一件事。

代码语言:text
复制
查询归查询
写入归写入
图片处理归图片处理

不要搞一个“超级函数”。

第二,依赖要管住

代码语言:text
复制
固定版本
+
依赖扫描
+
漏洞修复
+
构建可追溯

别觉得:

“这是开源库,应该没问题。”

第三,权限要收紧

函数需要:

代码语言:text
复制
OSS Read

就给:

代码语言:text
复制
OSS Read

不要顺手给:

代码语言:text
复制
OSS *

更不要:

代码语言:text
复制
*
*

因为安全这件事情,最怕的不是没有防护。

最怕的是“为了方便,什么都允许”。

Serverless 可以让我们少维护很多服务器,但它绝对不会让我们少思考安全。

恰恰相反——

服务器越少,函数越多;基础设施越“无感”,权限、依赖和代码安全就越应该被我们认真对待。

这才是 Serverless 真正成熟之后,运维人员应该面对的问题。

我是 Echo_Wish,我们下篇继续聊 Serverless。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • Serverless 真不是“免运维”:函数隔离、依赖漏洞和权限坑,一个都不能少
    • 一、先别把 Serverless 想得太美:函数也可能成为攻击入口
  • 二、函数隔离:别以为“一个函数一个容器”就万事大吉
  • 三、最小权限:函数需要什么,就给什么
  • 四、权限不是越大越方便,而是越大越危险
  • 五、真正容易被忽略的,是第三方依赖
  • 六、依赖风险,有时候比业务代码漏洞更麻烦
  • 七、别只扫描自己的代码,也要扫描依赖
  • 八、依赖版本不要写得太随意
  • 九、Secrets千万别直接写进函数代码
  • 十、环境变量也不是“保险箱”
  • 十一、Serverless 最容易犯的另一个错误:函数“万能化”
  • 十二、真正靠谱的 Serverless 安全模型
  • 十三、最后再聊一个容易被忽略的问题:安全监控
  • 十四、写在最后:Serverless真正危险的不是函数,而是“默认信任”
    • 第一,函数要隔离
    • 第二,依赖要管住
    • 第三,权限要收紧
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档