Шифрование и криптография в веб-приложениях

HTTPS, JWT, хеширование паролей и защита данных

Безопасность — не опция. Утечки данных стоят компаниям миллионы. Шифрование, хеширование, JWT, OAuth — основы защиты. Разберем криптографию для веб-разработчиков. Основы криптографии Symmetric vs Asymmetric: Symmetric — один ключ для encrypt и decrypt (AES) Asymmetric — пара ключей public/private (RSA) Хеширование паролей НИКОГДА НЕ ХРАНИТЕ ПАРОЛИ В PLAIN TEXT! // Плохо const user = { email: 'john@example.com', password: 'mypassword123' // НЕТ! }; // Хорошо - bcrypt const bcrypt = require('bcrypt'); const saltRounds = 10; const hashedPassword = await bcrypt.hash(password, saltRounds); const user = { email: 'john@example.com', password: hashedPassword }; // Verify password const isValid = await bcrypt.compare(inputPassword, user.password); Почему bcrypt: Slow by design — защита от brute force Автоматически добавляет salt Адаптивный — можно увеличить rounds JWT (JSON Web Tokens) const jwt = require('jsonwebtoken'); // Create token const token = jwt.sign( { userId: user.id, email: user.email, role: user.role }, process.env.JWT_SECRET, { expiresIn: '24h', issuer: 'myapp.com', audience: 'myapp-users' } ); // Verify token try { const decoded = jwt.verify(token, process.env.JWT_SECRET); console.log(decoded.userId); } catch (error) { console.error('Invalid token'); } JWT Structure: // Header { "alg": "HS256", "typ": "JWT" } // Payload { "userId": 123, "email": "john@example.com", "iat": 1704123456, "exp": 1704209856 } // Signature HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret ) Refresh Tokens // Login const accessToken = jwt.sign({ userId }, SECRET, { expiresIn: '15m' }); const refreshToken = jwt.sign({ userId }, REFRESH_SECRET, { expiresIn: '7d' }); // Store refresh token await db.refreshTokens.create({ userId, token: refreshToken, expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000) }); res.json({ accessToken, refreshToken }); // Refresh endpoint app.post('/auth/refresh', async (req, res) => { const { refreshToken } = req.body; try { const decoded = jwt.verify(refreshToken, REFRESH_SECRET); // Check if token exists in DB const storedToken = await db.refreshTokens.findOne({ userId: decoded.userId, token: refreshToken }); if (!storedToken) { return res.status(401).json({ error: 'Invalid token' }); } // Generate new access token const newAccessToken = jwt.sign( { userId: decoded.userId }, SECRET, { expiresIn: '15m' } ); res.json({ accessToken: newAccessToken }); } catch (error) { res.status(401).json({ error: 'Invalid token' }); } }); HTTPS/TLS const https = require('https'); const fs = require('fs'); const options = { key: fs.readFileSync('private-key.pem'), cert: fs.readFileSync('certificate.pem') }; https.createServer(options, app).listen(443); Let's Encrypt (free SSL): # Certbot sudo certbot --nginx -d example.com -d www.example.com # Auto-renewal sudo certbot renew --dry-run Encryption в Node.js AES-256-GCM: const crypto = require('crypto'); function encrypt(text, key) { const iv = crypto.randomBytes(16); const cipher = crypto.createCipheriv('aes-256-gcm', 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') }; } function decrypt(encrypted, key, iv, authTag) { const decipher = crypto.createDecipheriv( 'aes-256-gcm', key, Buffer.from(iv, 'hex') ); decipher.setAuthTag(Buffer.from(authTag, 'hex')); let decrypted = decipher.update(encrypted, 'hex', 'utf8'); decrypted += decipher.final('utf8'); return decrypted; } const key = crypto.randomBytes(32); // 256 bits const result = encrypt('Secret message', key); const original = decrypt(result.encrypted, key, result.iv, result.authTag); OAuth 2.0 // Google OAuth example const { OAuth2Client } = require('google-auth-library'); const client = new OAuth2Client(CLIENT_ID, CLIENT_SECRET, REDIRECT_URI); // Authorization URL const authUrl = client.generateAuthUrl({ access_type: 'offline', scope: ['profile', 'email'] }); // Handle callback app.get('/auth/google/callback', async (req, res) => { const { code } = req.query; const { tokens } = await client.getToken(code); client.setCredentials(tokens); const ticket = await client.verifyIdToken({ idToken: tokens.id_token, audience: CLIENT_ID }); const payload = ticket.getPayload(); const { email, name, picture } = payload; // Create or login user let user = await db.users.findByEmail(email); if (!user) { user = await db.users.create({ email, name, picture }); } const jwt = generateJWT(user); res.json({ token: jwt }); }); CSRF Protection const csrf = require('csurf'); const csrfProtection = csrf({ cookie: true }); app.get('/form', csrfProtection, (req, res) => { res.render('form', { csrfToken: req.csrfToken() }); }); app.post('/process', csrfProtection, (req, res) => { // Process form }); XSS Protection // Sanitize user input const DOMPurify = require('isomorphic-dompurify'); const dirty = req.body.content; const clean = DOMPurify.sanitize(dirty); // Content Security Policy app.use((req, res, next) => { res.setHeader( 'Content-Security-Policy', "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'" ); next(); }); Secrets Management // .env (NEVER commit to git!) DB_PASSWORD=supersecret JWT_SECRET=anothersecret API_KEY=secret123 // Usage require('dotenv').config(); const dbPassword = process.env.DB_PASSWORD; AWS Secrets Manager: const AWS = require('aws-sdk'); const secretsManager = new AWS.SecretsManager(); async function getSecret(secretName) { const data = await secretsManager.getSecretValue({ SecretId: secretName }).promise(); return JSON.parse(data.SecretString); } const dbCreds = await getSecret('prod/db/credentials'); Rate Limiting const rateLimit = require('express-rate-limit'); const limiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 100, // Max 100 requests per window message: 'Too many requests' }); app.use('/api/', limiter); Best Practices HTTPS everywhere Хешируйте пароли с bcrypt Используйте JWT для stateless auth Rotation secrets регулярно Principle of least privilege Input validation и sanitization Security headers Regular security audits Заключение: Безопасность — постоянная работа. HTTPS обязателен. Хешируйте пароли. JWT для authentication. OAuth для third-party. Защита от XSS и CSRF. Secrets в environment variables. Rate limiting против abuse. Security — ответственность каждого разработчика.