
Secure Coding in TypeScript: A Comprehensive Guide
Introduction
Ensuring security in TypeScript applications is crucial for protecting sensitive data, preventing vulnerabilities, and maintaining application integrity. In this guide, we will explore best practices for secure coding in TypeScript, covering key concepts, common pitfalls, and practical solutions.
1. Use Strict Type Checking
TypeScript provides strong typing, which helps catch potential errors early in development. Always enable strict
mode in your tsconfig.json
:
{
"compilerOptions": {
"strict": true
}
}
Benefits:
- Prevents implicit
any
types. - Ensures better type inference.
- Helps in detecting runtime errors during development.
2. Avoid Using any
Using any
defeats the purpose of TypeScript's type system. Instead, define explicit types:
// Bad Practice
let userData: any = fetchUserData();
// Good Practice
interface User {
id: number;
name: string;
}
let userData: User = fetchUserData();
3. Validate User Input
Never trust user input. Use validation libraries like zod
or class-validator
to ensure data integrity.
import { z } from 'zod';
const userSchema = z.object({
name: z.string().min(2),
age: z.number().positive()
});
const userData = getUserInput();
const validatedData = userSchema.parse(userData);
4. Use Environment Variables for Sensitive Data
Never hardcode secrets in your source code. Use .env
files and process.env
:
import dotenv from 'dotenv';
dotenv.config();
const dbPassword = process.env.DB_PASSWORD;
5. Implement Proper Error Handling
Avoid exposing sensitive information in error messages. Use a global error handler:
class AppError extends Error {
constructor(message: string, public statusCode: number) {
super(message);
}
}
function errorHandler(err: AppError) {
console.error(err.message);
}
6. Secure API Requests
When making API requests, use HTTPS and authenticate properly:
const fetchSecureData = async () => {
const response = await fetch('https://api.example.com/data', {
method: 'GET',
headers: {
Authorization: `Bearer undefined`
}
});
return response.json();
};
7. Prevent Cross-Site Scripting (XSS)
Sanitize user-generated content before rendering:
import DOMPurify from 'dompurify';
const safeHTML = DOMPurify.sanitize(userInput);
8. Avoid SQL Injection
Use parameterized queries when interacting with databases:
import { Pool } from 'pg';
const pool = new Pool();
const userId = 1;
const result = await pool.query('SELECT * FROM users WHERE id = $1', [userId]);
9. Keep Dependencies Updated
Regularly update dependencies to avoid security vulnerabilities:
npm audit fix --force
10. Implement Role-Based Access Control (RBAC)
Restrict user permissions based on roles:
const userRoles = {
admin: ['read', 'write', 'delete'],
user: ['read']
};
const hasPermission = (role: string, action: string) => userRoles[role]?.includes(action);
Conclusion
By following these secure coding practices, you can enhance the security of your TypeScript applications, mitigate risks, and ensure robust software development.