| name | security-first |
| description | Security-first development practices (Shift-Left Security). Trigger: When handling user input, authentication, data storage, or API design.
|
| license | Apache-2.0 |
| metadata | {"author":"mrp4sten","version":"1.0","scope":["root"],"auto_invoke":["Handling user input or authentication","Storing sensitive data","Designing APIs or endpoints","Working with external dependencies"]} |
| allowed-tools | Read, Edit, Write, Glob, Grep, Bash |
Security-First Development
Security is not a feature — it's a requirement. Apply security principles from day one (Shift-Left Security).
OWASP Top 10 (2021)
The most critical web application security risks:
- Broken Access Control
- Cryptographic Failures
- Injection
- Insecure Design
- Security Misconfiguration
- Vulnerable and Outdated Components
- Identification and Authentication Failures
- Software and Data Integrity Failures
- Security Logging and Monitoring Failures
- Server-Side Request Forgery (SSRF)
1. Input Validation (Defense Against Injection)
Never trust user input. Always validate, sanitize, and escape.
SQL Injection Prevention
const query = `SELECT * FROM users WHERE email = '${userInput}'`;
db.execute(query);
const query = 'SELECT * FROM users WHERE email = ?';
db.execute(query, [userInput]);
XSS Prevention
document.innerHTML = userInput;
import DOMPurify from 'dompurify';
document.innerHTML = DOMPurify.sanitize(userInput);
document.textContent = userInput;
Command Injection Prevention
filename="$USER_INPUT"
cat "$filename"
if [[ "$USER_INPUT" =~ ^[a-zA-Z0-9._-]+$ ]]; then
cat "$USER_INPUT"
else
echo "Invalid filename"
exit 1
fi
Input Validation Rules
import { z } from 'zod';
const CreateUserSchema = z.object({
email: z.string().email().max(255),
password: z.string().min(12).max(128),
age: z.number().int().min(0).max(150),
role: z.enum(['user', 'admin']),
});
function createUser(input: unknown): User {
const data = CreateUserSchema.parse(input);
}
2. Authentication & Authorization
Password Storage (NEVER Plain Text)
import bcrypt from 'bcrypt';
db.insert('users', { email, password });
import crypto from 'crypto';
const hash = crypto.createHash('md5').update(password).digest('hex');
const SALT_ROUNDS = 12;
const hashedPassword = await bcrypt.hash(password, SALT_ROUNDS);
db.insert('users', { email, password: hashedPassword });
const isValid = await bcrypt.compare(inputPassword, storedHash);
JWT Security
import jwt from 'jsonwebtoken';
const token = jwt.sign({ userId }, 'secret123');
const secret = process.env.JWT_SECRET;
if (!secret || secret.length < 32) {
throw new Error('JWT_SECRET must be at least 256 bits');
}
const token = jwt.sign(
{ userId },
secret,
{
algorithm: 'HS256',
expiresIn: '15m',
issuer: 'my-app',
audience: 'api.myapp.com',
}
);
try {
const payload = jwt.verify(token, secret, {
algorithms: ['HS256'],
issuer: 'my-app',
audience: 'api.myapp.com',
});
} catch (error) {
throw new UnauthorizedError('Invalid token');
}
Authorization (Access Control)
function deleteUser(req: Request): void {
if (req.body.isAdmin) {
db.deleteUser(req.params.id);
}
}
function deleteUser(req: Request): void {
const currentUser = getUserFromToken(req.headers.authorization);
if (!currentUser.hasRole('admin')) {
throw new ForbiddenError('Admin access required');
}
const targetUserId = req.params.id;
if (currentUser.role !== 'admin' && currentUser.id !== targetUserId) {
throw new ForbiddenError('Cannot delete other users');
}
db.deleteUser(targetUserId);
}
3. Secrets Management
❌ NEVER Hardcode Secrets
const apiKey = 'sk_live_abc123xyz';
const dbPassword = 'mypassword123';
const apiKey = process.env.API_KEY;
const dbPassword = process.env.DB_PASSWORD;
if (!apiKey || !dbPassword) {
throw new Error('Missing required environment variables');
}
Secrets Rotation
const API_KEYS = [
process.env.API_KEY_CURRENT,
process.env.API_KEY_PREVIOUS,
].filter(Boolean);
function validateApiKey(key: string): boolean {
return API_KEYS.includes(key);
}
4. Cryptography
HTTPS Only (No Exceptions)
app.listen(3000);
import https from 'https';
import fs from 'fs';
const options = {
key: fs.readFileSync('private-key.pem'),
cert: fs.readFileSync('certificate.pem'),
};
https.createServer(options, app).listen(443);
import http from 'http';
http.createServer((req, res) => {
res.writeHead(301, { Location: `https://${req.headers.host}${req.url}` });
res.end();
}).listen(80);
Encryption at Rest
import crypto from 'crypto';
const ALGORITHM = 'aes-256-gcm';
const KEY = Buffer.from(process.env.ENCRYPTION_KEY, 'hex');
function encrypt(plaintext: string): string {
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv(ALGORITHM, KEY, iv);
let encrypted = cipher.update(plaintext, 'utf8', 'hex');
encrypted += cipher.final('hex');
const authTag = cipher.getAuthTag();
return `${iv.toString('hex')}:${authTag.toString('hex')}:${encrypted}`;
}
function decrypt(ciphertext: string): string {
const [ivHex, authTagHex, encrypted] = ciphertext.split(':');
const iv = Buffer.from(ivHex, 'hex');
const authTag = Buffer.from(authTagHex, 'hex');
const decipher = crypto.createDecipheriv(ALGORITHM, KEY, iv);
decipher.setAuthTag(authTag);
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
5. Rate Limiting & DoS Prevention
import rateLimit from 'express-rate-limit';
const limiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 100,
message: 'Too many requests, please try again later',
standardHeaders: true,
legacyHeaders: false,
});
app.use('/api/', limiter);
const authLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 5,
skipSuccessfulRequests: true,
});
app.post('/api/login', authLimiter, loginHandler);
6. CORS (Cross-Origin Resource Sharing)
import cors from 'cors';
app.use(cors({ origin: '*' }));
const ALLOWED_ORIGINS = [
'https://app.example.com',
'https://admin.example.com',
];
app.use(cors({
origin: (origin, callback) => {
if (!origin || ALLOWED_ORIGINS.includes(origin)) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
},
credentials: true,
maxAge: 86400,
}));
7. Dependency Security
Keep Dependencies Updated
npm audit
yarn audit
pnpm audit
npm audit fix
npm update
Lock File Integrity
git add package-lock.json
git add yarn.lock
git add pnpm-lock.yaml
npm ci
Minimize Dependencies
import _ from 'lodash';
import { debounce } from 'lodash-es/debounce';
8. Security Headers
import helmet from 'helmet';
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", 'data:', 'https:'],
},
},
hsts: {
maxAge: 31536000,
includeSubDomains: true,
preload: true,
},
}));
app.use((req, res, next) => {
res.setHeader('X-Content-Type-Options', 'nosniff');
res.setHeader('X-Frame-Options', 'DENY');
res.setHeader('X-XSS-Protection', '1; mode=block');
next();
});
9. Logging & Monitoring
import winston from 'winston';
const logger = winston.createLogger({
level: 'info',
format: winston.format.json(),
transports: [
new winston.transports.File({ filename: 'error.log', level: 'error' }),
new winston.transports.File({ filename: 'combined.log' }),
],
});
logger.info('User login', { email, password });
logger.info('User login attempt', {
email,
ip: req.ip,
userAgent: req.headers['user-agent'],
timestamp: new Date().toISOString(),
});
logger.warn('Failed login attempt', { email, ip, attempts: 3 });
logger.error('Suspicious activity detected', { userId, action });
Security Checklist
Before deploying:
Resources