From a99b3599b3fc66e72cf302e640459c8e144ac645 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 2 Jul 2025 16:29:58 +0000 Subject: [PATCH 1/2] Initial plan From 5b6f80d200f00bcba2c77667d9649f62da8e89d3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 2 Jul 2025 16:43:23 +0000 Subject: [PATCH 2/2] Implement comprehensive secure communication layer documentation and examples Co-authored-by: kmock930 <78272416+kmock930@users.noreply.github.com> --- __test__/navBar_expect_result.json | 2 +- src/app/content/navMenu.json | 9 + src/app/security/README.md | 279 +++++++ src/app/security/components/CodeBlock.js | 70 ++ src/app/security/docs/api-security.js | 334 ++++++++ src/app/security/docs/best-practices.js | 456 +++++++++++ src/app/security/docs/data-sanitization.js | 743 ++++++++++++++++++ src/app/security/docs/https-implementation.js | 371 +++++++++ .../api-examples/secureApiExamples.js | 423 ++++++++++ .../examples/secure-forms/ContactForm.js | 221 ++++++ .../examples/testing/securityTests.js | 373 +++++++++ src/app/security/page.js | 106 +++ src/app/security/utils/securityUtils.js | 531 +++++++++++++ 13 files changed, 3917 insertions(+), 1 deletion(-) create mode 100644 src/app/security/README.md create mode 100644 src/app/security/components/CodeBlock.js create mode 100644 src/app/security/docs/api-security.js create mode 100644 src/app/security/docs/best-practices.js create mode 100644 src/app/security/docs/data-sanitization.js create mode 100644 src/app/security/docs/https-implementation.js create mode 100644 src/app/security/examples/api-examples/secureApiExamples.js create mode 100644 src/app/security/examples/secure-forms/ContactForm.js create mode 100644 src/app/security/examples/testing/securityTests.js create mode 100644 src/app/security/page.js create mode 100644 src/app/security/utils/securityUtils.js diff --git a/__test__/navBar_expect_result.json b/__test__/navBar_expect_result.json index 55a7551..9f5ef51 100644 --- a/__test__/navBar_expect_result.json +++ b/__test__/navBar_expect_result.json @@ -1,4 +1,4 @@ [ - ["Home", "Albums", "Contact Me"], + ["Home", "Security", "Albums", "Contact Me"], ["Welcoming", "About Me", {"My Professional Self": ["Resume","Projects","Education","Work Experience","Skills"]}] ] \ No newline at end of file diff --git a/src/app/content/navMenu.json b/src/app/content/navMenu.json index 66508de..abd97ed 100644 --- a/src/app/content/navMenu.json +++ b/src/app/content/navMenu.json @@ -6,6 +6,15 @@ {"My Professional Self": ["Resume", "Projects", "Education", "Work Experience", "Skills"]} ] }, + { + "Security": [ + "Overview", + {"API Security": ["Authentication", "Authorization", "Rate Limiting", "Input Validation"]}, + {"HTTPS Implementation": ["SSL/TLS Setup", "Certificate Management", "Security Headers"]}, + {"Data Sanitization": ["Input Sanitization", "Output Encoding", "XSS Prevention"]}, + {"Best Practices": ["Secure Coding", "Security Testing", "Common Vulnerabilities"]} + ] + }, { "Albums": [ {"Childhood": ["School", "Family"]}, diff --git a/src/app/security/README.md b/src/app/security/README.md new file mode 100644 index 0000000..f406a13 --- /dev/null +++ b/src/app/security/README.md @@ -0,0 +1,279 @@ +# Secure Communication Layer Documentation + +## Overview + +This comprehensive guide provides practical security implementations for developers with limited security experience. It covers essential practices for secure frontend-backend communication, including API security, HTTPS implementation, and data sanitization. + +## Quick Start + +### 1. Install Required Dependencies + +```bash +# Frontend dependencies +npm install isomorphic-dompurify xss + +# Backend dependencies (Express.js example) +npm install express helmet express-rate-limit express-validator cors +npm install jsonwebtoken bcryptjs joi +``` + +### 2. Basic Security Setup + +```javascript +// Import security utilities +import { InputSanitizer, SecureApiClient, ErrorHandler } from './security/utils/securityUtils'; + +// Create secure API client +const api = new SecureApiClient('https://api.yourdomain.com'); + +// Sanitize user input +const cleanInput = InputSanitizer.sanitizeString(userInput, { + removeHtml: true, + removeScripts: true, + maxLength: 1000 +}); +``` + +## Security Components + +### 🔐 API Security +- **Authentication & Authorization**: JWT tokens, OAuth 2.0, API keys +- **Input Validation**: Server-side validation with Joi/express-validator +- **Rate Limiting**: Prevent abuse and DDoS attacks +- **Error Handling**: Secure error responses + +### 🔒 HTTPS Implementation +- **SSL/TLS Setup**: Let's Encrypt, certificate management +- **Security Headers**: HSTS, CSP, X-Frame-Options +- **Certificate Monitoring**: Automated renewal and alerts + +### 🛡️ Data Sanitization +- **Input Sanitization**: XSS prevention, HTML encoding +- **Output Encoding**: Safe content rendering +- **Content Security Policy**: Browser-level protection + +## File Structure + +``` +src/app/security/ +├── components/ +│ └── CodeBlock.js # Reusable code display component +├── docs/ +│ ├── api-security.js # API security guidelines +│ ├── https-implementation.js # HTTPS setup guide +│ └── data-sanitization.js # Data sanitization guide +├── utils/ +│ └── securityUtils.js # Security utility functions +├── examples/ +│ ├── secure-forms/ # Example secure form implementations +│ ├── api-examples/ # API security examples +│ └── testing/ # Security testing examples +└── page.js # Main security overview page +``` + +## Usage Examples + +### Secure Form Handling + +```javascript +import { InputSanitizer, InputValidator } from './security/utils/securityUtils'; + +const handleFormSubmit = async (formData) => { + // Validate input + if (!InputValidator.isEmail(formData.email)) { + throw new Error('Invalid email format'); + } + + // Sanitize input + const sanitizedData = { + name: InputSanitizer.sanitizeString(formData.name, { removeHtml: true }), + email: InputSanitizer.sanitizeEmail(formData.email), + message: InputSanitizer.sanitizeString(formData.message, { removeScripts: true }) + }; + + // Send to API + const response = await api.post('/contact', sanitizedData); + return response; +}; +``` + +### Secure API Client Usage + +```javascript +import { SecureApiClient } from './security/utils/securityUtils'; + +const api = new SecureApiClient('https://api.example.com', { + timeout: 10000, + retryAttempts: 3 +}); + +// Authenticated request +api.setAuthToken('your-jwt-token'); +const userData = await api.get('/user/profile'); + +// Handle errors +try { + await api.post('/sensitive-data', data); +} catch (error) { + const errorInfo = ErrorHandler.handleApiError(error); + console.log(errorInfo.userMessage); +} +``` + +## Security Checklist + +### Frontend Security +- [ ] Input validation on all forms +- [ ] XSS prevention with proper encoding +- [ ] HTTPS enforced for all requests +- [ ] Content Security Policy implemented +- [ ] Sensitive data not stored in localStorage +- [ ] API rate limiting on client side + +### Backend Security +- [ ] Input validation with schemas (Joi/Zod) +- [ ] Authentication middleware on protected routes +- [ ] Rate limiting implemented +- [ ] CORS properly configured +- [ ] Security headers configured +- [ ] Parameterized database queries +- [ ] Error handling without information disclosure + +### Infrastructure Security +- [ ] SSL certificate installed and auto-renewing +- [ ] Security headers configured (HSTS, CSP, etc.) +- [ ] Regular security updates +- [ ] Monitoring and alerting set up +- [ ] Backup and recovery procedures + +## Common Vulnerabilities & Prevention + +### Cross-Site Scripting (XSS) +```javascript +// ❌ Dangerous - direct HTML insertion +element.innerHTML = userInput; + +// ✅ Safe - use React's built-in escaping +return
{userInput}
; + +// ✅ Safe - sanitize if HTML is needed +import DOMPurify from 'dompurify'; +const clean = DOMPurify.sanitize(userInput); +``` + +### SQL Injection +```javascript +// ❌ Dangerous - string concatenation +const query = `SELECT * FROM users WHERE id = ${userId}`; + +// ✅ Safe - parameterized queries +const query = 'SELECT * FROM users WHERE id = $1'; +const result = await pool.query(query, [userId]); +``` + +### Cross-Site Request Forgery (CSRF) +```javascript +// ✅ CSRF protection with SameSite cookies +app.use(session({ + cookie: { + sameSite: 'strict', + secure: true, // HTTPS only + httpOnly: true + } +})); +``` + +## Environment Configuration + +### Development +```javascript +// next.config.js (development) +module.exports = { + async headers() { + return [{ + source: '/(.*)', + headers: [ + { key: 'X-Frame-Options', value: 'SAMEORIGIN' }, + // Relaxed CSP for development + { key: 'Content-Security-Policy', value: "default-src 'self' 'unsafe-inline'" } + ] + }]; + } +}; +``` + +### Production +```javascript +// next.config.js (production) +const strictCSP = "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'"; + +module.exports = { + async headers() { + return [{ + source: '/(.*)', + headers: [ + { key: 'Strict-Transport-Security', value: 'max-age=31536000; includeSubDomains; preload' }, + { key: 'X-Frame-Options', value: 'DENY' }, + { key: 'Content-Security-Policy', value: strictCSP }, + { key: 'X-Content-Type-Options', value: 'nosniff' } + ] + }]; + } +}; +``` + +## Testing Security + +### Automated Security Testing +```javascript +// Example security test +describe('Security Tests', () => { + test('should sanitize XSS attempts', () => { + const maliciousInput = ''; + const sanitized = InputSanitizer.sanitizeString(maliciousInput, { removeScripts: true }); + expect(sanitized).not.toContain('` in forms +2. **SQL Injection**: Test with `'; DROP TABLE users; --` +3. **CSRF Testing**: Make requests from different origins +4. **Rate Limiting**: Send rapid requests to test limits + +## Resources & Further Reading + +### Standards & Guidelines +- [OWASP Top 10](https://owasp.org/www-project-top-ten/) +- [OWASP Cheat Sheets](https://cheatsheetseries.owasp.org/) +- [Mozilla Web Security Guidelines](https://infosec.mozilla.org/guidelines/web_security) + +### Tools for Security Testing +- [OWASP ZAP](https://www.zaproxy.org/) - Security scanner +- [Snyk](https://snyk.io/) - Vulnerability scanning +- [SecurityHeaders.com](https://securityheaders.com/) - Header analysis +- [SSL Labs](https://www.ssllabs.com/ssltest/) - SSL configuration testing + +### Libraries & Frameworks +- [Helmet.js](https://helmetjs.github.io/) - Express security middleware +- [DOMPurify](https://github.com/cure53/DOMPurify) - XSS sanitizer +- [Joi](https://joi.dev/) - Input validation +- [express-rate-limit](https://github.com/nfriedly/express-rate-limit) - Rate limiting + +## Contributing + +Found a security issue or want to improve the documentation? Please: + +1. **For security vulnerabilities**: Report privately to the maintainers +2. **For improvements**: Submit a pull request with your changes +3. **For questions**: Open an issue for discussion + +## License + +This security documentation and utilities are provided under the MIT License. Use at your own risk and always perform thorough security testing in your specific environment. \ No newline at end of file diff --git a/src/app/security/components/CodeBlock.js b/src/app/security/components/CodeBlock.js new file mode 100644 index 0000000..968572e --- /dev/null +++ b/src/app/security/components/CodeBlock.js @@ -0,0 +1,70 @@ +import React, { useState } from 'react'; +import { Box, Typography, IconButton, Tooltip } from '@mui/material'; +import ContentCopyIcon from '@mui/icons-material/ContentCopy'; +import CheckIcon from '@mui/icons-material/Check'; + +const CodeBlock = ({ code, language = 'javascript', title }) => { + const [copied, setCopied] = useState(false); + + const handleCopy = async () => { + try { + await navigator.clipboard.writeText(code); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + } catch (err) { + console.error('Failed to copy code:', err); + } + }; + + return ( + + {title && ( + + {title} + + )} + + + + {copied ? : } + + +
+          {code}
+        
+
+
+ ); +}; + +export default CodeBlock; \ No newline at end of file diff --git a/src/app/security/docs/api-security.js b/src/app/security/docs/api-security.js new file mode 100644 index 0000000..c19adcc --- /dev/null +++ b/src/app/security/docs/api-security.js @@ -0,0 +1,334 @@ +import React from 'react'; +import { Container, Typography, Paper, Box, Alert, Accordion, AccordionSummary, AccordionDetails, Divider } from '@mui/material'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import CodeBlock from '../components/CodeBlock'; + +export default function ApiSecurity() { + return ( + + + API Security Guidelines + + + + API security is critical for protecting your application and user data. Implement multiple layers of security. + + + + }> + Authentication & Authorization + + + + Always authenticate users and authorize access to resources. Never trust client-side validation alone. + + + + JWT Token Implementation + { + const token = localStorage.getItem('authToken'); + + const response = await fetch(\`\${API_BASE_URL}\${endpoint}\`, { + ...options, + headers: { + 'Content-Type': 'application/json', + 'Authorization': \`Bearer \${token}\`, + ...options.headers, + }, + }); + + if (response.status === 401) { + // Token expired, redirect to login + localStorage.removeItem('authToken'); + window.location.href = '/login'; + return; + } + + if (!response.ok) { + throw new Error(\`API Error: \${response.status}\`); + } + + return response.json(); +}; + +// Usage example +try { + const userData = await apiCall('/api/user/profile'); + console.log(userData); +} catch (error) { + console.error('Failed to fetch user data:', error); +}`} /> + + + + Backend Token Validation + { + const authHeader = req.headers['authorization']; + const token = authHeader && authHeader.split(' ')[1]; + + if (!token) { + return res.status(401).json({ error: 'Access token required' }); + } + + jwt.verify(token, process.env.JWT_SECRET, (err, user) => { + if (err) { + return res.status(403).json({ error: 'Invalid or expired token' }); + } + + req.user = user; + next(); + }); +}; + +// Protected route example +app.get('/api/user/profile', authenticateToken, (req, res) => { + // User is authenticated, req.user contains user info + res.json({ + id: req.user.id, + email: req.user.email + }); +});`} /> + + + + + + }> + Input Validation & Sanitization + + + + Always validate and sanitize user input on both client and server sides. + + + + Frontend Input Validation + { + if (typeof input !== 'string') return input; + + return input + .trim() + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, ''') + .replace(/\\//g, '/'); +}; + +export const validateEmail = (email) => { + const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/; + return emailRegex.test(email); +}; + +// React form validation example +const ContactForm = () => { + const [formData, setFormData] = useState({ email: '', message: '' }); + const [errors, setErrors] = useState({}); + + const handleSubmit = async (e) => { + e.preventDefault(); + + const newErrors = {}; + + if (!validateEmail(formData.email)) { + newErrors.email = 'Please enter a valid email address'; + } + + if (formData.message.length < 10) { + newErrors.message = 'Message must be at least 10 characters'; + } + + if (Object.keys(newErrors).length > 0) { + setErrors(newErrors); + return; + } + + const sanitizedData = { + email: sanitizeInput(formData.email), + message: sanitizeInput(formData.message) + }; + + try { + await apiCall('/api/contact', { + method: 'POST', + body: JSON.stringify(sanitizedData) + }); + } catch (error) { + console.error('Submission failed:', error); + } + }; +};`} /> + + + + Backend Validation with Joi + { + return (req, res, next) => { + const { error } = schema.validate(req.body); + + if (error) { + return res.status(400).json({ + error: 'Validation failed', + details: error.details.map(detail => detail.message) + }); + } + + next(); + }; +}; + +// Usage in route +app.post('/api/contact', + validateInput(contactSchema), + (req, res) => { + // Input is validated and safe to process + const { email, message, name } = req.body; + + // Additional server-side sanitization if needed + const sanitizedMessage = DOMPurify.sanitize(message); + + // Process the request... + res.json({ success: true }); + } +);`} /> + + + + + + }> + Rate Limiting & Protection + + + + Implement rate limiting to prevent abuse and DDoS attacks. + + + + Express Rate Limiting + + + + + + + }> + Error Handling & Logging + + + + Proper error handling prevents information disclosure while maintaining good user experience. + + + + Secure Error Handling + { + // Log error details for debugging (remove in production) + console.error('API Error:', error); + + let userMessage = 'An unexpected error occurred. Please try again.'; + + if (error.status === 400) { + userMessage = 'Please check your input and try again.'; + } else if (error.status === 401) { + userMessage = 'Please log in to continue.'; + // Redirect to login + } else if (error.status === 403) { + userMessage = 'You do not have permission to perform this action.'; + } else if (error.status === 429) { + userMessage = 'Too many requests. Please wait before trying again.'; + } + + if (showUserMessage) { + // Show user-friendly message (using your preferred notification system) + showNotification(userMessage, 'error'); + } + + return { userMessage, originalError: error }; +}; + +// Backend error handling middleware +app.use((error, req, res, next) => { + // Log error details for monitoring + console.error('Server Error:', { + message: error.message, + stack: error.stack, + url: req.url, + method: req.method, + ip: req.ip, + userAgent: req.get('User-Agent'), + timestamp: new Date().toISOString() + }); + + // Don't leak sensitive information + const isDevelopment = process.env.NODE_ENV === 'development'; + + res.status(error.status || 500).json({ + error: error.message || 'Internal server error', + ...(isDevelopment && { stack: error.stack }) + }); +});`} /> + + + + + + Key Takeaways + + • Always authenticate and authorize on the server side
+ • Validate and sanitize all input data
+ • Implement rate limiting for all public endpoints
+ • Use secure error handling that doesn't leak sensitive information
+ • Regularly update dependencies and monitor for vulnerabilities +
+
+
+ ); +} \ No newline at end of file diff --git a/src/app/security/docs/best-practices.js b/src/app/security/docs/best-practices.js new file mode 100644 index 0000000..23c0190 --- /dev/null +++ b/src/app/security/docs/best-practices.js @@ -0,0 +1,456 @@ +import React from 'react'; +import { Container, Typography, Paper, Box, Alert, List, ListItem, ListItemText, Chip, Divider, Grid } from '@mui/material'; +import SecurityIcon from '@mui/icons-material/Security'; +import CheckCircleIcon from '@mui/icons-material/CheckCircle'; +import WarningIcon from '@mui/icons-material/Warning'; +import CodeBlock from '../components/CodeBlock'; + +export default function BestPractices() { + return ( + + + + + Security Best Practices + + + + + Following these best practices will significantly improve your application's security posture. + + + + {/* Frontend Best Practices */} + + + + Frontend Security + + + + + + Do's + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Don'ts + + + + + + + + + + + + + + + + + + + + + + + {/* Backend Best Practices */} + + + + Backend Security + + + + + + Do's + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Don'ts + + + + + + + + + + + + + + + + + + + + + + + + {/* Security Implementation Checklist */} + + + Security Implementation Checklist + + + + + Authentication & Authorization + + + + + + + + + + + + + + + + + + + + + + Data Protection + + + + + + + + + + + + + + + + + + + + + + Infrastructure + + + + + + + + + + + + + + + + + + + + + + + {/* Code Examples */} + + + Quick Reference Code Examples + + + + Secure API Request + { + try { + // Validate and sanitize data before sending + const validatedData = validateFormData(formData); + const response = await api.post('/contact', validatedData); + + return { success: true, data: response }; + } catch (error) { + const errorInfo = ErrorHandler.handleApiError(error); + return { success: false, error: errorInfo.userMessage }; + } +};`} /> + + + + Input Sanitization + { + switch (type) { + case 'email': + return InputSanitizer.sanitizeEmail(input); + case 'url': + return InputSanitizer.sanitizeUrl(input); + case 'html': + return InputSanitizer.sanitizeString(input, { + removeScripts: true, + removeHtml: true + }); + default: + return InputSanitizer.sanitizeString(input, { + removeScripts: true, + maxLength: 1000 + }); + } +}; + +// Validate before processing +if (InputValidator.isEmail(email) && InputValidator.hasMinLength(message, 10)) { + // Process the validated data +}`} /> + + + + Secure Backend Route + { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + error: 'Validation failed', + details: errors.array() + }); + } + + // Process validated data + processContactForm(req.body); + res.json({ success: true }); + } +);`} /> + + + + {/* Security Testing */} + + + Security Testing Guidelines + + + + Regular security testing is crucial for maintaining a secure application. Here are key areas to focus on: + + + + Automated Security Testing + { + test('should prevent XSS attacks', () => { + const maliciousInput = ''; + const sanitized = sanitizeInput(maliciousInput); + expect(sanitized).not.toContain(' + + + ); +} + +function generateNonce() { + return Buffer.from(crypto.randomUUID()).toString('base64'); +}`} /> + + + + + + Data Sanitization Checklist + + ✓ Validate all input on both client and server
+ ✓ Sanitize data before storing in database
+ ✓ Encode output when displaying user content
+ ✓ Use parameterized queries for database operations
+ ✓ Implement Content Security Policy
+ ✓ Regular security testing and updates
+ ✓ Monitor for unusual patterns and potential attacks +
+
+
+ ); +} \ No newline at end of file diff --git a/src/app/security/docs/https-implementation.js b/src/app/security/docs/https-implementation.js new file mode 100644 index 0000000..32d91b1 --- /dev/null +++ b/src/app/security/docs/https-implementation.js @@ -0,0 +1,371 @@ +import React from 'react'; +import { Container, Typography, Paper, Box, Alert, Accordion, AccordionSummary, AccordionDetails, List, ListItem, ListItemText } from '@mui/material'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import CodeBlock from '../components/CodeBlock'; + +export default function HttpsImplementation() { + return ( + + + HTTPS Implementation Guide + + + + Never serve a production application over HTTP. HTTPS is essential for protecting user data and maintaining trust. + + + + }> + SSL/TLS Certificate Setup + + + + Setting up SSL certificates is the first step to enabling HTTPS. Here are common approaches: + + + + Production Setup with Let's Encrypt (Free) + + + + + Nginx Configuration with SSL + + + + + Development Setup with mkcert + + + + + Next.js with Custom Server for HTTPS + { + createServer(httpsOptions, async (req, res) => { + try { + const parsedUrl = parse(req.url, true); + await handle(req, res, parsedUrl); + } catch (err) { + console.error('Error occurred handling', req.url, err); + res.statusCode = 500; + res.end('internal server error'); + } + }).listen(port, (err) => { + if (err) throw err; + console.log(\`> Ready on https://\${hostname}:\${port}\`); + }); +}); + +// package.json script +// "dev:https": "node server.js"`} /> + + + + + + }> + Security Headers Configuration + + + + Security headers provide additional protection against various attacks. Here's how to implement them: + + + + Next.js Security Headers + + + + + Express.js Security Headers with Helmet + { + res.setHeader('X-API-Version', '1.0'); + res.setHeader('X-Powered-By', 'Custom-Server'); // Override default + next(); +});`} /> + + + + + Security Headers Explanation:
+ • HSTS: Forces HTTPS connections
+ • X-Frame-Options: Prevents clickjacking
+ • X-Content-Type-Options: Prevents MIME sniffing
+ • CSP: Prevents XSS attacks
+ • Referrer-Policy: Controls referrer information +
+
+
+
+ + + }> + Certificate Management & Monitoring + + + + Proper certificate management ensures your HTTPS setup remains secure and functional. + + + + Certificate Monitoring Script + /dev/null | openssl x509 -noout -dates | grep 'notAfter' | cut -d= -f2) + +# Convert to epoch time +EXPIRY_EPOCH=$(date -d "$EXPIRY_DATE" +%s) +CURRENT_EPOCH=$(date +%s) + +# Calculate days until expiry +DAYS_UNTIL_EXPIRY=$(( ($EXPIRY_EPOCH - $CURRENT_EPOCH) / 86400 )) + +echo "SSL certificate for $DOMAIN expires in $DAYS_UNTIL_EXPIRY days" + +if [ $DAYS_UNTIL_EXPIRY -lt $DAYS_BEFORE_EXPIRY ]; then + echo "WARNING: Certificate expires soon!" + # Send alert (email, Slack, etc.) + # curl -X POST -H 'Content-type: application/json' --data '{"text":"SSL certificate for '$DOMAIN' expires in '$DAYS_UNTIL_EXPIRY' days!"}' YOUR_SLACK_WEBHOOK_URL +fi + +# Add to crontab to run daily: +# 0 9 * * * /path/to/check-ssl.sh`} language="bash" /> + + + + Health Check Endpoint + h), + recommendations: generateRecommendations(checks) + } + }); +} + +function generateRecommendations(checks) { + const recommendations = []; + + if (!checks.ssl.enabled) { + recommendations.push('Enable HTTPS for all traffic'); + } + + if (!checks.ssl.headers.hsts) { + recommendations.push('Add HSTS header for better security'); + } + + if (!checks.ssl.headers.csp) { + recommendations.push('Implement Content Security Policy'); + } + + return recommendations; +}`} /> + + + + + + HTTPS Checklist + + + + + + + + + + + + + + + + + + + + + +
+ ); +} \ No newline at end of file diff --git a/src/app/security/examples/api-examples/secureApiExamples.js b/src/app/security/examples/api-examples/secureApiExamples.js new file mode 100644 index 0000000..ee1a439 --- /dev/null +++ b/src/app/security/examples/api-examples/secureApiExamples.js @@ -0,0 +1,423 @@ +/** + * Backend API Security Examples + * + * These examples demonstrate secure API implementations + * for common use cases with proper validation, sanitization, + * and security measures. + */ + +const express = require('express'); +const rateLimit = require('express-rate-limit'); +const helmet = require('helmet'); +const { body, validationResult } = require('express-validator'); +const jwt = require('jsonwebtoken'); +const bcrypt = require('bcryptjs'); +const cors = require('cors'); + +const app = express(); + +// Security middleware +app.use(helmet({ + contentSecurityPolicy: { + directives: { + defaultSrc: ["'self'"], + scriptSrc: ["'self'"], + styleSrc: ["'self'", "'unsafe-inline'"], + }, + }, +})); + +app.use(cors({ + origin: process.env.ALLOWED_ORIGINS?.split(',') || ['http://localhost:3000'], + credentials: true, +})); + +app.use(express.json({ limit: '10mb' })); + +// Rate limiting +const generalLimiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 100, // limit each IP to 100 requests per windowMs + message: { + error: 'Too many requests from this IP, please try again later.' + } +}); + +const authLimiter = rateLimit({ + windowMs: 15 * 60 * 1000, + max: 5, // 5 attempts per 15 minutes + skipSuccessfulRequests: true, +}); + +app.use(generalLimiter); +app.use('/api/auth', authLimiter); + +// JWT Authentication Middleware +const authenticateToken = (req, res, next) => { + const authHeader = req.headers['authorization']; + const token = authHeader && authHeader.split(' ')[1]; + + if (!token) { + return res.status(401).json({ error: 'Access token required' }); + } + + jwt.verify(token, process.env.JWT_SECRET, (err, user) => { + if (err) { + return res.status(403).json({ error: 'Invalid or expired token' }); + } + + req.user = user; + next(); + }); +}; + +// Input sanitization middleware +const sanitizeInput = (req, res, next) => { + const DOMPurify = require('isomorphic-dompurify'); + + const sanitize = (obj) => { + if (typeof obj === 'string') { + return DOMPurify.sanitize(obj, { + ALLOWED_TAGS: [], + ALLOWED_ATTR: [] + }).trim(); + } + + if (Array.isArray(obj)) { + return obj.map(sanitize); + } + + if (obj && typeof obj === 'object') { + const sanitized = {}; + for (const [key, value] of Object.entries(obj)) { + const cleanKey = key.replace(/[^a-zA-Z0-9_]/g, ''); + sanitized[cleanKey] = sanitize(value); + } + return sanitized; + } + + return obj; + }; + + if (req.body) req.body = sanitize(req.body); + if (req.query) req.query = sanitize(req.query); + if (req.params) req.params = sanitize(req.params); + + next(); +}; + +// Validation schemas +const registerValidation = [ + body('email') + .isEmail() + .withMessage('Must be a valid email') + .normalizeEmail(), + body('password') + .isLength({ min: 8 }) + .withMessage('Password must be at least 8 characters') + .matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]/) + .withMessage('Password must contain uppercase, lowercase, number and special character'), + body('name') + .isLength({ min: 2, max: 50 }) + .withMessage('Name must be between 2 and 50 characters') + .matches(/^[a-zA-Z\s]+$/) + .withMessage('Name can only contain letters and spaces') +]; + +const contactValidation = [ + body('name') + .isLength({ min: 2, max: 50 }) + .withMessage('Name must be between 2 and 50 characters') + .matches(/^[a-zA-Z\s]+$/) + .withMessage('Name can only contain letters and spaces'), + body('email') + .isEmail() + .withMessage('Must be a valid email') + .normalizeEmail(), + body('message') + .isLength({ min: 10, max: 1000 }) + .withMessage('Message must be between 10 and 1000 characters'), + body('website') + .optional({ checkFalsy: true }) + .isURL({ protocols: ['http', 'https'] }) + .withMessage('Must be a valid URL') +]; + +// Error handling middleware +const handleValidationErrors = (req, res, next) => { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + error: 'Validation failed', + details: errors.array() + }); + } + next(); +}; + +// EXAMPLE 1: User Registration with Security +app.post('/api/auth/register', + sanitizeInput, + registerValidation, + handleValidationErrors, + async (req, res) => { + try { + const { email, password, name } = req.body; + + // Check if user already exists + const existingUser = await getUserByEmail(email); + if (existingUser) { + return res.status(409).json({ error: 'User already exists' }); + } + + // Hash password + const saltRounds = 12; + const hashedPassword = await bcrypt.hash(password, saltRounds); + + // Create user + const user = await createUser({ + email, + password: hashedPassword, + name, + createdAt: new Date(), + isVerified: false + }); + + // Generate JWT + const token = jwt.sign( + { id: user.id, email: user.email }, + process.env.JWT_SECRET, + { expiresIn: '24h' } + ); + + res.status(201).json({ + message: 'User created successfully', + token, + user: { + id: user.id, + email: user.email, + name: user.name + } + }); + + } catch (error) { + console.error('Registration error:', error); + res.status(500).json({ error: 'Internal server error' }); + } + } +); + +// EXAMPLE 2: Secure Login +app.post('/api/auth/login', + sanitizeInput, + [ + body('email').isEmail().normalizeEmail(), + body('password').notEmpty() + ], + handleValidationErrors, + async (req, res) => { + try { + const { email, password } = req.body; + + // Get user + const user = await getUserByEmail(email); + if (!user) { + return res.status(401).json({ error: 'Invalid credentials' }); + } + + // Verify password + const isValidPassword = await bcrypt.compare(password, user.password); + if (!isValidPassword) { + return res.status(401).json({ error: 'Invalid credentials' }); + } + + // Generate JWT + const token = jwt.sign( + { id: user.id, email: user.email }, + process.env.JWT_SECRET, + { expiresIn: '24h' } + ); + + res.json({ + message: 'Login successful', + token, + user: { + id: user.id, + email: user.email, + name: user.name + } + }); + + } catch (error) { + console.error('Login error:', error); + res.status(500).json({ error: 'Internal server error' }); + } + } +); + +// EXAMPLE 3: Protected Route +app.get('/api/user/profile', + authenticateToken, + async (req, res) => { + try { + const user = await getUserById(req.user.id); + if (!user) { + return res.status(404).json({ error: 'User not found' }); + } + + res.json({ + id: user.id, + email: user.email, + name: user.name, + createdAt: user.createdAt, + lastLogin: user.lastLogin + }); + + } catch (error) { + console.error('Profile fetch error:', error); + res.status(500).json({ error: 'Internal server error' }); + } + } +); + +// EXAMPLE 4: Contact Form with Security +app.post('/api/contact', + sanitizeInput, + contactValidation, + handleValidationErrors, + async (req, res) => { + try { + const { name, email, message, website } = req.body; + + // Additional security checks + const blockedDomains = ['tempmail.com', '10minutemail.com']; + const emailDomain = email.split('@')[1]; + + if (blockedDomains.includes(emailDomain)) { + return res.status(400).json({ error: 'Email domain not allowed' }); + } + + // Spam detection + const spamPatterns = [ + /viagra/i, /casino/i, /lottery/i, /winner/i, + /congratulations/i, /million dollars/i + ]; + + const content = `${name} ${message}`; + if (spamPatterns.some(pattern => pattern.test(content))) { + console.log('Potential spam detected:', { email, name, ip: req.ip }); + return res.status(400).json({ error: 'Message appears to be spam' }); + } + + // Store message + const contactMessage = await saveContactMessage({ + name, + email, + message, + website, + timestamp: new Date(), + ip: req.ip, + userAgent: req.get('User-Agent') + }); + + res.json({ + success: true, + message: 'Contact form submitted successfully', + id: contactMessage.id + }); + + } catch (error) { + console.error('Contact form error:', error); + res.status(500).json({ error: 'Internal server error' }); + } + } +); + +// EXAMPLE 5: File Upload with Security +const multer = require('multer'); +const path = require('path'); + +const upload = multer({ + dest: 'uploads/', + limits: { + fileSize: 5 * 1024 * 1024, // 5MB limit + }, + fileFilter: (req, file, cb) => { + // Allow only specific file types + const allowedTypes = /jpeg|jpg|png|gif|pdf|doc|docx/; + const extname = allowedTypes.test(path.extname(file.originalname).toLowerCase()); + const mimetype = allowedTypes.test(file.mimetype); + + if (mimetype && extname) { + return cb(null, true); + } else { + cb(new Error('Invalid file type')); + } + } +}); + +app.post('/api/upload', + authenticateToken, + upload.single('file'), + (req, res) => { + if (!req.file) { + return res.status(400).json({ error: 'No file uploaded' }); + } + + // Additional security: scan file content if needed + // const fileContent = fs.readFileSync(req.file.path); + // if (containsMaliciousContent(fileContent)) { + // fs.unlinkSync(req.file.path); + // return res.status(400).json({ error: 'File rejected' }); + // } + + res.json({ + message: 'File uploaded successfully', + filename: req.file.filename, + originalName: req.file.originalname, + size: req.file.size + }); + } +); + +// Global error handler +app.use((error, req, res, next) => { + console.error('Server Error:', { + message: error.message, + stack: error.stack, + url: req.url, + method: req.method, + ip: req.ip, + userAgent: req.get('User-Agent'), + timestamp: new Date().toISOString() + }); + + const isDevelopment = process.env.NODE_ENV === 'development'; + + res.status(error.status || 500).json({ + error: error.message || 'Internal server error', + ...(isDevelopment && { stack: error.stack }) + }); +}); + +// Database functions (implement with your preferred database) +async function getUserByEmail(email) { + // Implement with your database + // Example with PostgreSQL: + // const result = await pool.query('SELECT * FROM users WHERE email = $1', [email]); + // return result.rows[0]; +} + +async function getUserById(id) { + // Implement with your database +} + +async function createUser(userData) { + // Implement with your database +} + +async function saveContactMessage(messageData) { + // Implement with your database +} + +module.exports = app; \ No newline at end of file diff --git a/src/app/security/examples/secure-forms/ContactForm.js b/src/app/security/examples/secure-forms/ContactForm.js new file mode 100644 index 0000000..9944100 --- /dev/null +++ b/src/app/security/examples/secure-forms/ContactForm.js @@ -0,0 +1,221 @@ +import React, { useState } from 'react'; +import { TextField, Button, Box, Alert, CircularProgress } from '@mui/material'; +import { InputSanitizer, InputValidator, SecureApiClient } from '../utils/securityUtils'; + +const SecureContactForm = () => { + const [formData, setFormData] = useState({ + name: '', + email: '', + message: '', + website: '' + }); + const [errors, setErrors] = useState({}); + const [isSubmitting, setIsSubmitting] = useState(false); + const [submitStatus, setSubmitStatus] = useState(null); + + const api = new SecureApiClient('/api'); + + const validateField = (name, value) => { + let error = ''; + + switch (name) { + case 'name': + if (!InputValidator.hasMinLength(value, 2)) { + error = 'Name must be at least 2 characters'; + } else if (!InputValidator.hasMaxLength(value, 50)) { + error = 'Name must be less than 50 characters'; + } + break; + + case 'email': + if (!value) { + error = 'Email is required'; + } else if (!InputValidator.isEmail(value)) { + error = 'Please enter a valid email address'; + } + break; + + case 'message': + if (!InputValidator.hasMinLength(value, 10)) { + error = 'Message must be at least 10 characters'; + } else if (!InputValidator.hasMaxLength(value, 1000)) { + error = 'Message must be less than 1000 characters'; + } + break; + + case 'website': + if (value && !InputValidator.isUrl(value)) { + error = 'Please enter a valid URL'; + } + break; + } + + return error; + }; + + const handleInputChange = (e) => { + const { name, value } = e.target; + + // Real-time sanitization + let sanitizedValue = value; + + switch (name) { + case 'name': + sanitizedValue = InputSanitizer.sanitizeString(value, { + removeHtml: true, + removeScripts: true, + maxLength: 50 + }); + break; + + case 'email': + sanitizedValue = InputSanitizer.sanitizeEmail(value); + break; + + case 'message': + sanitizedValue = InputSanitizer.sanitizeString(value, { + removeScripts: true, + maxLength: 1000 + }); + break; + + case 'website': + if (value) { + sanitizedValue = InputSanitizer.sanitizeUrl(value); + } + break; + } + + setFormData(prev => ({ + ...prev, + [name]: sanitizedValue + })); + + // Real-time validation + const error = validateField(name, sanitizedValue); + setErrors(prev => ({ + ...prev, + [name]: error + })); + }; + + const handleSubmit = async (e) => { + e.preventDefault(); + setIsSubmitting(true); + setSubmitStatus(null); + + // Final validation + const validationErrors = {}; + + Object.keys(formData).forEach(key => { + const error = validateField(key, formData[key]); + if (error) { + validationErrors[key] = error; + } + }); + + if (Object.keys(validationErrors).length > 0) { + setErrors(validationErrors); + setIsSubmitting(false); + return; + } + + try { + // Submit with additional sanitization + const sanitizedData = { + name: InputSanitizer.sanitizeString(formData.name, { removeHtml: true }), + email: InputSanitizer.sanitizeEmail(formData.email), + message: InputSanitizer.sanitizeString(formData.message, { removeScripts: true }), + website: formData.website ? InputSanitizer.sanitizeUrl(formData.website) : '' + }; + + await api.post('/contact', sanitizedData); + + setSubmitStatus({ type: 'success', message: 'Message sent successfully!' }); + setFormData({ name: '', email: '', message: '', website: '' }); + setErrors({}); + } catch (error) { + setSubmitStatus({ + type: 'error', + message: 'Failed to send message. Please try again.' + }); + } finally { + setIsSubmitting(false); + } + }; + + return ( + +

