├── Diagrams ├── JavaScriptSecurity.drawio └── JavaScriptSecurityBookCover.png ├── Readme.md ├── Recipe-01-XSSProtection └── index.html ├── Recipe-02-ContentSecurityPolicy └── index.html ├── Recipe-03-EscapingHTMLEntities └── index.html ├── Recipe-04-AntiForgeryToken └── index.html ├── Recipe-05-ValidateCORS └── index.js ├── Recipe-06-ValidateNumbers └── index.html ├── Recipe-07-SqlInjection └── index.js ├── Recipe-08-InsecureJsonHandling └── index.html ├── Recipe-09-SecurePasswordStore └── index.html ├── Recipe-10-TokenBasedAuthenticatin ├── client-script.js ├── client.html └── server.js ├── Recipe-11-RoleBasedAccessControl ├── clinet.html └── server.js ├── Recipe-12-HTTPSCommunicatin ├── client.html └── server.js ├── Recipe-13-SecurelyHandlingCookies ├── index.html └── server.js ├── Recipe-14-ManInTheMiddleAttack ├── index.html └── server.js ├── Recipe-15-ValidateFileType ├── index.html ├── server.js └── upload.js ├── Recipe-16-FileSizeRestrictions ├── index.html └── server.js ├── Recipe-17-AntiVirusScan └── server.js ├── Recipe-18-SecureWebStorage ├── index.html └── server.js ├── Recipe-19-EnableHSTS └── server.js ├── Recipe-20-XContentTypeOptions └── server.js ├── Recipe-21-AvoidEvalMethod └── index.html ├── Recipe-22-HandlePromises └── index.html ├── Recipe-23-EnforceStrictMode └── index.html ├── Recipe-24-SecureMobileAppComm ├── script.js └── server.js ├── Recipe-25-TouchId ├── index.html └── main.js ├── Recipe-26-ClientSideLogging ├── index.html ├── logging.js └── server.js ├── Recipe-27-ErrorMonitoring ├── index.html └── server.js ├── Recipe-28-AutomatedSecurityTesting └── security-test.js ├── Recipe-29-StaticCodeAnalysis ├── .eslintrc.js └── package.json ├── Recipe-30-SelectThirdPartyLib └── readme.md ├── Recipe-31-UpdateDependency └── readme.md ├── Recipe-32-SecureRouting └── server.js ├── Recipe-33-ProtectAgainstXSSI ├── index.html └── server.js ├── Recipe-34-ManageAPIKeys └── readme.md ├── Recipe-35-UserSessionData ├── index.html └── server.js ├── Recipe-36-SecureWebWorkerComm ├── index.html └── worker.js ├── Recipe-37-WebSocketRateLimit └── server.js ├── Recipe-38-MultiFactorAuthentication └── server.js ├── Recipe-39-SecureWebSocketComm └── server.js ├── Recipe-40-URLInfoDisclosure ├── client.html └── server.js ├── Recipe-41-URLScriptInjection └── index.html └── Recipe-42-EscapeHtml ├── index.html └── server.js /Diagrams/JavaScriptSecurityBookCover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajamsrgit/JavaScriptSecurityCookbook/0bd4988dc8321d603b4b9cb55b0c903650d9d3ca/Diagrams/JavaScriptSecurityBookCover.png -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # JavaScript Security Cookbook 2 | This eBook `JavaScript Security Cookbook` will guide you on making your web applications more secure from many attacks. You will learn 40+ useful recipes to protect your websites from hackers and bad actors. 3 | 4 | Security measures are vital when writing code in JavaScript and Node JS. Don't let hackers ruin your awesome work! 😎 5 | That's why I built the "JavaScript Security Cookbook". 6 | • 40+ Recipes 7 | • Infographics 8 | • Source code 9 | • 63 Pages eBook (PDF) 10 | Download Link 11 | [JavaScript Security Cookbook](https://onlinemsr.gumroad.com/l/javascript-security-cookbook) 12 | 13 | ![JavaScript Security Cookbook](/Diagrams/JavaScriptSecurityBookCover.png?raw=true "JavaScript Security Cookbook") 14 | 15 | The book covers how to: 16 | 17 | - Protect your website from XSS and CSRF attacks by checking user input and using CSP and anti-CSRF tokens. 18 | 19 | - Validate and sanitize your data to prevent SQL injection and JSON injection. 20 | 21 | - Implement authentication and authorization using secure passwords, tokens, and roles. 22 | 23 | - Use HTTPS, cookies, and security headers to encrypt communication and prevent man-in-the-middle attacks. 24 | 25 | - Handle file uploads, client-side storage, and security testing safely and securely. 26 | 27 | - Improve your security with secure coding practices, mobile security, and monitoring and logging. 28 | 29 | - Learn advanced security topics like multi-factor authentication and WebSockets communication. 30 | 31 | This eBook is a must-have for anyone who wants to build secure web applications using JavaScript and Node.js. It will give you the knowledge and skills to defend your websites from cyber threats! 32 | 33 | ## Table of Contents 34 | 35 | | Recipe | Description | 36 | |-----------| --------------------------------| 37 | | Recipe 1 | Sanitizing User Input in Forms | 38 | | Recipe 2 | Implementing Content Security Policy (CSP) | 39 | | Recipe 3 | Escaping HTML Entities in Dynamic Content | 40 | | Recipe 4 | Implementing Anti-CSRF Tokens | 41 | | Recipe 5 | Validating Cross-Origin Requests | 42 | | Recipe 6 | Input Validation for Numeric Fields | 43 | | Recipe 7 | Protecting Against SQL Injection | 44 | | Recipe 8 | Safeguarding Against JSON Injection | 45 | | Recipe 9 | Secure Password Storage with Hashing | 46 | | Recipe 10 | Implementing Token-Based Authentication | 47 | | Recipe 11 | Role-Based Access Control (RBAC) in JavaScript | 48 | | Recipe 12 | Implementing HTTPS in JavaScript | 49 | | Recipe 13 | Securely Handling Cookies | 50 | | Recipe 14 | Protecting Against Man-in-the-Middle Attacks | 51 | | Recipe 15 | Validating and Restricting File Types | 52 | | Recipe 16 | Implementing File Size Restrictions | 53 | | Recipe 17 | Securing File Uploads with Anti-Virus Scanning | 54 | | Recipe 18 | Securely Using Web Storage | 55 | | Recipe 19 | Configuring Strict-Transport-Security (HSTS) | 56 | | Recipe 20 | Implementing X-Content-Type-Options | 57 | | Recipe 21 | Avoiding Eval() and Function Constructors | 58 | | Recipe 22 | Proper Use of JavaScript Promises | 59 | | Recipe 23 | Enforcing Strict Mode | 60 | | Recipe 24 | Securing Mobile App Communication | 61 | | Recipe 25 | Implementing Touch ID/Face ID Authentication | 62 | | Recipe 26 | Implementing Client-Side Logging | 63 | | Recipe 27 | Setting up Error Monitoring with JavaScript | 64 | | Recipe 28 | Implementing Automated Security Testing | 65 | | Recipe 29 | Using Static Code Analysis Tools | 66 | | Recipe 30 | Vetted Third-Party Library Selection | 67 | | Recipe 31 | Regularly Updating Dependencies | 68 | | Recipe 32 | Implementing Secure Routing | 69 | | Recipe 33 | Protecting Against Cross-Site Script Inclusion (XSSI) | 70 | | Recipe 34 | Securely Managing API Keys | 71 | | Recipe 35 | Best Practices for Handling User Session Data | 72 | | Recipe 36 | Securing Communication with Web Workers | 73 | | Recipe 37 | Rate Limiting and Connection Management | 74 | | Recipe 38 | Enhancing Security with Multi-Factor Authentication | 75 | | Recipe 39 | Secure WebSocket Communication | 76 | | Recipe 40 | Protect Against Script Injection in URLs | 77 | | Recipe 41 | Information Disclosure Through URLs | 78 | | Recipe 42 | Escape HTML Output | 79 | 80 | ## Discover more at [Raja MSR](https://www.rajamsr.com). 81 | -------------------------------------------------------------------------------- /Recipe-01-XSSProtection/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | XSS Protection 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 |
16 | 17 |
18 | 19 | 27 | 28 | -------------------------------------------------------------------------------- /Recipe-02-ContentSecurityPolicy/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 14 | 15 | Your Web Page Title 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /Recipe-03-EscapingHTMLEntities/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Dynamic Content Example 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 | "; 25 | 26 | // Escape HTML entities before injecting dynamic content 27 | var escapedContent = escapeHtml(userInput); 28 | 29 | // Inject the escaped content into the page 30 | var dynamicContentContainer = document.getElementById('dynamicContentContainer'); 31 | dynamicContentContainer.innerHTML = "

