- Treat all external input as untrusted, including user input, API requests, uploaded files, headers, and data from third-party services.
- Validate input on the server side using allowlists wherever practical.
- Enforce appropriate type, length, range, and format constraints.
- Reject malformed or unexpected input rather than attempting to “fix” it.
- Do not rely on client-side validation for security.
- Use established authentication mechanisms rather than implementing authentication from scratch.
- Enforce strong password policies and securely hash passwords using modern password-hashing algorithms.
- Apply authorization checks on every protected operation, not only in the UI.
- Follow the principle of least privilege.
- Never hard-code passwords, API keys, tokens, or other credentials in source code.
- Implement appropriate session expiration and revocation.
- Store application secrets in an approved secrets manager, such as Azure Key Vault, rather than in source code or configuration files.
- Access secrets using managed identities or workload identities where possible instead of storing credentials for Key Vault access.
- Never commit secrets, connection strings, API keys, passwords, or certificates to source control.
- Apply least-privilege access policies/Role-Base-Access-Control to Key Vault resources.
- Enable auditing and monitoring for secret access.
- Rotate secrets and keys according to their security requirements.
- Use parameterized queries or prepared statements.
- Never construct SQL queries by concatenating untrusted input.
- Use database accounts with the minimum required privileges.
- Validate and constrain data before storing it.
- Protect sensitive data at rest and in transit.
- Avoid exposing database error details to users.
- Encode output according to its destination: HTML, JavaScript, URLs, shell commands, etc.
- Use framework-provided escaping mechanisms whenever available.
- Never pass untrusted input directly to operating-system commands, interpreters, templates, or dynamic code execution.
- Avoid dynamic evaluation such as
eval()unless there is a documented and reviewed security requirement.
- Protect against common vulnerabilities such as XSS, CSRF, SQL injection, SSRF, path traversal, and insecure direct object references.
- Use appropriate security-related HTTP headers.
- Configure cookies with
Secure,HttpOnly, and appropriateSameSiteattributes. - Use HTTPS for all sensitive communications.
- Implement rate limiting for authentication, password-reset, and other abuse-prone endpoints.
- Do not expose internal implementation details through APIs.
- Authenticate and authorize every protected API endpoint.
- Validate request bodies, parameters, headers, and uploaded content.
- Apply rate limits and request-size limits.
- Return only the data the caller is authorized to receive.
- Avoid excessive data exposure in API responses.
- Use explicit API schemas and reject unexpected fields where appropriate.
- Version APIs when security-sensitive changes could affect existing clients.
- Treat uploaded files as untrusted.
- Validate file type, size, name, and content rather than trusting extensions or client-supplied MIME types.
- Store uploads outside executable directories where possible.
- Generate server-side filenames instead of using user-supplied paths.
- Prevent path traversal and unauthorized file access.
- Scan potentially dangerous uploads where appropriate.
- Show users generic, actionable error messages without revealing sensitive implementation details.
- Log security-relevant events such as authentication failures, authorization failures, privilege changes, and suspicious activity.
- Never log passwords, tokens, session identifiers, encryption keys, or other sensitive secrets.
- Protect logs from unauthorized access and tampering.
- Ensure logs contain enough context to support investigation without unnecessarily collecting personal data.
- Use well-established cryptographic libraries and approved algorithms.
- Do not implement cryptographic algorithms yourself.
- Do not use deprecated or broken algorithms or protocols.
- Generate cryptographic keys and random values using a cryptographically secure random-number generator.
- Protect encryption keys separately from encrypted data.
- Define key rotation and lifecycle procedures.
- Keep dependencies updated and remove unused dependencies.
- Use trusted package repositories and verify package integrity where supported.
- Scan dependencies for known vulnerabilities.
- Review new dependencies before introducing them into production systems.
- Pin or otherwise control dependency versions appropriately for reproducible builds.
- Treat third-party code as potentially untrusted.
- Use secure defaults.
- Disable debugging, verbose errors, development accounts, and unnecessary services in production.
- Separate configuration from application code.
- Review infrastructure and application configuration as part of security testing.
- Ensure development and test configurations cannot accidentally expose production data or credentials.
For languages where memory safety is a concern:
- Check array and buffer boundaries.
- Validate allocation sizes.
- Avoid unsafe memory operations unless necessary and thoroughly reviewed.
- Properly release resources such as files, sockets, database connections, and locks.
- Protect against resource exhaustion through appropriate limits and timeouts.
- Protect shared state from race conditions.
- Use appropriate synchronization mechanisms.
- Design operations that may be retried to be idempotent where possible.
- Set timeouts on external calls.
- Avoid trusting client-controlled timestamps, counters, or state transitions.
- Consider replay, duplicate-request, and partial-failure scenarios.
- Collect only data required for the application's purpose.
- Minimize storage and retention of sensitive information.
- Restrict access to personal and confidential data.
- Do not include sensitive information in URLs when avoidable.
- Apply appropriate encryption and access controls.
- Follow applicable privacy and data-protection requirements.
Every security-sensitive change should receive appropriate review.
Developers should:
- Review authentication and authorization changes carefully.
- Check for injection and input-validation issues.
- Run automated static analysis and dependency scanning.
- Include security-focused tests for important controls.
- Perform dynamic/application security testing where appropriate.
- Treat security findings as engineering defects and track them to resolution.
Before production deployment:
- Ensure secrets are not present in source code or build artifacts.
- Verify security configuration.
- Run applicable automated security checks.
- Confirm required authentication and authorization controls.
- Remove debugging functionality.
- Ensure monitoring and security logging are operational.
- Have a rollback procedure for security-related releases.
A development task should not be considered complete until the developer has considered:
- All external input is validated.
- Authentication is correctly enforced where required.
- Authorization is checked server-side.
- No secrets are hard-coded or committed.
- Database queries use safe parameterization.
- Output is appropriately encoded.
- Errors do not expose sensitive information.
- Sensitive information is not unnecessarily logged.
- Dependencies have been checked for known vulnerabilities.
- Security-relevant functionality has appropriate tests.
- Secure configuration has been verified.
- Security-sensitive code has received peer review.