摘要:AWS Lambda等无服务器(Serverless)技术,将开发者从繁琐的服务器运维中解放出来,实现了极致的弹性和成本效益。然而,这种“无服务器”的抽象,并非“无安全”。它将安全战场从传统的网络边界,转移到了以函数、事件和权限为核心的“微观”层面。本文将深入剖析Lambda面临的四大核心攻击向量,包括过度授权的IAM角色、事件注入、不安全的代码与依赖,并通过详尽的代码示例和防御策略,为您构建一套能够抵御现代云原生攻击的、可落地的纵深防御“铠甲”。

关键词:AWS Lambda, Serverless安全, 云安全, IAM, 事件注入, 纵深防御, 安全编码


引言:当“服务器”消失之后

Serverless的承诺是诱人的:你只需编写核心业务逻辑(函数),然后将其上传到云端。云服务商(如AWS)会负责处理所有底层的服务器、操作系统、补丁和伸缩。这听起来像是一个安全乌托邦——没有服务器,就没有服务器漏洞。

这是一个危险的误解。安全责任并没有消失,它只是发生了“转移”。在Serverless的世界里,安全不再是关于加固一台台服务器,而是关于保护:

  • 每一个函数的执行权限 (IAM Role)。

  • 每一次事件触发的数据 (Event Payload)。

  • 每一行函数自身的代码及其依赖。

第一章:Lambda的“隐形”攻击面

1. 过度授权的IAM角色 (Over-privileged IAM Roles) —— “万能钥匙”

  • 核心风险: 这是Lambda安全中最常见、最致命的“原罪”。 为了图方便,开发者常常为一个Lambda函数,附加一个拥有过多权限(如s3:*, dynamodb:*)的IAM角色。

  • 后果: 一旦这个函数自身的代码存在任何一个微小的漏洞,攻击者就能利用这个漏洞,继承并滥用其背后那个强大的IAM角色,从而在你的AWS账户中“横行霸道”。这个函数就成了攻击者进入你云环境的“特洛伊木马”。

2. 事件注入与不安全的输入处理 (Event Injection & Unsafe Input Handling)

  • 核心风险: 盲目信任来自事件源的数据。 Lambda函数可以被多种事件触发(API Gateway的HTTP请求、S3的文件上传、SQS的消息等)。这些事件的载荷(Payload)完全可被外部控制,必须被视为不可信的输入。

  • 高层次概念(对应您之前询问的ZIP文件风险):

    • 场景: 一个Lambda函数被设置为在S3存储桶有新ZIP文件上传时自动触发。函数的逻辑是解压这个ZIP包,并处理其中的文件。

    • 脆弱的设计: 如果函数的代码不加验证地使用ZIP包内的文件名来构建一个文件路径,并将其传递给一个Shell命令(如os.system(f"process_file.sh {filename}")),就会产生命令注入

    • 攻击者如何利用? 攻击者可以精心构造一个ZIP文件,其中包含一个文件名叫dummy.txt; /bin/bash -c '...'的文件。当脆弱的代码解压并拼接这个文件名时,恶意命令就被执行了。

    • 利用/tmp目录: Lambda执行环境为每个函数提供了一个可写的/tmp目录(512MB)。攻击者可以利用这个空间,先将恶意脚本或二进制文件写入,然后再通过注入的命令来执行它。

3. 不安全的函数代码与依赖项

  • 风险: Lambda函数自身的代码,与传统Web应用一样,也可能存在SQL注入、XSS(如果函数返回HTML)、反序列化等经典漏洞。同时,其引入的第三方开源库(供应链风险)也可能包含已知漏洞。


第二章:纵深防御的“云原生”实践

2.1 为函数戴上“镣铐”——最小权限IAM角色

黄金法则:为每一个Lambda函数,创建一个独立的、专用的、权限最小化的IAM角色。

  • 危险的IAM策略(过度授权):

    JSON

    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Action": "s3:*", // 允许对S3做任何事
                "Resource": "*"   // 允许对所有S3存储桶
            }
        ]
    }
    
  • 安全的IAM策略(最小权限): 假设函数只需要从my-app-uploads这个桶中,读取用户上传的图片。

    JSON

    // SECURE IAM POLICY
    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Action": "s3:GetObject", // 只允许读取对象
                "Resource": "arn:aws:s3:::my-app-uploads/users/*" // 只允许访问特定路径
            }
        ]
    }
    

