civil-and-structural-engineering
How to Manage Secrets and Sensitive Data in Serverless Applications
Table of Contents
Introduction: The Unique Security Demands of Serverless
Serverless computing has transformed how teams build and deploy applications. By abstracting away servers, scaling, and patching, platforms like AWS Lambda, Azure Functions, and Google Cloud Functions let developers focus purely on business logic. However, this paradigm shift also introduces new security challenges, particularly around the management of secrets and sensitive data. In a traditional server‑based application, secrets often reside in a dedicated vault on the filesystem or inside a protected database. In serverless, functions are ephemeral, stateless, and frequently invoked – there is no persistent filesystem or long‑lived process to keep secrets safe. Hard‑coding API keys, database credentials, or encryption keys into code is an unacceptable risk, as the code is often stored in version control and can be inspected by anyone with access.
This article provides a comprehensive guide to handling secrets in serverless environments. We’ll examine the core challenges, dive into best practices, walk through concrete implementation patterns using major cloud providers, and discuss how to secure the entire lifecycle of sensitive data – from development to production.
Understanding the Challenges of Secret Management in Serverless
Serverless architectures are inherently stateless. When a function is invoked, it runs in a container that is torn down after execution (or reused for a short time). This ephemeral nature means you cannot rely on long‑running processes or file systems to store secrets. Common pitfalls include:
- Exposure in code and logs: Developers may inadvertently commit secrets to source control or log them during debugging. Once a secret is in a log stream, it can be retrieved by anyone with log access – and logs are often retained indefinitely.
- Environment variable limitations: While environment variables are convenient, they are often set during deployment and stored in plain text in the function configuration. If an attacker gains read access to the function’s configuration (e.g., via compromised CI/CD pipeline), they obtain the secret. Additionally, environment variables are visible in the cloud provider’s console, so internal teams may have unnecessary exposure.
- Cold starts and caching: Retrieving secrets on every invocation can introduce latency and cost. Developers sometimes cache secrets in memory, but the ephemeral container may be reused for multiple invocations – leading to stale or expired secrets if rotation is frequent.
- Auditability and rotation: Without a centralised vault, it is difficult to know who accessed which secret when, or to rotate secrets without updating every function.
These challenges are compounded by the distributed, event‑driven nature of serverless applications. A single function might need to call a database, an external API, and a queue – each requiring separate credentials. Managing all of these securely across dozens or hundreds of functions demands a systematic approach.
Best Practices for Managing Secrets and Sensitive Data
The foundation of any serverless security strategy is the principle of least privilege: each function should have access only to the secrets it absolutely needs, and for the shortest duration possible. Below are the essential practices, organised by category.
1. Use Dedicated Secret Management Services
Every major cloud provider offers a purpose‑built service for storing and accessing secrets:
- AWS Secrets Manager – manages secrets with automatic rotation and fine‑grained access policies.
- Azure Key Vault – stores secrets, keys, and certificates, and integrates with Azure Functions via managed identities.
- Google Cloud Secret Manager – offers versioning, IAM controls, and integration with Cloud Functions and Cloud Run.
These services encrypt secrets at rest and in transit, provide audit logs of every access, and allow you to rotate secrets without redeploying functions. Never store secrets in plain text configuration files or inline code.
2. Leverage Environment Variables – But With Care
Environment variables remain a common way to inject configuration into serverless functions. However, they should never hold secrets directly. Instead, use environment variables to store references to secrets (e.g., the ARN of a secret in AWS Secrets Manager or the name of a secret in Azure Key Vault). The function then retrieves the actual secret at runtime using the appropriate SDK. This way, even if an attacker reads the environment variables, they only get a pointer, not the secret itself.
3. Encrypt Everything at Rest and in Transit
Secrets must be encrypted wherever they reside: inside the secret management service, when cached in memory (using techniques like memory‑hard encryption), and when transmitted over the network. All major secret management services enforce encryption at rest using envelope encryption with customer‑managed keys (CMKs) where possible. For transit, always use TLS 1.2 or higher between your function and the secret store.
4. Implement Strict Access Controls and the Principle of Least Privilege
Use role‑based access control (RBAC) or attribute‑based access control (ABAC) to limit which functions can read which secrets. In AWS, attach IAM policies to the function’s execution role that grant secretsmanager:GetSecretValue only for specific secret ARNs. Similarly, in Azure, use managed identities and assign granular Key Vault access policies. Avoid wildcard permissions that allow a function to read any secret in the account.
Additionally, restrict access to the secret management service itself. Only administrators should be able to create, modify, or delete secrets. Operators and developers should be limited to reading secrets needed for their work, and audit logs should be reviewed periodically.
5. Rotate Secrets Regularly
Automatic secret rotation is critical for limiting the blast radius of a compromise. AWS Secrets Manager can rotate secrets on a schedule (e.g., every 30 days) by calling a Lambda function that updates the secret in the target service (such as a database). Azure Key Vault integrates with other Azure services for rotation, though it requires custom automation for non‑Azure targets. Google Cloud Secret Manager supports versioning, making manual rotation straightforward, but automatic rotation requires a Cloud Function or Cloud Scheduler job.
Even with automatic rotation, you must ensure that old secret versions are not kept indefinitely. Implement a retention policy that purges older versions after a safe window (e.g., 30 days after rotation) to prevent an attacker from using an old, compromised secret.
6. Use Dynamic and Temporary Credentials Where Possible
For services that support it, prefer temporary credentials over long‑lived secrets. For example, AWS Lambda functions can assume IAM roles that issue temporary credentials (via STS) for accessing S3, DynamoDB, or other AWS services. This eliminates the need for any hard‑coded credentials altogether. Similarly, Azure Functions can use managed identities to authenticate to Azure services without storing any secrets. Google Cloud Functions can use service accounts with short‑lived tokens.
7. Audit and Monitor Secret Access
Enable logging on your secret management service and ship those logs to a central security information and event management (SIEM) platform. Monitor for unusual access patterns, such as a function reading secrets more frequently than expected, or access from unknown IP addresses. Set up alerts for errors like “access denied” to the secret store, which could indicate a misconfigured function or a brute‑force attempt.
Implementing Secrets Management: Real‑World Patterns
Knowing the practices is one thing; applying them correctly is another. Below are implementation patterns for the three major cloud providers, along with cross‑platform considerations.
AWS Lambda with AWS Secrets Manager
To integrate AWS Secrets Manager with a Lambda function, follow these steps:
- Create the secret – Store your database password, API key, or other sensitive string as a secret in Secrets Manager. Enable automatic rotation if the target service supports it.
- Grant the Lambda execution role access – Add a policy that allows
secretsmanager:GetSecretValueon the specific secret ARN. Optionally, also allowsecretsmanager:DescribeSecretfor metadata. - Retrieve the secret at runtime – In your function code (Node.js, Python, etc.), import the AWS SDK and call
secretsmanager.getSecretValue(). Cache the secret in a global variable to reduce latency and cost on repeated invocations. For example (simplified code):
const AWS = require('aws-sdk');
const secretsManager = new AWS.SecretsManager();
let cachedSecret;
exports.handler = async () => {
if (!cachedSecret) {
const data = await secretsManager.getSecretValue({ SecretId: 'arn:aws:secretsmanager:us-east-1:123456789012:secret:MyDbPassword-abc123' }).promise();
cachedSecret = data.SecretString;
}
// use cachedSecret securely, never log it
};
Note that the secret is fetched only once per cold start. On subsequent warm invocations, the cached value is reused. If you rotate secrets frequently, consider setting a short Time‑to‑Live (TTL) on the cache, or check the secret’s version before reuse.
Azure Functions with Azure Key Vault
Azure offers a more seamless integration through Key Vault references in App Configuration or as part of the function’s settings. Instead of calling the SDK manually, you can set an environment variable to this special syntax:
@Microsoft.KeyVault(SecretUri=https://myvault.vault.azure.net/secrets/DbPassword/)
When the function runs, Azure automatically resolves the reference and injects the secret value as an environment variable. This approach greatly simplifies code and keeps secrets out of any configuration file. However, you must still grant the function’s system‑assigned managed identity the Key Vault Secret User role.
For functions that need to retrieve multiple secrets dynamically, use the Azure.Identity and Azure.Security.KeyVault.Secrets SDKs to fetch secrets by name. Always use DefaultAzureCredential for authentication, which will use the managed identity in production and your local credentials during development.
Google Cloud Functions with Secret Manager
Google Cloud Functions can access secrets via environment variables that reference a secret version. In the deployment command, you can specify an environment variable like DB_PASSWORD whose value is set to projects/PROJECT_ID/secrets/MY_SECRET/versions/latest. The function will automatically resolve the secret value at runtime. Alternatively, use the Secret Manager client library to fetch secrets on demand.
One unique feature of Google Cloud Secret Manager is that you can grant access at the secret level using IAM bindings, and you can also use Customer‑Managed Encryption Keys (CMEK) for additional protection.
Beyond the Cloud: Secrets in CI/CD and Development
Secrets must be managed not only in production, but also during development and continuous integration/continuous deployment (CI/CD) pipelines. Developers often need to test serverless functions locally with real service endpoints. The safest practice is to use personal secrets or temporary credentials that are scoped to their identity and have limited permissions.
- Local development: Use tools like
aws-vault(for AWS),Azure CLIwithaz account set, orgcloud auth application-default loginto inject credentials via environment variables. Never hard‑code secrets in local configuration files that might be committed. - CI/CD pipelines: Store secrets as pipeline secrets (e.g., GitHub Actions secrets, GitLab CI/CD variables) and inject them at build or deploy time. Avoid printing secrets in logs; use masked variables where possible. For multi‑stage deployments, consider using a dedicated secret management service that the pipeline calls via its own identity, rather than passing secrets through environment variables.
- Infrastructure as Code (IaC): If you use Terraform, AWS CloudFormation, or Azure Bicep to deploy serverless functions, never hard‑code secrets in the IaC templates. Instead, use a secure remote state backend and reference secrets from the cloud provider’s secret store. Many IaC tools have dedicated resources to read secrets securely (e.g.,
aws_secretsmanager_secretdata source in Terraform).
Compliance and Standardisation
Many regulatory frameworks (GDPR, SOC 2, PCI‑DSS) require strict controls over access to sensitive data. Proper secret management helps meet these requirements by providing:
- Audit trails: Secret management services log every read, write, and delete, giving you a complete history of access.
- Least privilege enforcement: IAM policies ensure that only authorised functions and users can access secrets.
- Encryption: Secrets are encrypted at rest and in transit, satisfying data protection requirements.
Adopt a company‑wide policy for secret naming, rotation intervals, and review cycles. Use tools like OWASP’s Secret Management Cheat Sheet (OWASP Secrets Management Cheat Sheet) and NIST SP 800‑57 (NIST SP 800‑57) as authoritative references for key management.
Conclusion: Building a Secret‑Safe Serverless Architecture
Managing secrets in serverless applications is not a one‑time task but an ongoing discipline. The ephemeral and distributed nature of serverless demands that you never trust code or configuration to hold secrets. Instead, rely on dedicated secret management services, enforce least‑privilege access, rotate credentials automatically, and monitor every access.
By following the best practices outlined in this article – leveraging AWS Secrets Manager, Azure Key Vault, or Google Cloud Secret Manager; using environment variables only as pointers; caching wisely; and integrating secure secret handling into CI/CD – you can build serverless applications that are both powerful and secure. Remember that secrets are the keys to your digital kingdom. Treat them with the respect they deserve.
For deeper dives, refer to the official documentation: