Статья по безопасности Serverless / FaaS (Serverless / FaaS Security Cheat Sheet)
Введение
Вычисления в модели serverless (функции как сервис — FaaS), например AWS Lambda, Azure Functions и Google Cloud Functions, упрощают разработку и масштабирование приложений. При этом модель выполнения (короткоживущие событийно-ориентированные функции в управляемой среде) создаёт особые риски безопасности по сравнению с классической архитектурой.
Эта статья описывает практики защиты serverless-приложений и снижения поверхности атаки.
Ключевые риски
- Избыточные права функций (широкие роли IAM, политики с
*). - Непроверенные данные событий (API Gateway, S3, Pub/Sub, IoT).
- Утечки при холодном старте и в контексте выполнения (состояние, побочные каналы по времени).
- Злоупотребление цепочками вызовов (скомпрометированная функция вызывает другие).
- Риски общей среды (утечки в мультитенантности, повторное использование
/tmp). - Секреты в коде или переменных окружения.
- Чрезмерный сетевой доступ.
Лучшие практики
1. Принцип наименьших привилегий
- Назначайте функциям минимально необходимые права IAM.
- Отдельная роль на функцию (избегайте общих ролей с высокими привилегиями).
- Ограничивайте ключи к БД и API минимальным набором операций.
Плохая политика IAM (слишком широкая):
{
"Effect": "Allow",
"Action": "*",
"Resource": "*"
}
Хорошая политика IAM (с ограничением области):
{
"Effect": "Allow",
"Action": ["dynamodb:GetItem", "dynamodb:PutItem"],
"Resource": "arn:aws:dynamodb:us-east-1:123456789012:table/Orders"
}
2. Изоляция окружений
- Отключайте сетевой доступ по умолчанию, если он не нужен (например исходящий в интернет).
- Размещайте функции в приватных подсетях с контролируемым egress.
- Изолируйте чувствительные функции (платежи, аутентификация) от универсальных.
- Разделяйте продакшен и стенды жёсткими границами.
Конфигурация VPC для AWS Lambda (ограничительная):
VpcConfig:
SubnetIds:
- subnet-123456
SecurityGroupIds:
- sg-restrict-outbound
3. Безопасный вызов функций
- Требуйте аутентификацию и авторизацию для всех триггеров (API Gateway, Pub/Sub, S3, IoT).
- Вызовы «функция — функция» подтверждайте подписанными токенами или workload identity.
- Применяйте rate limiting и throttling против DoS и злоупотреблений.
Пример авторизатора API Gateway (проверка JWT):
{
"Type": "JWT",
"IdentitySource": "$request.header.Authorization",
"Issuer": "https://secure-idp.example.com/",
"Audience": "my-api-client"
}
4. Валидация данных события
- Считайте любое тело события недоверенным вводом.
- Применяйте строгую проверку и при необходимости нормализацию (длина, тип, формат).
- Защищайтесь от типовых инъекций (SQLi, XSS, JSON-инъекция, небезопасная десериализация).
- Удаляйте лишние поля и метаданные до обработки.
Пример: проверка ввода в Python для Lambda
import json
import re
def lambda_handler(event, context):
body = json.loads(event["body"])
email = body.get("email", "")
if not re.match(r"[^@]+@[^@]+\.[^@]+", email):
return {"statusCode": 400, "body": "Invalid email"}
# безопасная обработка
return {"statusCode": 200, "body": "OK"}
5. Холодный старт и безопасность контекста выполнения
- Не полагайтесь на то, что среда выполнения «чиста» между вызовами.
- Не храните секреты и временные чувствительные данные в глобальных или статических переменных.
- Учитывайте побочные каналы (разница во времени, оставшиеся файлы в
/tmp). - Для критичных нагрузок используйте одноразовые среды выполнения, если платформа это поддерживает.
Плохо:
# Секрет остаётся в глобальной переменной между вызовами
SECRET_KEY = "hardcoded-secret"
Хорошо:
import os
from my_secrets_lib import get_secret
def lambda_handler(event, context):
secret = get_secret("db-password") # получать заново при необходимости
...
6. Управление секретами
- По возможности не храните секреты в переменных окружения.
- Получайте секреты из хранилищ, используя локальные расширения с кэшем, а не только env.
- Используйте краткоживущие учётные данные (STS, федерация workload identity).
- Автоматизируйте ротацию секретов.
Получение секрета в AWS Lambda (Python через Parameters and Secrets Extension):
Важно: для функции нужно подключить и включить расширение AWS Lambda Parameters and Secrets Extension (например слоём Lambda). Иначе http://localhost:2773 будет недоступен, и запросы к нему завершатся ошибкой.
import os
import json
import urllib.request
import urllib.parse
def get_secret(secret_name):
"""
Читает секрет через локальный эндпоинт расширения Lambda.
Быстрее и дешевле за счёт локального кэша.
"""
# Расширение слушает локальный HTTP на порту 2773
encoded_name = urllib.parse.quote_plus(secret_name)
secrets_extension_endpoint = (
f"http://localhost:2773/secretsmanager/get?secretId={encoded_name}"
)
# Аутентификация токеном окружения Lambda
session_token = os.environ.get("AWS_SESSION_TOKEN")
if not session_token:
raise RuntimeError(
"Missing AWS_SESSION_TOKEN required for "
"Parameters and Secrets Extension authentication."
)
headers = {
"X-Aws-Parameters-Secrets-Token": session_token
}
request = urllib.request.Request(
secrets_extension_endpoint,
headers=headers,
method="GET"
)
with urllib.request.urlopen(request, timeout=3) as response:
if response.status != 200:
raise Exception(
f"Error retrieving secret: {response.read().decode('utf-8')}"
)
response_json = json.loads(response.read())
secret_string = response_json["SecretString"]
return json.loads(secret_string)
См. также Secrets Management Cheat Sheet.
7. Мониторинг и журналирование
- Централизованные логи (CloudWatch, Azure Monitor, GCP Logging).
- Маскируйте секреты и персональные данные.
Пример: редактирование полей перед записью в лог
import logging
def log_event(event):
safe_event = {k: ("***" if "password" in k else v) for k, v in event.items()}
logging.info(safe_event)
8. Безопасность цепочки поставки
- Сканируйте зависимости (
npm audit,pip-audit,safety). - Используйте минимальные пакеты для деплоя.
- Подписывайте пакеты и проверяйте контрольные суммы.
Проверка хеша слоя AWS Lambda:
shasum -a 256 layer.zip
Стоит и не стоит
Стоит:
- Навязывать наименьшие привилегии для каждой функции.
- Проверять все входные данные событий.
- Брать секреты из хранилищ, а не только из переменных окружения.
- Ограничивать исходящий сетевой трафик.
- Мониторить вызовы и логи.
Не стоит:
- Вшивать секреты в код или конфигурацию в репозитории.
- Считать, что среда выполнения между вызовами гарантированно чистая.
- Выдавать права IAM с
*. - Оставлять чувствительные данные в
/tmpили глобальных переменных. - Слепо доверять источникам событий.
Ссылки
© Перевод на русский язык. Оригинальные материалы: OWASP Cheat Sheet Series.
Этот проект использует материалы OWASP, распространяемые по лицензии CC BY-SA 4.0.