Secure Contact Form Example

+ + {submitStatus && ( + + {submitStatus.message} + + )} + + + + + + + + + + +
+ ); +}; + +export default SecureContactForm; \ No newline at end of file diff --git a/src/app/security/examples/testing/securityTests.js b/src/app/security/examples/testing/securityTests.js new file mode 100644 index 0000000..a268fda --- /dev/null +++ b/src/app/security/examples/testing/securityTests.js @@ -0,0 +1,373 @@ +/** + * Security Testing Examples + * + * This file demonstrates how to test security implementations + * and validate that your security measures are working correctly. + */ + +const { InputSanitizer, InputValidator, SecureApiClient, ErrorHandler } = require('../utils/securityUtils'); + +describe('Security Testing Suite', () => { + + describe('Input Sanitization Tests', () => { + + test('should prevent XSS attacks', () => { + const maliciousInputs = [ + '', + '', + 'javascript:alert(document.cookie)', + '', + '', + '">' + ]; + + maliciousInputs.forEach(input => { + const sanitized = InputSanitizer.sanitizeString(input, { removeScripts: true }); + expect(sanitized).not.toContain('
Hello & goodbye "quotes" \'apostrophes\'
'; + const escaped = InputSanitizer.escapeHtml(input); + + expect(escaped).toContain('<div>'); + expect(escaped).toContain('&'); + expect(escaped).toContain('"'); + expect(escaped).toContain('''); + }); + + test('should sanitize email addresses', () => { + const testCases = [ + { input: 'user@example.com', expected: 'user@example.com' }, + { input: 'USER@EXAMPLE.COM', expected: 'user@example.com' }, + { input: ' user@example.com ', expected: 'user@example.com' }, + { input: 'user', + 'ftp://example.com', + 'file:///etc/passwd' + ]; + + validUrls.forEach(url => { + const sanitized = InputSanitizer.sanitizeUrl(url); + expect(sanitized).toBe(url); + }); + + invalidUrls.forEach(url => { + const sanitized = InputSanitizer.sanitizeUrl(url); + expect(sanitized).toBe(''); + }); + }); + }); + + describe('Input Validation Tests', () => { + + test('should validate email formats', () => { + const validEmails = [ + 'user@example.com', + 'test.email@domain.co.uk', + 'user+tag@example.org' + ]; + + const invalidEmails = [ + 'invalid-email', + '@example.com', + 'user@', + 'user..double.dot@example.com' + ]; + + validEmails.forEach(email => { + expect(InputValidator.isEmail(email)).toBe(true); + }); + + invalidEmails.forEach(email => { + expect(InputValidator.isEmail(email)).toBe(false); + }); + }); + + test('should validate password strength', () => { + const testCases = [ + { password: 'Password123!', shouldBeValid: true, minScore: 4 }, + { password: 'password', shouldBeValid: false, maxScore: 2 }, + { password: 'PASSWORD', shouldBeValid: false, maxScore: 2 }, + { password: '12345678', shouldBeValid: false, maxScore: 2 }, + { password: 'Aa1!', shouldBeValid: false, maxScore: 3 }, // too short + { password: 'LongPasswordWithoutNumbers!', shouldBeValid: false, maxScore: 4 } + ]; + + testCases.forEach(({ password, shouldBeValid, minScore, maxScore }) => { + const result = InputValidator.validatePassword(password); + expect(result.isValid).toBe(shouldBeValid); + + if (minScore) { + expect(result.score).toBeGreaterThanOrEqual(minScore); + } + + if (maxScore) { + expect(result.score).toBeLessThanOrEqual(maxScore); + } + }); + }); + + test('should validate string lengths', () => { + expect(InputValidator.hasMinLength('hello', 3)).toBe(true); + expect(InputValidator.hasMinLength('hi', 3)).toBe(false); + expect(InputValidator.hasMaxLength('hello', 10)).toBe(true); + expect(InputValidator.hasMaxLength('hello world!', 10)).toBe(false); + }); + }); + + describe('API Client Security Tests', () => { + let apiClient; + + beforeEach(() => { + apiClient = new SecureApiClient('https://api.example.com'); + // Mock fetch for testing + global.fetch = jest.fn(); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + test('should include authentication headers', async () => { + const mockResponse = { ok: true, json: () => Promise.resolve({ data: 'test' }) }; + global.fetch.mockResolvedValueOnce(mockResponse); + + apiClient.setAuthToken('test-token'); + await apiClient.get('/test'); + + expect(global.fetch).toHaveBeenCalledWith( + 'https://api.example.com/test', + expect.objectContaining({ + headers: expect.objectContaining({ + 'Authorization': 'Bearer test-token' + }) + }) + ); + }); + + test('should sanitize request data', async () => { + const mockResponse = { ok: true, json: () => Promise.resolve({ success: true }) }; + global.fetch.mockResolvedValueOnce(mockResponse); + + const maliciousData = { + name: '', + message: 'Hello ' + }; + + await apiClient.post('/test', maliciousData); + + const fetchCall = global.fetch.mock.calls[0]; + const requestBody = JSON.parse(fetchCall[1].body); + + expect(requestBody.name).not.toContain('', + '', + '">', + '' + ]; + + return xssPayloads.map(payload => { + const component = renderFunction(); + const input = component.querySelector(inputSelector); + const output = component.querySelector(outputSelector); + + // Simulate user input + input.value = payload; + input.dispatchEvent(new Event('input')); + + // Check if XSS payload is properly escaped + return { + payload, + isVulnerable: output.innerHTML.includes('