" + escapedContent + "

"; 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /Recipe-04-AntiForgeryToken/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | CSRF Protection Example 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /Recipe-05-ValidateCORS/index.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const app = express(); 3 | 4 | // Enable CORS for all routes 5 | app.use((req, res, next) => { 6 | res.header('Access-Control-Allow-Origin', '*'); // Replace '*' with your specific origin 7 | res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE'); 8 | res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization'); 9 | res.header('Access-Control-Allow-Credentials', true); 10 | 11 | // Continue to the next middleware 12 | next(); 13 | }); 14 | 15 | // Your routes and other middleware go here 16 | 17 | const port = 3000; 18 | app.listen(port, () => { 19 | console.log(`Server is running on port ${port}`); 20 | }); 21 | -------------------------------------------------------------------------------- /Recipe-06-ValidateNumbers/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Numeric Input Validation 7 | 8 | 9 | 10 | 11 | 12 | 13 |

14 | 15 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /Recipe-07-SqlInjection/index.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const bodyParser = require('body-parser'); 3 | const mysql = require('mysql'); 4 | 5 | const app = express(); 6 | const port = 3000; 7 | 8 | // Create a MySQL connection pool 9 | const pool = mysql.createPool({ 10 | host: 'your-database-host', 11 | user: 'your-database-user', 12 | password: 'your-database-password', 13 | database: 'your-database-name', 14 | }); 15 | 16 | // Middleware to parse JSON and urlencoded data 17 | app.use(bodyParser.json()); 18 | app.use(bodyParser.urlencoded({ extended: true })); 19 | 20 | // Route for handling user input 21 | app.post('/users', (req, res) => { 22 | // Get user input from the request body 23 | const { username, password } = req.body; 24 | 25 | // Use a parameterized query to avoid SQL injection 26 | const query = 'INSERT INTO users (username, password) VALUES (?, ?)'; 27 | const values = [username, password]; 28 | 29 | // Use the connection pool to execute the query 30 | pool.query(query, values, (error, results, fields) => { 31 | if (error) { 32 | console.error('SQL injection protection failed:', error); 33 | res.status(500).send('Internal Server Error'); 34 | } else { 35 | res.status(200).send('User added successfully'); 36 | } 37 | }); 38 | }); 39 | 40 | // Start the server 41 | app.listen(port, () => { 42 | console.log(`Server is running on port ${port}`); 43 | }); 44 | -------------------------------------------------------------------------------- /Recipe-08-InsecureJsonHandling/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Secure JSON Handling 7 | 8 | 9 | 10 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /Recipe-09-SecurePasswordStore/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Password Hashing Example 7 | 8 | 9 | 10 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /Recipe-10-TokenBasedAuthenticatin/client-script.js: -------------------------------------------------------------------------------- 1 | // script.js 2 | 3 | async function login() { 4 | const username = document.getElementById('username').value; 5 | const password = document.getElementById('password').value; 6 | 7 | const response = await fetch('http://localhost:3000/login', { 8 | method: 'POST', 9 | headers: { 10 | 'Content-Type': 'application/json', 11 | }, 12 | body: JSON.stringify({ username, password }), 13 | }); 14 | 15 | const data = await response.json(); 16 | if (response.ok) { 17 | const token = data.token; 18 | localStorage.setItem('token', token); 19 | alert('Login successful'); 20 | displayProtectedContent(); 21 | } else { 22 | alert(`Login failed: ${data.error}`); 23 | } 24 | } 25 | 26 | async function displayProtectedContent() { 27 | const token = localStorage.getItem('token'); 28 | if (!token) { 29 | alert('You need to log in first'); 30 | return; 31 | } 32 | 33 | const response = await fetch('http://localhost:3000/protected', { 34 | headers: { 35 | 'Authorization': token, 36 | }, 37 | }); 38 | 39 | if (response.ok) { 40 | const data = await response.json(); 41 | document.getElementById('protectedContent').innerText = data.message; 42 | } else { 43 | alert('Access denied'); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Recipe-10-TokenBasedAuthenticatin/client.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Token-based Authentication 9 | 10 | 11 |

Login

12 |
13 | 14 | 15 |
16 | 17 | 18 |
19 | 20 |
21 | 22 |

Protected Content

