Introduction: Why TypeScript Application Security Matters from Day One
TypeScript is a superset of JavaScript that adds static typing. Many beginner developers mistakenly believe that typing automatically solves all security problems. In reality, TypeScript compiles to regular JavaScript, and all vulnerabilities inherent to JS remain relevant. In this article, we will break down the top 10 vulnerabilities that beginners face and show how to avoid them.
1. XSS (Cross-Site Scripting)
XSS is one of the most common attacks. An attacker injects a malicious script into a page, which executes in the user's browser.
Example of vulnerable code:
function renderUserInput(input: string): string { return `<div>${input}</div>`;}
// Attacker passes: <script>alert('XSS')</script>How to fix:
Always escape user input. Use libraries like DOMPurify or built-in escaping methods:
import DOMPurify from 'dompurify';
function safeRender(input: string): string { const clean = DOMPurify.sanitize(input); return `<div>${clean}</div>`;}2. SQL Injections
If you form SQL queries by concatenating strings with user input, you open the door to SQL injections.
Vulnerable code:
function getUser(username: string): Promise<User> { const query = `SELECT * FROM users WHERE username = '${username}'`; return db.execute(query);}Safe alternative:
function getUserSafe(username: string): Promise<User> { const query = 'SELECT * FROM users WHERE username = $1'; return db.execute(query, [username]); // parameterized query}3. Unsafe Password Storage
Never store passwords in plain text. Use bcrypt or argon2 for hashing.
import bcrypt from 'bcrypt';
async function hashPassword(password: string): Promise<string> { const saltRounds = 12; return bcrypt.hash(password, saltRounds);}
async function verifyPassword(password: string, hash: string): Promise<boolean> { return bcrypt.compare(password, hash);}4. Insecure Direct Object References (IDOR)
When a user can access other users' data simply by changing an ID in the URL.
Example:
// Vulnerable: any user can get any orderapp.get('/api/order/:id', (req, res) => { const order = getOrderById(req.params.id); res.json(order);});
// Safe: check ownershipapp.get('/api/order/:id', (req, res) => { const order = getOrderById(req.params.id); if (order.userId !== req.user.id) { return res.status(403).json({ error: 'Forbidden' }); } res.json(order);});5. Using Outdated Dependencies
TypeScript projects often use npm packages. Outdated versions may contain known vulnerabilities.
- Regularly run
npm audit - Use
npm updateto update packages - Set up automatic checks in CI/CD
6. Insufficient Input Validation
TypeScript types are not checked at runtime. Always validate data on the server.
interface UserInput { email: string; age: number;}
// Not enough: TypeScript types do not protect at runtimefunction processUser(input: UserInput) { // ...}
// Correct: use Zod or Joi for validationimport { z } from 'zod';
const UserSchema = z.object({ email: z.string().email(), age: z.number().min(18).max(120)});
function processUserSafe(input: unknown) { const validate