Building Your Learning Module...
Getting things ready for you!
Find videos you like?
Save to resource drawer for future reference!
Secure coding is the practice of writing code that is resistant to attacks and protects sensitive data. It involves following security principles, avoiding common vulnerabilities, and thinking like an attacker.
Grant minimum permissions necessary
Multiple layers of security
Default to secure state on errors
All input is potentially malicious
✅ Sanitize input, use parameterized queries, escape output
✅ Use strong session management, implement MFA, secure password storage
✅ Encrypt data in transit (HTTPS), at rest, don't log sensitive data
✅ Secure defaults, disable debug mode, update dependencies
✅ Verify permissions on server, implement RBAC, deny by default
// ✅ Secure password hashing (bcrypt)
const bcrypt = require('bcrypt');
async function hashPassword(password) {
// Use high salt rounds (10-12)
const saltRounds = 12;
const hash = await bcrypt.hash(password, saltRounds);
return hash;
}
async function verifyPassword(password, hash) {
return await bcrypt.compare(password, hash);
}
// ✅ Secure session management
const session = require('express-session');
app.use(session({
secret: process.env.SESSION_SECRET, // Strong random secret
name: 'sessionId', // Don't use default name
resave: false,
saveUninitialized: false,
cookie: {
secure: true, // HTTPS only
httpOnly: true, // No JavaScript access
maxAge: 3600000, // 1 hour
sameSite: 'strict' // CSRF protection
}
}));
// ✅ Rate limiting to prevent brute force
const rateLimit = require('express-rate-limit');
const loginLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 5, // 5 attempts
message: 'Too many login attempts, try again later'
});
app.post('/login', loginLimiter, async (req, res) => {
const { email, password } = req.body;
// ✅ Always use constant-time comparison
const user = await findUserByEmail(email);
if (!user) {
// ✅ Don't reveal if user exists
return res.status(401).json({ error: 'Invalid credentials' });
}
const isValid = await verifyPassword(password, user.passwordHash);
if (!isValid) {
// ✅ Log failed attempts
logFailedLogin(email, req.ip);
return res.status(401).json({ error: 'Invalid credentials' });
}
// ✅ Regenerate session on login
req.session.regenerate((err) => {
if (err) return res.status(500).json({ error: 'Login failed' });
req.session.userId = user.id;
req.session.save((err) => {
if (err) return res.status(500).json({ error: 'Login failed' });
res.json({ success: true });
});
});
});// ✅ Environment variables for secrets
require('dotenv').config();
const config = {
apiKey: process.env.API_KEY,
dbPassword: process.env.DB_PASSWORD,
jwtSecret: process.env.JWT_SECRET
};
// ❌ DON'T hardcode secrets
// const apiKey = "sk-1234567890abcdef";
// ✅ Encrypt sensitive data at rest
const crypto = require('crypto');
function encrypt(text) {
const algorithm = 'aes-256-gcm';
const key = Buffer.from(process.env.ENCRYPTION_KEY, 'hex');
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv(algorithm, key, iv);
let encrypted = cipher.update(text, 'utf8', 'hex');
encrypted += cipher.final('hex');
const authTag = cipher.getAuthTag();
return {
encrypted,
iv: iv.toString('hex'),
authTag: authTag.toString('hex')
};
}
// ✅ Secure logging - redact sensitive data
function sanitizeLog(data) {
const sensitive = ['password', 'token', 'ssn', 'creditCard'];
const sanitized = { ...data };
for (const key of sensitive) {
if (sanitized[key]) {
sanitized[key] = '[REDACTED]';
}
}
return sanitized;
}
// Usage
const userData = {
email: 'user@example.com',
password: 'secret123',
name: 'John'
};
console.log(sanitizeLog(userData));
// { email: 'user@example.com', password: '[REDACTED]', name: 'John' }
// ✅ Secure token generation
function generateSecureToken() {
return crypto.randomBytes(32).toString('hex');
}
// ✅ JWT with short expiration
const jwt = require('jsonwebtoken');
function createToken(userId) {
return jwt.sign(
{ userId },
process.env.JWT_SECRET,
{ expiresIn: '15m' } // Short-lived tokens
);
}
// ✅ Verify and refresh tokens
function verifyToken(token) {
try {
return jwt.verify(token, process.env.JWT_SECRET);
} catch (error) {
throw new Error('Invalid token');
}
}// ✅ HTTPS only for API calls
const axios = require('axios');
const api = axios.create({
baseURL: 'https://api.example.com', // HTTPS
timeout: 5000,
headers: {
'Content-Type': 'application/json'
}
});
// ✅ Validate SSL certificates
const https = require('https');
const agent = new https.Agent({
rejectUnauthorized: true // Don't allow self-signed certs
});
// ✅ API key in headers, not URL
async function secureAPICall(endpoint, data) {
try {
const response = await api.post(endpoint, data, {
httpsAgent: agent,
headers: {
'Authorization': `Bearer ${process.env.API_KEY}`,
'X-Request-ID': generateSecureToken()
}
});
return response.data;
} catch (error) {
// ✅ Don't expose internal error details
console.error('API call failed:', error.message);
throw new Error('External service unavailable');
}
}
// ✅ Implement request timeout
async function callWithTimeout(promise, timeout = 5000) {
return Promise.race([
promise,
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Request timeout')), timeout)
)
]);
}
// ✅ Validate API responses
function validateAPIResponse(data, schema) {
// Use a validation library like Joi or Zod
const { error, value } = schema.validate(data);
if (error) {
throw new Error('Invalid API response');
}
return value;
}Build security in from the start
Not an afterthought
Multiple security layers
Never rely on one defense
Validate and sanitize everything
Assume all input is malicious
Update dependencies regularly
Follow security advisories