23 |
24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Recipe-10-TokenBasedAuthenticatin/server.js: -------------------------------------------------------------------------------- 1 | // TODO: Install the required packages using npm install express jsonwebtoken 2 | 3 | // server.js 4 | const express = require('express'); 5 | const jwt = require('jsonwebtoken'); 6 | 7 | const app = express(); 8 | const secretKey = 'your_secret_key'; 9 | 10 | app.use(express.json()); 11 | 12 | // Login endpoint 13 | app.post('/login', (req, res) => { 14 | // Validate user credentials (this is a simplified example) 15 | const { username, password } = req.body; 16 | if (username === 'user' && password === 'password') { 17 | // Generate a token 18 | const token = jwt.sign({ username }, secretKey, { expiresIn: '1h' }); 19 | res.json({ token }); 20 | } else { 21 | res.status(401).json({ error: 'Invalid credentials' }); 22 | } 23 | }); 24 | 25 | // Protected endpoint 26 | app.get('/protected', authenticateToken, (req, res) => { 27 | res.json({ message: 'Access granted' }); 28 | }); 29 | 30 | // Middleware to authenticate token 31 | function authenticateToken(req, res, next) { 32 | const token = req.header('Authorization'); 33 | if (!token) return res.sendStatus(401); 34 | 35 | jwt.verify(token, secretKey, (err, user) => { 36 | if (err) return res.sendStatus(403); 37 | req.user = user; 38 | next(); 39 | }); 40 | } 41 | 42 | const PORT = 3000; 43 | app.listen(PORT, () => { 44 | console.log(`Server is running on http://localhost:${PORT}`); 45 | }); 46 | -------------------------------------------------------------------------------- /Recipe-11-RoleBasedAccessControl/clinet.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | RBAC Example 7 | 8 | 9 | 10 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /Recipe-11-RoleBasedAccessControl/server.js: -------------------------------------------------------------------------------- 1 | // Install required packages: 2 | // npm install express body-parser 3 | 4 | const express = require('express'); 5 | const bodyParser = require('body-parser'); 6 | 7 | const app = express(); 8 | const port = 3000; 9 | 10 | // User roles 11 | const roles = { 12 | ADMIN: 'admin', 13 | USER: 'user', 14 | GUEST: 'guest', 15 | }; 16 | 17 | // Sample user data with assigned roles 18 | const users = [ 19 | { username: 'admin1', password: 'adminpass', role: roles.ADMIN }, 20 | { username: 'user1', password: 'userpass', role: roles.USER }, 21 | { username: 'guest1', password: 'guestpass', role: roles.GUEST }, 22 | ]; 23 | 24 | app.use(bodyParser.json()); 25 | 26 | // Authentication middleware 27 | function authenticate(req, res, next) { 28 | const { username, password } = req.body; 29 | 30 | const user = users.find(u => u.username === username && u.password === password); 31 | 32 | if (user) { 33 | req.user = user; 34 | next(); 35 | } else { 36 | res.status(401).send('Authentication failed'); 37 | } 38 | } 39 | 40 | // Authorization middleware 41 | function authorize(role) { 42 | return (req, res, next) => { 43 | if (req.user && req.user.role === role) { 44 | next(); 45 | } else { 46 | res.status(403).send('Unauthorized'); 47 | } 48 | }; 49 | } 50 | 51 | // Example protected routes 52 | app.get('/admin', authenticate, authorize(roles.ADMIN), (req, res) => { 53 | res.send('Admin has access to special features.'); 54 | }); 55 | 56 | app.get('/user', authenticate, authorize(roles.USER), (req, res) => { 57 | res.send('User has access to regular features.'); 58 | }); 59 | 60 | app.get('/guest', authenticate, authorize(roles.GUEST), (req, res) => { 61 | res.send('Guest has limited access.'); 62 | }); 63 | 64 | app.listen(port, () => { 65 | console.log(`Server is running on port ${port}`); 66 | }); 67 | -------------------------------------------------------------------------------- /Recipe-12-HTTPSCommunicatin/client.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Secure Webpage 7 | 8 | 9 |

Hello, Secure World!

10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Recipe-12-HTTPSCommunicatin/server.js: -------------------------------------------------------------------------------- 1 | const https = require('https'); 2 | const fs = require('fs'); 3 | 4 | const options = { 5 | key: fs.readFileSync('server.key'), 6 | cert: fs.readFileSync('server.cert') 7 | }; 8 | 9 | const server = https.createServer(options, (req, res) => { 10 | res.writeHead(200); 11 | res.end('Hello, secure world!'); 12 | }); 13 | 14 | const PORT = 443; // Standard HTTPS port 15 | 16 | server.listen(PORT, () => { 17 | console.log(`Server running on https://localhost:${PORT}`); 18 | }); 19 | -------------------------------------------------------------------------------- /Recipe-13-SecurelyHandlingCookies/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Secure Cookies Example 7 | 8 | 9 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Recipe-13-SecurelyHandlingCookies/server.js: -------------------------------------------------------------------------------- 1 | /*In this example: 2 | 3 | To set HTTP-only and secure flags for cookies on the server side in a Node.js application, you typically do this within your server code. Here's a brief example using Express, a popular web framework for Node.js: 4 | 5 | 6 | First, install Express if you haven't already: 7 | npm install express 8 | 9 | 10 | The cookie-parser middleware is used to parse cookies in the request. 11 | The / route sets a cookie using the res.cookie method with the httpOnly and secure options. 12 | Make sure to adjust the secure option based on your deployment environment. In the example, it is set to true only in a production environment, assuming that you are using HTTPS in production. 13 | 14 | Remember to handle sensitive information carefully and follow security best practices in your application. 15 | */ 16 | const express = require('express'); 17 | const cookieParser = require('cookie-parser'); 18 | 19 | const app = express(); 20 | const port = 3000; 21 | 22 | app.use(cookieParser()); 23 | 24 | app.get('/', (req, res) => { 25 | // Set HTTP-only and secure cookie 26 | res.cookie('exampleCookie', 'exampleValue', { 27 | maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days 28 | httpOnly: true, 29 | secure: process.env.NODE_ENV === 'production', // Set to true in production (requires HTTPS) 30 | }); 31 | 32 | res.send('Cookie set successfully!'); 33 | }); 34 | 35 | app.listen(port, () => { 36 | console.log(`Server is running at http://localhost:${port}`); 37 | }); 38 | -------------------------------------------------------------------------------- /Recipe-14-ManInTheMiddleAttack/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Secure Webpage 7 | 35 | 36 | 37 |

Secure Webpage

38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /Recipe-14-ManInTheMiddleAttack/server.js: -------------------------------------------------------------------------------- 1 | const https = require('https'); 2 | const fs = require('fs'); 3 | 4 | // Load your SSL certificate and private key 5 | const options = { 6 | key: fs.readFileSync('path/to/private-key.pem'), 7 | cert: fs.readFileSync('path/to/certificate.pem') 8 | }; 9 | 10 | // Define the expected public key hash (SHA-256) 11 | const expectedPublicKeyHash = 'YOUR_PUBLIC_KEY_HASH'; 12 | 13 | // Create an HTTPS server 14 | const server = https.createServer(options, (req, res) => { 15 | res.writeHead(200, {'Content-Type': 'text/plain'}); 16 | res.end('Hello, this is a secure server!\n'); 17 | }); 18 | 19 | // Listen on a specific port 20 | const PORT = 3000; 21 | server.listen(PORT, () => { 22 | console.log(`Server running on https://localhost:${PORT}`); 23 | }); 24 | 25 | // Add a listener for the 'secureConnection' event to perform certificate pinning 26 | server.on('secureConnection', (tlsSocket) => { 27 | const peerCertificate = tlsSocket.getPeerCertificate(); 28 | 29 | // Check if the public key hash matches the expected hash 30 | if (peerCertificate && peerCertificate.fingerprint256 === expectedPublicKeyHash) { 31 | console.log('Certificate is valid. Connection secure.'); 32 | } else { 33 | console.error('Certificate verification failed. Potential MitM attack!'); 34 | // Handle the error or close the connection as needed 35 | tlsSocket.destroy(); 36 | } 37 | }); 38 | -------------------------------------------------------------------------------- /Recipe-15-ValidateFileType/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Secure File Upload 8 | 9 | 10 |
11 | 12 | 13 |
14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Recipe-15-ValidateFileType/server.js: -------------------------------------------------------------------------------- 1 | // Make sure to install the required Node.js packages using: 2 | // npm install express multer 3 | 4 | // These examples provide basic file type validation. You may want to enhance security by considering file size limits, using a content security policy, and implementing additional measures depending on your specific use case. 5 | 6 | // server.js 7 | const express = require('express'); 8 | const multer = require('multer'); 9 | const app = express(); 10 | const port = 3000; 11 | 12 | // Configure multer to handle file uploads 13 | const storage = multer.memoryStorage(); 14 | const upload = multer({ storage: storage }); 15 | 16 | app.use(express.static('public')); 17 | 18 | app.post('/upload', upload.single('file'), (req, res) => { 19 | const file = req.file; 20 | 21 | if (!file) { 22 | return res.status(400).send('No file uploaded.'); 23 | } 24 | 25 | const allowedTypes = ['image/jpeg', 'image/jpg', 'image/png']; 26 | if (allowedTypes.includes(file.mimetype)) { 27 | // Save or process the file as needed 28 | res.send('File uploaded successfully.'); 29 | } else { 30 | // Delete the file and send an error response 31 | res.status(400).send('Invalid file type. Please upload a JPG or PNG file.'); 32 | } 33 | }); 34 | 35 | app.listen(port, () => { 36 | console.log(`Server listening at http://localhost:${port}`); 37 | }); 38 | -------------------------------------------------------------------------------- /Recipe-15-ValidateFileType/upload.js: -------------------------------------------------------------------------------- 1 | // upload.js 2 | function uploadFile() { 3 | const fileInput = document.getElementById('fileInput'); 4 | const file = fileInput.files[0]; 5 | 6 | if (file) { 7 | const allowedTypes = ['image/jpeg', 'image/jpg', 'image/png']; 8 | if (allowedTypes.includes(file.type)) { 9 | // Proceed with file upload 10 | alert('File is valid. Uploading...'); 11 | // You can submit the form or send the file to the server using AJAX 12 | } else { 13 | alert('Invalid file type. Please upload a JPG or PNG file.'); 14 | } 15 | } else { 16 | alert('Please choose a file before uploading.'); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Recipe-16-FileSizeRestrictions/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | File Upload 8 | 9 | 10 | 11 | 12 |