效果: 即使这个函数被RCE,攻击者获得的权限也仅限于“读取用户图片”,而无法列出所有存储桶、删除数据或修改权限。

2.2 “净化”输入——安全处理S3事件的代码实践

  • 脆弱的Python代码(处理ZIP文件):

    Python

    # VULNERABLE CODE
    import os
    import boto3
    
    s3 = boto3.client('s3')
    
    def lambda_handler(event, context):
        bucket = event['Records'][0]['s3']['bucket']['name']
        key = event['Records'][0]['s3']['object']['key']
    
        # 下载ZIP文件到可写的/tmp目录
        s3.download_file(bucket, key, '/tmp/archive.zip')
    
        # 致命缺陷:直接将解压操作交给Shell,且文件名可被控制
        # 如果key是 "a.zip; id", 命令注入就发生了
        os.system(f"unzip /tmp/archive.zip -d /tmp/output")
        return {'status': 'ok'}
    
  • 安全的代码(白名单验证 + 安全API):

    Python

    # SECURE CODE
    import boto3
    import zipfile
    import os
    
    s3 = boto3.client('s3')
    
    def lambda_handler(event, context):
        bucket = event['Records'][0]['s3']['bucket']['name']
        key = event['Records'][0]['s3']['object']['key']
    
        # 防御1:对输入进行白名单验证
        if not key.endswith('.zip'):
            raise ValueError("Invalid file type.")
    
        download_path = '/tmp/archive.zip'
        s3.download_file(bucket, key, download_path)
    
        # 防御2:使用安全的、不调用Shell的库来处理文件
        with zipfile.ZipFile(download_path, 'r') as zip_ref:
            for member in zip_ref.infolist():
                # 防御3:绝不信任压缩包内的文件名!
                # 使用os.path.basename来剥离任何目录遍历尝试
                final_filename = os.path.basename(member.filename)
                if not final_filename:
                    continue # 如果是目录,则跳过
    
                target_path = os.path.join('/tmp/output', final_filename)
    
                # ... 在这里安全地处理提取出的文件 ...
    
        return {'status': 'ok'}
    

2.3 供应链安全(SCA)

  • 实战: 在CI/CD流水线中(如GitHub Actions, AWS CodePipeline),集成TrivySnyk等工具,在部署Lambda函数之前,自动扫描其依赖项是否存在已知漏洞。

  • 示例 (GitHub Actions + Trivy):

    YAML

    - name: Scan function dependencies for vulnerabilities
      uses: aquasecurity/trivy-action@master
      with:
        scan-type: 'fs' # 文件系统扫描
        scan-ref: './my-lambda-function-dir' # 指向函数代码和依赖目录
        exit-code: '1' # 如果发现高危漏洞,则构建失败
        format: 'table'
    

2.4 日志与监控

  • AWS CloudTrail: 记录所有对你AWS账户的API调用。如果你看到一个Lambda函数的IAM角色,突然在尝试调用它本不应有的权限(如iam:CreateUser),这就是一个强烈的告警信号。

  • Amazon CloudWatch: 收集所有Lambda函数的执行日志。配置告警规则,监控函数的异常行为,例如:

    • **执行时间(Duration)**远超正常基线。

    • **错误率(Errors)**突然飙升。

    • **调用量(Invocations)**在非预期的时间激增。

结论

无服务器(Serverless)架构并非安全的“银弹”,它只是将安全的战场,从宏观的“服务器加固”,转移到了微观的**“函数级纵深防御”**。

一个安全的Lambda应用,是一个最小权限IAM角色健壮的安全编码实践持续的供应链扫描全面的运行时监控共同作用的结果。对于拥抱Serverless的开发者和企业而言,必须深刻理解这份新的“安全契约”,将安全思维注入到每一个函数、每一个事件和每一个权限策略之中,才能真正驾驭无服务器带来的敏捷与弹性,同时不为攻击者留下任何可乘之机。

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