Expert guidance for security auditing, compliance assessments, code reviews, vulnerability assessments, and regulatory compliance (SOC 2, GDPR, HIPAA, PCI-DSS).
Core Concepts
Audit Types
-
Security Audit: Vulnerability assessment, penetration testing
-
Code Audit: Code review, static analysis, security patterns
-
Compliance Audit: SOC 2, GDPR, HIPAA, PCI-DSS, ISO 27001
-
Infrastructure Audit: Configuration review, access control
-
Process Audit: SDLC, change management, incident response
Audit Frameworks
-
OWASP ASVS (Application Security Verification Standard)
-
NIST Cybersecurity Framework
-
CIS Controls
-
ISO 27001/27002
-
SOC 2 Trust Service Criteria
Audit Process
-
Planning and scoping
-
Information gathering
-
Vulnerability identification
-
Risk assessment
-
Reporting
-
Remediation tracking
-
Follow-up verification
Security Code Review
Authentication Review
// ❌ Issues to flag
class AuthService {
// Issue 1: Weak password requirements
validatePassword(password) {
return password.length >= 6; // Too short!
}
// Issue 2: Password stored in plaintext
async createUser(email, password) {
await db.users.create({ email, password }); // No hashing!
}
// Issue 3: Timing attack vulnerability
async login(email, password) {
const user = await db.users.findOne({ email });
if (!user) return null;
// Direct comparison reveals timing
if (user.password === password) {
return user;
}
return null;
}
// Issue 4: No rate limiting
// Issue 5: No MFA support
// Issue 6: Predictable session tokens
generateSessionToken() {
return Math.random().toString(36); // Not cryptographically secure!
}
}
// ✅ Secure implementation
const bcrypt = require('bcrypt');
const crypto = require('crypto');
class SecureAuthService {
// Strong password validation
validatePassword(password) {
const minLength = 12;
const hasUppercase = /[A-Z]/.test(password);
const hasLowercase = /[a-z]/.test(password);
const hasNumber = /[0-9]/.test(password);
const hasSpecial = /[^A-Za-z0-9]/.test(password);
return password.length >= minLength &&
hasUppercase && hasLowercase &&
hasNumber && hasSpecial;
}
// Secure password hashing
async hashPassword(password) {
const saltRounds = 12;
return await bcrypt.hash(password, saltRounds);
}
async createUser(email, password) {
if (!this.validatePassword(password)) {
throw new Error('Password does not meet requirements');
}
const passwordHash = await this.hashPassword(password);
await db.users.create({
email: email.toLowerCase(),
passwordHash
});
}
// Constant-time comparison with rate limiting
async login(email, password) {
// Check rate limit
const attempts = await this.getLoginAttempts(email);
if (attempts > 5) {
throw new Error('Too many login attempts. Try again later.');
}
const user = await db.users.findOne({
email: email.toLowerCase()
});
// Always hash password even if user not found (timing attack prevention)
const isValid = user ?
await bcrypt.compare(password, user.passwordHash) :
await bcrypt.compare(password, '$2b$12$dummyhash');
if (!user || !isValid) {
await this.recordFailedAttempt(email);
throw new Error('Invalid credentials');
}
await this.clearLoginAttempts(email);
return user;
}
// Cryptographically secure tokens
generateSessionToken() {
return crypto.randomBytes(32).toString('hex');
}
// MFA support
async verifyMFA(user, token) {
const speakeasy = require('speakeasy');
return speakeasy.totp.verify({
secret: user.mfaSecret,
encoding: 'base32',
token,
window: 2
});
}
}
SQL Injection Review
// Audit checklist for SQL injection:
// 1. Are all queries parameterized?
// 2. Is user input sanitized?
// 3. Are ORM features used correctly?
// 4. Are stored procedures parameterized?
// ❌ Vulnerable patterns to flag
async function searchUsers(name) {
// Issue: String concatenation
const query = `SELECT * FROM users WHERE name = '${name}'`;
return await db.query(query);
}
async function updateUser(id, data) {
// Issue: Dynamic column names not validated
const columns = Object.keys(data).join(', ');
const query = `UPDATE users SET ${columns} WHERE id = ${id}`;
return await db.query(query);
}
// ❌ ORM misuse
async function findUsers(filters) {
// Issue: Raw WHERE clause from user input
return await User.findAll({
where: db.literal(filters.where)
});
}
// ✅ Secure patterns
async function searchUsers(name) {
// Parameterized query
return await db.query(
'SELECT * FROM users WHERE name = ?',
[name]
);
}
async function updateUser(id, data) {
// Whitelist allowed columns
const allowedColumns = ['name', 'email', 'bio'];
const updates = {};
for (const [key, value] of Object.entries(data)) {
if (allowedColumns.includes(key)) {
updates[key] = value;
}
}
return await User.update(updates, {
where: { id }
});
}
async function findUsers(filters) {
// Use ORM query builder
return await User.findAll({
where: {
name: { [Op.like]: `%${filters.name}%` },
active: true
}
});
}
Authorization Review
// Audit checklist:
// 1. Is authentication checked before authorization?
// 2. Are resource ownership checks present?
// 3. Is role-based access control implemented?
// 4. Are there direct object reference vulnerabilities?
// ❌ Insecure patterns
app.delete('/api/posts/:id', authenticate, async (req, res) => {
// Issue: No authorization check!
await Post.delete(req.params.id);
res.status(204).send();
});
app.get('/api/documents/:id', async (req, res) => {
// Issue: No authentication at all!
const doc = await Document.findById(req.params.id);
res.json(doc);
});
// ✅ Secure patterns
const authorize = (resource) => async (req, res, next) => {
const item = await db[resource].findById(req.params.id);
if (!item) {
return res.status(404).json({ error: 'Not found' });
}
// Check ownership or admin role
if (item.userId !== req.user.id && !req.user.isAdmin) {
return res.status(403).json({ error: 'Forbidden' });
}
req.resource = item;
next();
};
app.delete('/api/posts/:id',
authenticate,
authorize('posts'),
async (req, res) => {
await req.resource.delete();
res.status(204).send();
}
);
// Role-based access control
const requireRole = (...roles) => (req, res, next) => {
if (!req.user || !roles.includes(req.user.role)) {
return res.status(403).json({ error: 'Insufficient permissions' });
}
next();
};
app.post('/api/admin/users',
authenticate,
requireRole('admin'),
async (req, res) => {
// Admin-only endpoint
}
);
XSS and Output Encoding Review
// Audit checklist:
// 1. Is user input escaped in HTML context?
// 2. Is Content-Security-Policy header set?
// 3. Are dangerous functions (eval, innerHTML) avoided?
// 4. Is templating engine auto-escaping enabled?
// ❌ Vulnerable patterns
app.get('/search', (req, res) => {
// Issue: No escaping
res.send(`<h1>Results for: ${req.query.q}</h1>`);
});
app.post('/comment', async (req, res) => {
// Issue: Storing unsanitized HTML
await Comment.create({
text: req.body.comment,
html: req.body.comment // Dangerous!
});
});
// Client-side issues
function displayComment(comment) {
// Issue: Using innerHTML
document.getElementById('comment').innerHTML = comment;
// Issue: Using eval
eval(comment);
}
// ✅ Secure patterns
const escape = require('escape-html');
app.get('/search', (req, res) => {
res.send(`<h1>Results for: ${escape(req.query.q)}</h1>`);
});
// Or use templating with auto-escape
app.get('/search', (req, res) => {
res.render('search', { query: req.query.q }); // Auto-escaped
});
// Content Security Policy
app.use((req, res, next) => {
res.setHeader('Content-Security-Policy',
"default-src 'self'; " +
"script-src 'self'; " +
"style-src 'self' 'unsafe-inline'; " +
"img-src 'self' data: https:;"
);
next();
});
// Client-side: Use textContent
function displayComment(comment) {
document.getElementById('comment').textContent = comment;
}
Compliance Auditing
GDPR Compliance Checklist
// GDPR Requirements Audit
// 1. Lawful Basis for Processing
// ✓ Explicit consent obtained
// ✓ Purpose clearly stated
// ✓ Option to withdraw consent
// 2. Data Minimization
// Review: Are we collecting only necessary data?
async function createUser(data) {
// ❌ Collecting too much
const user = {
email: data.email,
password: data.password,
ssn: data.ssn, // Unnecessary!
medicalHistory: data.medical, // Unnecessary!
location: data.location // May be unnecessary
};
// ✅ Only essential data
const user = {
email: data.email,
passwordHash: await hashPassword(data.password)
};
}
// 3. Right to Access (Subject Access Request)
app.get('/api/gdpr/data', authenticate, async (req, res) => {
const userData = {
personalInfo: await User.findById(req.user.id),
posts: await Post.findByUserId(req.user.id),
comments: await Comment.findByUserId(req.user.id),
loginHistory: await LoginHistory.findByUserId(req.user.id)
};
res.json(userData);
});
// 4. Right to Erasure (Right to be Forgotten)
app.delete('/api/gdpr/delete-account', authenticate, async (req, res) => {
const userId = req.user.id;
await db.transaction(async (tx) => {
// Anonymize or delete personal data
await User.anonymize(userId, tx);
await Post.anonymizeByUser(userId, tx);
await Comment.anonymizeByUser(userId, tx);
// Keep audit logs (legal requirement)
await AuditLog.create({
action: 'account_deletion',
userId,
timestamp: new Date()
}, tx);
});
res.status(204).send();
});
// 5. Right to Data Portability
app.get('/api/gdpr/export', authenticate, async (req, res) => {
const data = await exportUserData(req.user.id);
res.setHeader('Content-Type', 'application/json');
res.setHeader('Content-Disposition', 'attachment; filename="my-data.json"');
res.json(data);
});
// 6. Breach Notification (72 hours)
async function handleDataBreach(breach) {
// Log breach
await SecurityIncident.create({
type: 'data_breach',
severity: breach.severity,
affectedUsers: breach.userIds.length,
detectedAt: new Date()
});
// Notify authorities within 72 hours if high risk
if (breach.severity === 'high') {
await notifyDataProtectionAuthority(breach);
}
// Notify affected users
for (const userId of breach.userIds) {
await notifyUserOfBreach(userId, breach);
}
}
// 7. Privacy by Design
// - Encryption at rest and in transit
// - Access controls
// - Audit logging
// - Data retention policies
// 8. Data Processing Agreement
// - Document third-party processors
// - Ensure processor compliance
// - Review contracts
SOC 2 Compliance Audit
// SOC 2 Trust Service Criteria
// 1. Security - Access Control class AccessControlAudit { async auditUserAccess() { // Review user permissions const users = await User.findAll(); const issues = [];
for (const user of users) {
// Check for overprivileged users
if (user.role === 'admin' && !user.adminJustification) {
issues.push({
type: 'excessive_privilege',
user: user.email,
message: 'Admin access without justification'
});
}
// Check for inactive users with access
const daysSinceLogin = daysBetween(user.lastLoginAt, new Date());
if (daysSinceLogin > 90) {
issues.push({
type: 'stale_access',
user: user.email,
message: `No login for ${daysSinceLogin} days`
});
}
}
return issues;
}
async auditAPIKeys() { const apiKeys = await APIKey.findAll(); const issues = [];
for (const key of apiKeys) {
// Check for keys without expiration
if (!key.expiresAt) {
issues.push({
type: 'no_expiration',
keyId: key.id,
message: 'API key has no expiration'
});
}
// Check for unused keys
if (!key.lastUsedAt ||
daysBetween(key.lastUsedAt, new Date()) > 90) {
issues.push({
type: 'unused_key',
keyId: key.id,
message: 'API key not used in 90 days'
});
}
}
return issues;
} }
// 2. Availability - Monitoring class