13 | 14 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /Recipe-16-FileSizeRestrictions/server.js: -------------------------------------------------------------------------------- 1 | /*This server-side code uses the multer middleware to handle file uploads and sets a file size limit of 1MB (adjust as needed). The upload.single('file') middleware processes a single file with the field name 'file'. You can adapt the server-side code based on your specific needs. 2 | 3 | Make sure to install the required Node.js packages by running: 4 | 5 | npm install express multer 6 | */ 7 | 8 | const express = require('express'); 9 | const multer = require('multer'); 10 | 11 | const app = express(); 12 | const port = 3000; 13 | 14 | const storage = multer.memoryStorage(); // Store the file in memory (adjust as needed) 15 | const upload = multer({ storage: storage, limits: { fileSize: 1024 * 1024 } }); // 1MB limit 16 | 17 | app.post('/upload', upload.single('file'), (req, res) => { 18 | if (!req.file) { 19 | return res.status(400).send('No file uploaded.'); 20 | } 21 | 22 | // Process the file or do further validation 23 | 24 | res.send('File uploaded successfully.'); 25 | }); 26 | 27 | app.listen(port, () => { 28 | console.log(`Server is listening at http://localhost:${port}`); 29 | }); 30 | -------------------------------------------------------------------------------- /Recipe-17-AntiVirusScan/server.js: -------------------------------------------------------------------------------- 1 | // server.js 2 | const express = require('express'); 3 | const multer = require('multer'); 4 | const { ClamScan } = require('@claviska/clamscan'); 5 | 6 | const app = express(); 7 | const port = 3000; 8 | 9 | const storage = multer.memoryStorage(); 10 | const upload = multer({ storage: storage }); 11 | 12 | const clamscan = new ClamScan({ 13 | clamscan: { 14 | path: '/usr/bin/clamscan', // Path to the ClamAV binary 15 | db: '/var/lib/clamav' 16 | }, 17 | preference: 'clamdscan' 18 | }); 19 | 20 | app.post('/upload', upload.single('file'), async (req, res) => { 21 | try { 22 | const { buffer } = req.file; 23 | const isSafe = await clamscan.isInfected(buffer); 24 | 25 | if (isSafe) { 26 | // Process the file since it's safe 27 | res.status(200).json({ message: 'File is safe!' }); 28 | } else { 29 | // Reject the file if it's infected 30 | res.status(400).json({ error: 'File is infected!' }); 31 | } 32 | } catch (error) { 33 | console.error(error); 34 | res.status(500).json({ error: 'Internal server error' }); 35 | } 36 | }); 37 | 38 | app.listen(port, () => { 39 | console.log(`Server is running on port ${port}`); 40 | }); 41 | -------------------------------------------------------------------------------- /Recipe-18-SecureWebStorage/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Secure Web Storage 8 | 9 | 10 | 11 | 12 | 13 | 14 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Recipe-18-SecureWebStorage/server.js: -------------------------------------------------------------------------------- 1 | /** 2 | To securely store sensitive information using web storage, you can follow these steps: 3 | 4 | 1. Use a strong encryption algorithm to encrypt the sensitive data. 5 | 2. Store the encrypted data in the web storage (localStorage or sessionStorage). 6 | 3. Decrypt the data when needed. 7 | 8 | Below is a simple example using Node.js and the crypto library for encryption and decryption. 9 | Note that this is a basic example, and in a real-world scenario, you may want to use more 10 | robust encryption libraries and additional security measures. 11 | 12 | **/ 13 | 14 | // Node.js server using Express 15 | const express = require('express'); 16 | const crypto = require('crypto'); 17 | const bodyParser = require('body-parser'); 18 | const app = express(); 19 | const PORT = 3000; 20 | 21 | app.use(bodyParser.json()); 22 | 23 | // Encryption key (should be stored securely) 24 | const encryptionKey = 'your_secret_encryption_key'; 25 | 26 | // Encryption function 27 | function encrypt(text) { 28 | const cipher = crypto.createCipher('aes-256-cbc', encryptionKey); 29 | let encrypted = cipher.update(text, 'utf-8', 'hex'); 30 | encrypted += cipher.final('hex'); 31 | return encrypted; 32 | } 33 | 34 | // Decryption function 35 | function decrypt(text) { 36 | const decipher = crypto.createDecipher('aes-256-cbc', encryptionKey); 37 | let decrypted = decipher.update(text, 'hex', 'utf-8'); 38 | decrypted += decipher.final('utf-8'); 39 | return decrypted; 40 | } 41 | 42 | // Example route to store encrypted data 43 | app.post('/store', (req, res) => { 44 | const sensitiveData = req.body.data; 45 | const encryptedData = encrypt(sensitiveData); 46 | // Store encrypted data in localStorage or sessionStorage 47 | // In a real application, you might want to implement proper error handling and validation. 48 | localStorage.setItem('encryptedData', encryptedData); 49 | res.send('Data stored securely.'); 50 | }); 51 | 52 | // Example route to retrieve and decrypt data 53 | app.get('/retrieve', (req, res) => { 54 | // Retrieve encrypted data from localStorage or sessionStorage 55 | const encryptedData = localStorage.getItem('encryptedData'); 56 | if (encryptedData) { 57 | // Decrypt the data when needed 58 | const decryptedData = decrypt(encryptedData); 59 | res.send(`Decrypted Data: ${decryptedData}`); 60 | } else { 61 | res.send('No data found.'); 62 | } 63 | }); 64 | 65 | app.listen(PORT, () => { 66 | console.log(`Server is running on http://localhost:${PORT}`); 67 | }); 68 | -------------------------------------------------------------------------------- /Recipe-19-EnableHSTS/server.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const helmet = require('helmet'); 3 | 4 | const app = express(); 5 | 6 | // Use helmet with HSTS middleware 7 | app.use(helmet({ 8 | hsts: { 9 | maxAge: 31536000, // 1 year in seconds 10 | includeSubDomains: true, 11 | preload: true 12 | } 13 | })); 14 | 15 | // Your other routes and middleware go here... 16 | 17 | // Start the server 18 | const PORT = process.env.PORT || 3000; 19 | app.listen(PORT, () => { 20 | console.log(`Server is running on port ${PORT}`); 21 | }); 22 | -------------------------------------------------------------------------------- /Recipe-20-XContentTypeOptions/server.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const helmet = require('helmet'); 3 | 4 | const app = express(); 5 | 6 | // Use the helmet middleware to set security headers 7 | app.use(helmet()); 8 | 9 | // Your other middleware and route handlers go here 10 | 11 | const PORT = process.env.PORT || 3000; 12 | app.listen(PORT, () => { 13 | console.log(`Server is running on port ${PORT}`); 14 | }); 15 | -------------------------------------------------------------------------------- /Recipe-21-AvoidEvalMethod/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Dynamic Code Execution 8 | 9 | 10 | 11 | 12 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /Recipe-22-HandlePromises/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Secure Promise Handling 8 | 9 | 10 | 11 | 12 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /Recipe-23-EnforceStrictMode/index.html: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Strict Mode Example 12 | 13 | 14 | 15 | 16 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Recipe-24-SecureMobileAppComm/script.js: -------------------------------------------------------------------------------- 1 | fetch('https://your-api-endpoint.com/data') 2 | .then(response => response.json()) 3 | .then(data => console.log(data)) 4 | .catch(error => console.error('Error:', error)); 5 | -------------------------------------------------------------------------------- /Recipe-24-SecureMobileAppComm/server.js: -------------------------------------------------------------------------------- 1 | // Import required modules 2 | const https = require('https'); 3 | const fs = require('fs'); 4 | 5 | // Read the SSL certificate files 6 | const privateKey = fs.readFileSync('path/to/private-key.pem', 'utf8'); 7 | const certificate = fs.readFileSync('path/to/certificate.pem', 'utf8'); 8 | const ca = fs.readFileSync('path/to/ca.pem', 'utf8'); 9 | 10 | // Create HTTPS server options 11 | const credentials = { key: privateKey, cert: certificate, ca: ca }; 12 | 13 | // Your Express or HTTP server logic 14 | const express = require('express'); 15 | const app = express(); 16 | 17 | // Define your routes and other application logic here 18 | 19 | // Create HTTPS server 20 | const httpsServer = https.createServer(credentials, app); 21 | 22 | // Set the server to listen on a specific port 23 | const PORT = 3000; 24 | httpsServer.listen(PORT, () => { 25 | console.log(`Server is running on https://localhost:${PORT}`); 26 | }); 27 | -------------------------------------------------------------------------------- /Recipe-25-TouchId/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Biometric Authentication 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Recipe-25-TouchId/main.js: -------------------------------------------------------------------------------- 1 | 2 | // Note that this is a simplified example, and in a real-world scenario, 3 | // you would need to implement server-side verification of the biometric credentials. 4 | 5 | document.addEventListener("DOMContentLoaded", function () { 6 | const biometricBtn = document.getElementById("biometricBtn"); 7 | 8 | biometricBtn.addEventListener("click", () => { 9 | authenticateWithBiometrics() 10 | .then((success) => { 11 | if (success) { 12 | alert("Biometric authentication successful!"); 13 | // Implement your logic for authenticated actions here 14 | } else { 15 | alert("Biometric authentication failed."); 16 | } 17 | }) 18 | .catch((error) => { 19 | console.error("Biometric authentication error:", error); 20 | }); 21 | }); 22 | 23 | async function authenticateWithBiometrics() { 24 | try { 25 | const credentials = await navigator.credentials.get({ 26 | publicKey: { 27 | challenge: new Uint8Array(32), 28 | rp: { name: "Example Web App" }, 29 | user: { id: new Uint8Array(16), name: "user@example.com", displayName: "User" }, 30 | pubKeyCredParams: [{ type: "public-key", alg: -7 }], 31 | }, 32 | }); 33 | 34 | // Check if the biometric authentication was successful 35 | if (credentials) { 36 | return true; 37 | } else { 38 | return false; 39 | } 40 | } catch (error) { 41 | throw error; 42 | } 43 | } 44 | }); 45 | -------------------------------------------------------------------------------- /Recipe-26-ClientSideLogging/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Client-side Logging Example 8 | 9 | 10 | 11 |

