On this page
Security practices ensure that software is developed with security considerations throughout the entire lifecycle. This practice covers secure coding, security training, and vulnerability management.
Secure Development Lifecycle
Secure Development Lifecycle
Security Training
Required Training Topics
Training Resources
Training Verification
training_requirements:
developers:
- course: "OWASP Top 10"
frequency: "annual"
verification: "certificate"
- course: "Secure Coding Practices"
frequency: "annual"
verification: "quiz_score >= 80%"
operations:
- course: "Security Incident Response"
frequency: "annual"
verification: "drill_participation"
all_staff:
- course: "Security Awareness"
frequency: "annual"
verification: "completion"
Threat Modeling
STRIDE Framework
Threat Model Process
1. IDENTIFY ASSETS
- What are we protecting?
- Data, systems, processes
2. CREATE ARCHITECTURE OVERVIEW
- Data flow diagrams
- Trust boundaries
- Entry points
3. DECOMPOSE APPLICATION
- Components
- Data stores
- External dependencies
4. IDENTIFY THREATS
- Use STRIDE for each component
- Document potential attacks
5. DOCUMENT MITIGATIONS
- Controls for each threat
- Residual risk acceptance
6. VALIDATE
- Review with security team
- Update as system evolves
Secure Coding Practices
// BAD: No validation
function createUser(email: string, name: string) {
db.query(`INSERT INTO users (email, name) VALUES ('${email}', '${name}')`);
}
// GOOD: Parameterized queries and validation
import { z } from 'zod';
const CreateUserSchema = z.object({
email: z.string().email().max(255),
name: z.string().min(1).max(100).regex(/^[a-zA-Z\s]+$/),
});
async function createUser(input: unknown) {
const { email, name } = CreateUserSchema.parse(input);
await db.query('INSERT INTO users (email, name) VALUES ($1, $2)', [email, name]);
}
Authentication
// Password hashing with bcrypt
import bcrypt from 'bcrypt';
const SALT_ROUNDS = 12;
async function hashPassword(password: string): Promise<string> {
return bcrypt.hash(password, SALT_ROUNDS);
}
async function verifyPassword(password: string, hash: string): Promise<boolean> {
return bcrypt.compare(password, hash);
}
Authorization
// Role-based access control
type Role = 'admin' | 'manager' | 'user';
type Permission = 'read' | 'write' | 'delete' | 'admin';
const rolePermissions: Record<Role, Permission[]> = {
admin: ['read', 'write', 'delete', 'admin'],
manager: ['read', 'write', 'delete'],
user: ['read'],
};
function hasPermission(userRole: Role, requiredPermission: Permission): boolean {
return rolePermissions[userRole].includes(requiredPermission);
}
// Usage in middleware
function requirePermission(permission: Permission) {
return (req: Request, res: Response, next: NextFunction) => {
if (!hasPermission(req.user.role, permission)) {
return res.status(403).json({ error: 'Forbidden' });
}
next();
};
}
Sensitive Data Handling
// Redact sensitive data in logs
function sanitizeForLogging(obj: object): object {
const sensitiveFields = ['password', 'ssn', 'creditCard', 'token'];
return JSON.parse(JSON.stringify(obj), (key, value) => {
if (sensitiveFields.includes(key.toLowerCase())) {
return '[REDACTED]';
}
return value;
});
}
// Encrypt sensitive data at rest
import { createCipheriv, createDecipheriv, randomBytes } from 'crypto';
const ALGORITHM = 'aes-256-gcm';
function encrypt(text: string, key: Buffer): { encrypted: string; iv: string; tag: string } {
const iv = randomBytes(16);
const cipher = createCipheriv(ALGORITHM, key, iv);
let encrypted = cipher.update(text, 'utf8', 'hex');
encrypted += cipher.final('hex');
return {
encrypted,
iv: iv.toString('hex'),
tag: cipher.getAuthTag().toString('hex'),
};
}
// Express.js security headers with Helmet
import helmet from 'helmet';
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
scriptSrc: ["'self'"],
imgSrc: ["'self'", "data:", "https:"],
connectSrc: ["'self'", "https://api.example.com"],
fontSrc: ["'self'"],
objectSrc: ["'none'"],
mediaSrc: ["'self'"],
frameSrc: ["'none'"],
},
},
hsts: {
maxAge: 31536000,
includeSubDomains: true,
preload: true,
},
referrerPolicy: { policy: 'strict-origin-when-cross-origin' },
}));
Vulnerability Management
Vulnerability Response Process
Vulnerability Response
Severity-Based SLAs
Dependency Scanning Configuration
# .github/workflows/security.yml
name: Security Scanning
on:
push:
branches: [main, develop]
pull_request:
schedule:
- cron: '0 0 * * *' # Daily
jobs:
dependency-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Snyk to check for vulnerabilities
uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
args: --severity-threshold=high
- name: Upload Snyk results
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: snyk.sarif
Security Code Review Checklist
Authentication & Authorization
☐ Password strength requirements enforced
☐ Passwords hashed with bcrypt/Argon2 (cost factor >= 10)
☐ Session tokens are cryptographically random
☐ Session invalidation on logout
☐ MFA implemented for sensitive operations
☐ Authorization checked on every request
☐ All input validated and sanitized
☐ Parameterized queries used (no SQL injection)
☐ Output encoding applied (no XSS)
☐ File uploads validated (type, size, name)
☐ Rate limiting implemented
Data Protection
☐ Sensitive data encrypted at rest
☐ TLS 1.2+ for data in transit
☐ PII minimization applied
☐ Secrets not hardcoded
☐ Secure logging (no sensitive data)
Error Handling
☐ Generic error messages to users
☐ Detailed errors only in logs
☐ No stack traces in production
☐ Failed operations fail securely
Compliance
This section fulfills ISO 13485 requirements for risk management (7.1) and design verification (7.3.6), and ISO 27001 requirements for secure development lifecycle (A.8.25), secure coding (A.8.28), security testing (A.8.29), and vulnerability management (A.8.8).
View full compliance matrix