Client-side Logging Example

12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Recipe-26-ClientSideLogging/logging.js: -------------------------------------------------------------------------------- 1 | // Import Log4js 2 | import log4js from 'log4js'; 3 | 4 | // Configure Log4js 5 | log4js.configure({ 6 | appenders: { file: { type: 'file', filename: 'client-logs.log' } }, 7 | categories: { default: { appenders: ['file'], level: 'info' } } 8 | }); 9 | 10 | // Create a logger instance 11 | const logger = log4js.getLogger(); 12 | 13 | // Log some messages 14 | logger.info('This is an informational message.'); 15 | logger.warn('This is a warning message.'); 16 | logger.error('This is an error message.'); 17 | -------------------------------------------------------------------------------- /Recipe-26-ClientSideLogging/server.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const app = express(); 3 | const path = require('path'); 4 | const fs = require('fs'); 5 | 6 | // Serve the HTML file 7 | app.get('/', (req, res) => { 8 | res.sendFile(path.join(__dirname, 'index.html')); 9 | }); 10 | 11 | // Serve the client-side JavaScript file 12 | app.get('/app.js', (req, res) => { 13 | res.sendFile(path.join(__dirname, 'app.js')); 14 | }); 15 | 16 | // Endpoint to receive client-side logs 17 | app.post('/log', express.json(), (req, res) => { 18 | const logs = req.body; 19 | // Store logs to a file or perform other actions as needed 20 | fs.appendFileSync('server-logs.log', JSON.stringify(logs) + '\n'); 21 | res.status(200).send('Logs received successfully.'); 22 | }); 23 | 24 | // Start the server 25 | const port = 3000; 26 | app.listen(port, () => { 27 | console.log(`Server is running on http://localhost:${port}`); 28 | }); 29 | -------------------------------------------------------------------------------- /Recipe-27-ErrorMonitoring/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Client-side Monitoring Example 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 |

Client-side Monitoring Example

17 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /Recipe-27-ErrorMonitoring/server.js: -------------------------------------------------------------------------------- 1 | // Install sentry module 2 | // npm install --save @sentry/node 3 | 4 | // In your Node.js server code 5 | const Sentry = require('@sentry/node'); 6 | 7 | Sentry.init({ 8 | dsn: 'YOUR_DSN', 9 | // Additional configuration for Node.js 10 | }); 11 | 12 | // Example: capture an error in a Node.js route handler 13 | app.get('/', (req, res) => { 14 | try { 15 | // Code that might throw an error 16 | throw new Error('This is a test error'); 17 | } catch (error) { 18 | // Capture and send the error to Sentry 19 | Sentry.captureException(error); 20 | res.status(500).send('Internal Server Error'); 21 | } 22 | }); 23 | -------------------------------------------------------------------------------- /Recipe-28-AutomatedSecurityTesting/security-test.js: -------------------------------------------------------------------------------- 1 | const ZAPClient = require('zap-api'); 2 | 3 | const targetUrl = 'http://your-website-url.com'; // replace with your website URL 4 | const apiKey = 'your-api-key'; // replace with your ZAP API key 5 | 6 | // Create ZAP client instance 7 | const zap = new ZAPClient({ 8 | apiKey: apiKey, 9 | proxy: 'http://localhost:8090', // ZAP proxy URL 10 | ajax: true, 11 | }); 12 | 13 | // Start ZAP and perform a basic spider scan 14 | async function startZAP() { 15 | try { 16 | // Start ZAP 17 | await zap.startZAP(); 18 | 19 | // Access your target URL through ZAP proxy 20 | await zap.accessTargetViaZAP(targetUrl); 21 | 22 | // Spider the target URL 23 | await zap.spider(targetUrl); 24 | 25 | // Wait for the spider to finish 26 | await zap.waitForSpiderToFinish(); 27 | 28 | // Print the spider results 29 | const spiderResults = await zap.getSpiderResults(); 30 | console.log('Spider Results:', spiderResults); 31 | 32 | // Perform active scan 33 | await zap.activeScan(targetUrl); 34 | 35 | // Wait for the active scan to finish 36 | await zap.waitForActiveScanToFinish(); 37 | 38 | // Print the scan results 39 | const scanResults = await zap.getScanResults(); 40 | console.log('Scan Results:', scanResults); 41 | } catch (error) { 42 | console.error('Error:', error.message); 43 | } finally { 44 | // Shutdown ZAP 45 | await zap.shutdownZAP(); 46 | } 47 | } 48 | 49 | // Run the security tests 50 | startZAP(); 51 | -------------------------------------------------------------------------------- /Recipe-29-StaticCodeAnalysis/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // ... other configurations ... 3 | plugins: ['security', 'node'], 4 | extends: ['eslint:recommended', 'plugin:security/recommended'], 5 | rules: { 6 | // Additional rules for security can be added here 7 | }, 8 | }; 9 | -------------------------------------------------------------------------------- /Recipe-29-StaticCodeAnalysis/package.json: -------------------------------------------------------------------------------- 1 | "scripts": { 2 | "lint": "eslint ." 3 | } 4 | -------------------------------------------------------------------------------- /Recipe-30-SelectThirdPartyLib/readme.md: -------------------------------------------------------------------------------- 1 | # Recipe 30: Vetted Third-Party Library Selection 2 | ## Create a new project 3 | Create a new directory for your project and navigate to it in the terminal. 4 | ``` 5 | mkdir my-webpage 6 | cd my-webpage 7 | ``` 8 | 9 | ## Initialize a new Node.js project 10 | Run the following command to initialize a new Node.js project and create a package.json file. 11 | 12 | ``` 13 | npm init -y 14 | ``` 15 | 16 | ## Install snyk 17 | Install the [snyk](https://www.npmjs.com/package/snyk) command-line tool globally. 18 | 19 | ``` 20 | npm install -g snyk 21 | ``` 22 | 23 | ## Authenticate snyk 24 | Authenticate snyk using the following command. This is required to access the vulnerability database. 25 | 26 | ``` 27 | snyk auth 28 | ``` 29 | 30 | ## Scan for vulnerabilities 31 | Run the following command to scan your project for vulnerabilities. This will analyze your dependencies and provide information about their security status. 32 | ``` 33 | snyk test 34 | ``` 35 | 36 | After running this command, snyk will provide a summary of any vulnerabilities found in your project. -------------------------------------------------------------------------------- /Recipe-31-UpdateDependency/readme.md: -------------------------------------------------------------------------------- 1 | # Recipe 31: Regularly Updating Dependencies 2 | 3 | ## Install npm-check-updates (NCU): 4 | [NCU](https://www.npmjs.com/package/npm-check-updates) is a tool that allows you to easily check for and update your project's dependencies. 5 | 6 | ``` 7 | npm install -g npm-check-updates 8 | ``` 9 | 10 | ## Update Dependencies Script: 11 | Create a JavaScript file, for example, `updateDependencies.js`, with the following content: 12 | 13 | ``` 14 | const { exec } = require('child_process'); 15 | 16 | const updateDependencies = () => { 17 | console.log('Checking for updates...'); 18 | exec('ncu -u && npm install', (error, stdout, stderr) => { 19 | if (error) { 20 | console.error(`Error: ${error.message}`); 21 | return; 22 | } 23 | console.log('Updates applied successfully.'); 24 | console.log('stdout:', stdout); 25 | console.error('stderr:', stderr); 26 | }); 27 | }; 28 | 29 | updateDependencies(); 30 | ``` 31 | 32 | ## Set Up a Cron Job (Optional): 33 | If you want to run this script periodically, you can set up a `cron` job. For example, to run the script every day at midnight, you can add the following entry to your crontab: 34 | 35 | ``` 36 | 0 0 * * * /path/to/node /path/to/updateDependencies.js 37 | ``` 38 | 39 | Replace `/path/to/node and /path/to/updateDependencies.js` with the actual paths to your `Node.js` executable and the update script. 40 | 41 | ## Run the Script Manually: 42 | You can also run the script manually whenever you want to check for and apply updates: 43 | ``` 44 | node updateDependencies.js 45 | ``` 46 | 47 | This script uses npm-check-updates to check for updates and update the `package.json` file. It then runs `npm install` to install the updated dependencies. 48 | 49 | ## Explanation: 50 | Frequent updates help ensure that your application benefits from security patches and improvements provided by library maintainers. 51 | -------------------------------------------------------------------------------- /Recipe-32-SecureRouting/server.js: -------------------------------------------------------------------------------- 1 | 2 | // To implement secure routing mechanisms with JWT token-based authentication in a Node.js application, 3 | // you can use libraries like Express for handling HTTP requests and jsonwebtoken for JWT token creation 4 | // and verification. Below is a short example demonstrating how you can implement secure routing: 5 | 6 | // npm install express jsonwebtoken 7 | 8 | // Import necessary modules 9 | const express = require('express'); 10 | const jwt = require('jsonwebtoken'); 11 | 12 | const app = express(); 13 | const PORT = 3000; 14 | 15 | // Secret key for JWT token 16 | const secretKey = 'yourSecretKey'; 17 | 18 | // Middleware for checking JWT token 19 | const verifyToken = (req, res, next) => { 20 | const token = req.headers['authorization']; 21 | 22 | if (!token) { 23 | return res.status(403).json({ message: 'Unauthorized: No token provided' }); 24 | } 25 | 26 | jwt.verify(token, secretKey, (err, decoded) => { 27 | if (err) { 28 | return res.status(401).json({ message: 'Unauthorized: Invalid token' }); 29 | } 30 | req.user = decoded; 31 | next(); 32 | }); 33 | }; 34 | 35 | // Public route (accessible without authentication) 36 | app.get('/public', (req, res) => { 37 | res.json({ message: 'Public route, no authentication required' }); 38 | }); 39 | 40 | // Secure route (accessible only with a valid JWT token) 41 | app.get('/secure', verifyToken, (req, res) => { 42 | res.json({ message: 'Secure route, authentication successful', user: req.user }); 43 | }); 44 | 45 | // Login route to generate a JWT token (for demonstration purposes) 46 | app.post('/login', (req, res) => { 47 | // In a real application, you would authenticate the user and generate a JWT token 48 | const user = { id: 1, username: 'example' }; 49 | const token = jwt.sign(user, secretKey, { expiresIn: '1h' }); 50 | 51 | res.json({ token }); 52 | }); 53 | 54 | // Start the server 55 | app.listen(PORT, () => { 56 | console.log(`Server is running on http://localhost:${PORT}`); 57 | }); 58 | -------------------------------------------------------------------------------- /Recipe-33-ProtectAgainstXSSI/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | XSSI Protection Example 8 | 9 | 10 | 11 | 12 |

XSSI Protection Example

13 | 14 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /Recipe-33-ProtectAgainstXSSI/server.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const bodyParser = require('body-parser'); 3 | const app = express(); 4 | const port = 3000; 5 | 6 | app.use(bodyParser.json()); 7 | 8 | app.post('/api/external-script', (req, res) => { 9 | // Validate the request (you may add more validation logic) 10 | const { script } = req.body; 11 | 12 | if (!script || typeof script !== 'string') { 13 | return res.status(400).json({ error: 'Invalid script format' }); 14 | } 15 | 16 | // Securely handle the external script (you may add more security measures) 17 | const sanitizedScript = sanitizeScript(script); 18 | 19 | // Respond with the sanitized script 20 | res.json({ sanitizedScript }); 21 | }); 22 | 23 | function sanitizeScript(script) { 24 | // Implement your sanitization logic here 25 | // For example, you can use a library like DOMPurify to sanitize HTML content 26 | // Install DOMPurify using: npm install dompurify 27 | const DOMPurify = require('dompurify'); 28 | return DOMPurify.sanitize(script); 29 | } 30 | 31 | app.listen(port, () => { 32 | console.log(`Server is running on http://localhost:${port}`); 33 | }); 34 | -------------------------------------------------------------------------------- /Recipe-34-ManageAPIKeys/readme.md: -------------------------------------------------------------------------------- 1 | # Recipe 34: Securely Managing API Keys 2 | 3 | ## 1. Install the required `dotenv` packages 4 | 5 | ``` 6 | npm install dotenv 7 | ``` 8 | 9 | ## 2. Create a `.env` file in the root of your project and add your API keys: 10 | 11 | ``` 12 | API_KEY=your_api_key_here 13 | ``` 14 | 15 | ## 3. Create a `config.js` file to manage your configuration: 16 | 17 | ``` 18 | // config.js 19 | require('dotenv').config(); 20 | 21 | const config = { 22 | apiKey: process.env.API_KEY, 23 | }; 24 | 25 | module.exports = config; 26 | ``` 27 | 28 | ## 4. Use the configuration in your main application file: 29 | 30 | ``` 31 | // app.js 32 | const config = require('./config'); 33 | 34 | // Example: Accessing the API key 35 | const apiKey = config.apiKey; 36 | 37 | // Use the apiKey in your API requests or other parts of your application 38 | ``` 39 | 40 | ## 5. Make sure to add the `.env` file to your .gitignore to avoid exposing sensitive information: 41 | ``` 42 | .gitignore 43 | node_modules 44 | .env 45 | ``` -------------------------------------------------------------------------------- /Recipe-35-UserSessionData/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Session Example 8 | 9 | 10 | 11 | 12 | 13 | 14 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /Recipe-35-UserSessionData/server.js: -------------------------------------------------------------------------------- 1 | // Required packages 2 | // npm install express express-session cookie-parser crypto 3 | 4 | const express = require('express'); 5 | const session = require('express-session'); 6 | const cookieParser = require('cookie-parser'); 7 | const crypto = require('crypto'); 8 | 9 | const app = express(); 10 | 11 | // Use cookie-parser middleware to parse cookies 12 | app.use(cookieParser()); 13 | 14 | // Use express-session middleware for managing sessions 15 | app.use( 16 | session({ 17 | secret: 'your-secret-key', 18 | resave: false, 19 | saveUninitialized: true, 20 | cookie: { secure: true, maxAge: 60000 }, // set secure and session expiration time (in milliseconds) 21 | }) 22 | ); 23 | 24 | // Route to set a session variable 25 | app.get('/setSession', (req, res) => { 26 | req.session.user = { username: 'exampleUser' }; 27 | res.send('Session variable set!'); 28 | }); 29 | 30 | // Route to get the session variable 31 | app.get('/getSession', (req, res) => { 32 | const user = req.session.user; 33 | res.json(user || {}); 34 | }); 35 | 36 | app.listen(3000, () => { 37 | console.log('Server is running on port 3000'); 38 | }); 39 | -------------------------------------------------------------------------------- /Recipe-36-SecureWebWorkerComm/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Secure Web Worker Communication 8 | 9 | 10 | 11 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /Recipe-36-SecureWebWorkerComm/worker.js: -------------------------------------------------------------------------------- 1 | // Web worker script 2 | 3 | // Receive and decrypt messages from the main thread 4 | onmessage = async (event) => { 5 | const { encryptedMessage, iv } = event.data; 6 | const key = await crypto.subtle.generateKey( 7 | { name: 'AES-GCM', length: 256 }, 8 | true, 9 | ['encrypt', 'decrypt'] 10 | ); 11 | 12 | const decryptedMessage = await crypto.subtle.decrypt( 13 | { name: 'AES-GCM', iv }, 14 | key, 15 | encryptedMessage 16 | ); 17 | 18 | const decodedMessage = new TextDecoder().decode(decryptedMessage); 19 | console.log('Decrypted Message in Worker:', decodedMessage); 20 | 21 | // Example: Encrypt and send a response to the main thread 22 | const responseMessage = 'Hello from the web worker!'; 23 | const responseEncodedMessage = new TextEncoder().encode(responseMessage); 24 | const responseIv = crypto.getRandomValues(new Uint8Array(12)); 25 | 26 | const responseEncryptedMessage = await crypto.subtle.encrypt( 27 | { name: 'AES-GCM', iv: responseIv }, 28 | key, 29 | responseEncodedMessage 30 | ); 31 | 32 | postMessage({ 33 | encryptedMessage: responseEncryptedMessage, 34 | iv: responseIv, 35 | }, [responseEncryptedMessage]); 36 | }; 37 | -------------------------------------------------------------------------------- /Recipe-37-WebSocketRateLimit/server.js: -------------------------------------------------------------------------------- 1 | // Make sure to install the necessary packages before running the code: 2 | // npm install express express-rate-limit http ws 3 | 4 | const express = require('express'); 5 | const expressRateLimit = require('express-rate-limit'); 6 | const http = require('http'); 7 | const WebSocket = require('ws'); 8 | 9 | const app = express(); 10 | const server = http.createServer(app); 11 | const wss = new WebSocket.Server({ server }); 12 | 13 | // Rate limiting middleware 14 | const limiter = expressRateLimit({ 15 | windowMs: 60 * 1000, // 1 minute 16 | max: 5, // Max 5 connections per minute per IP 17 | message: 'Too many connections from this IP, please try again after a minute.', 18 | }); 19 | 20 | app.use('/ws', limiter); 21 | 22 | wss.on('connection', (ws, req) => { 23 | // Handle WebSocket connections here 24 | console.log('WebSocket connection established.'); 25 | 26 | // Close the connection if inactive for 5 minutes 27 | const connectionTimeout = setTimeout(() => { 28 | ws.terminate(); 29 | }, 5 * 60 * 1000); 30 | 31 | ws.on('message', (message) => { 32 | // Handle incoming messages 33 | console.log(`Received message: ${message}`); 34 | }); 35 | 36 | ws.on('close', () => { 37 | // Clear the connection timeout when the WebSocket is closed 38 | clearTimeout(connectionTimeout); 39 | console.log('WebSocket connection closed.'); 40 | }); 41 | }); 42 | 43 | // Start the server 44 | const PORT = process.env.PORT || 3000; 45 | server.listen(PORT, () => { 46 | console.log(`Server is listening on port ${PORT}`); 47 | }); 48 | -------------------------------------------------------------------------------- /Recipe-38-MultiFactorAuthentication/server.js: -------------------------------------------------------------------------------- 1 | // Install necessary packages: 2 | // npm install express speakeasy body-parser 3 | 4 | // server.js 5 | const express = require('express'); 6 | const bodyParser = require('body-parser'); 7 | const speakeasy = require('speakeasy'); 8 | 9 | const app = express(); 10 | const port = 3000; 11 | 12 | app.use(bodyParser.json()); 13 | app.use(bodyParser.urlencoded({ extended: true })); 14 | 15 | // Dummy user data (replace with your user data storage) 16 | const users = {}; 17 | 18 | // Endpoint to register a user and generate a secret 19 | app.post('/register', (req, res) => { 20 | const { username } = req.body; 21 | const secret = speakeasy.generateSecret(); 22 | 23 | users[username] = { secret: secret.base32, isVerified: false }; 24 | 25 | res.json({ secret: secret.otpauth_url }); 26 | }); 27 | 28 | // Endpoint to verify the user's token 29 | app.post('/verify', (req, res) => { 30 | const { username, token } = req.body; 31 | const user = users[username]; 32 | 33 | if (!user) { 34 | return res.status(404).json({ error: 'User not found' }); 35 | } 36 | 37 | const verified = speakeasy.totp.verify({ 38 | secret: user.secret, 39 | encoding: 'base32', 40 | token: token, 41 | window: 1, // Allow tokens from the past 1 step and the next 1 step (30-second window) 42 | }); 43 | 44 | if (verified) { 45 | user.isVerified = true; 46 | return res.json({ success: true, message: 'Token verified successfully' }); 47 | } else { 48 | return res.status(401).json({ error: 'Invalid token' }); 49 | } 50 | }); 51 | 52 | // Example protected route that requires MFA 53 | app.get('/dashboard', (req, res) => { 54 | const { username } = req.query; 55 | const user = users[username]; 56 | 57 | if (user && user.isVerified) { 58 | res.json({ message: 'Welcome to the dashboard!' }); 59 | } else { 60 | res.status(401).json({ error: 'User not authenticated or verified' }); 61 | } 62 | }); 63 | 64 | app.listen(port, () => { 65 | console.log(`Server is running on port ${port}`); 66 | }); 67 | -------------------------------------------------------------------------------- /Recipe-39-SecureWebSocketComm/server.js: -------------------------------------------------------------------------------- 1 | // To implement secure WebSocket communication using the wss protocol and validating incoming 2 | // messages in a Node.js environment, you can use the ws library for WebSocket handling and 3 | // the https module for secure server setup. Below is a concise example: 4 | 5 | // npm install ws 6 | 7 | const https = require('https'); 8 | const fs = require('fs'); 9 | const WebSocket = require('ws'); 10 | 11 | // Load SSL certificate and key 12 | const serverOptions = { 13 | key: fs.readFileSync('path/to/private-key.pem'), 14 | cert: fs.readFileSync('path/to/certificate.pem'), 15 | }; 16 | 17 | // Create an HTTPS server 18 | const server = https.createServer(serverOptions, (req, res) => { 19 | res.writeHead(200, { 'Content-Type': 'text/plain' }); 20 | res.end('Secure WebSocket Server\n'); 21 | }); 22 | 23 | // Create a WebSocket server attached to the HTTPS server 24 | const wss = new WebSocket.Server({ server }); 25 | 26 | // WebSocket connection handling 27 | wss.on('connection', (ws) => { 28 | console.log('Client connected'); 29 | 30 | // WebSocket message handling 31 | ws.on('message', (message) => { 32 | // Implement your message validation logic here 33 | console.log(`Received message: ${message}`); 34 | 35 | // Example: Broadcast the message to all connected clients 36 | wss.clients.forEach((client) => { 37 | if (client !== ws && client.readyState === WebSocket.OPEN) { 38 | client.send(message); 39 | } 40 | }); 41 | }); 42 | 43 | // WebSocket connection closed 44 | ws.on('close', () => { 45 | console.log('Client disconnected'); 46 | }); 47 | }); 48 | 49 | // Start the server on a specified port 50 | const PORT = 3000; 51 | server.listen(PORT, () => { 52 | console.log(`Server listening on https://localhost:${PORT}`); 53 | }); 54 | -------------------------------------------------------------------------------- /Recipe-40-URLInfoDisclosure/client.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Secure Webpage 8 | 9 | 10 | 11 | 12 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | Secure Webpage 52 | 53 | 54 | 55 | 56 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /Recipe-40-URLInfoDisclosure/server.js: -------------------------------------------------------------------------------- 1 | // Install required packages 2 | // npm install express body-parser 3 | 4 | const express = require('express'); 5 | const bodyParser = require('body-parser'); 6 | 7 | const app = express(); 8 | const PORT = 3000; 9 | 10 | // Middleware to parse JSON data 11 | app.use(bodyParser.json()); 12 | 13 | // In-memory user database (replace it with a real database in production) 14 | const users = [ 15 | { id: 1, username: 'user1', password: 'password1' }, 16 | // Add more users as needed 17 | ]; 18 | 19 | // Function to generate a session token 20 | function generateToken(userId) { 21 | // Use a secure method to generate a token (e.g., JSON Web Token) 22 | return `token_${userId}_${Date.now()}`; 23 | } 24 | 25 | // Middleware to validate the token 26 | function authenticateToken(req, res, next) { 27 | const token = req.header('Authorization'); 28 | 29 | if (!token) return res.status(401).send('Access Denied'); 30 | 31 | // Extract user ID from the token and validate it (add more security checks) 32 | const userId = parseInt(token.split('_')[1]); 33 | 34 | const user = users.find(u => u.id === userId); 35 | if (!user) return res.status(401).send('Invalid Token'); 36 | 37 | req.user = user; 38 | next(); 39 | } 40 | 41 | // Route to authenticate and return a token 42 | app.post('/login', (req, res) => { 43 | const { username, password } = req.body; 44 | 45 | const user = users.find(u => u.username === username && u.password === password); 46 | if (!user) return res.status(401).send('Invalid Credentials'); 47 | 48 | const token = generateToken(user.id); 49 | res.json({ token }); 50 | }); 51 | 52 | // Protected route that requires authentication 53 | app.get('/protected', authenticateToken, (req, res) => { 54 | res.send(`Welcome, ${req.user.username}!`); 55 | }); 56 | 57 | // Start the server 58 | app.listen(PORT, () => { 59 | console.log(`Server is running on port ${PORT}`); 60 | }); 61 | -------------------------------------------------------------------------------- /Recipe-41-URLScriptInjection/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Sanitize and validate URL 8 | 9 | 10 | '; 13 | const sanitizedURL = encodeURI(userInputURL); 14 | 15 | 16 | 17 | 18 | 19 |

Sanitize and validate URL

20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /Recipe-42-EscapeHtml/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | XSS Prevention 8 | 9 | 10 | 11 |
12 | 13 | '; 21 | var escapedInput = escapeHtml(userInput); 22 | 23 | document.getElementById('output').innerHTML = escapedInput; 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Recipe-42-EscapeHtml/server.js: -------------------------------------------------------------------------------- 1 | // Ensure you have the jsdom package installed in your Node.js project: 2 | 3 | const { JSDOM } = require('jsdom'); 4 | 5 | function escapeHtml(input) { 6 | const dom = new JSDOM(input); 7 | return dom.window.document.body.textContent || ""; 8 | } 9 | 10 | // Example usage 11 | const userInput = ''; 12 | const escapedInput = escapeHtml(userInput); 13 | 14 | console.log(escapedInput); 15 | --------------------------------------------------------------------------------