Các lỗ hổng bảo mật phổ biến trong Node.js API và cách phòng tránh?
VI- SQL injection (OWASP A03):
db.query('SELECT * FROM users WHERE id = ' + req.params.id) → attacker truyền 1 OR 1=1 dump toàn bộ DB. - Fix: parameterized queries
db.query('SELECT * FROM users WHERE id = $1', [id]) hoặc ORM. - NoSQL injection MongoDB:
{ username: req.body.username } với body { username: { $gt: '' } } bypass auth. - Fix: validate với Zod/Joi trước khi query.
- XSS (A07): lưu
<script>alert(1)</script> vào DB rồi render unescaped → steal cookies. - Fix: escape output, CSP headers, DOMPurify.
- ReDoS:
/(a+)+/.test(userInput) với input 'aaaaaaaaab' → exponential backtracking block event loop hàng giây. - Fix:
safe-regex package kiểm tra pattern, timeout regex execution. - Directory traversal:
fs.readFile('/uploads/' + req.query.file) với file=../../etc/passwd. - Fix:
path.resolve() + verify kết quả starts with allowed directory. - Prototype pollution:
obj[userKey] = userValue với key __proto__ corrupt Object prototype. - Fix:
Object.create(null) cho plain objects, validate keys. - Dependency vulnerabilities:
npm audit + npm audit fix, snyk cho CI/CD. - Helmet.js: một lệnh
app.use(helmet()) set 11 security headers (X-Frame-Options, HSTS, CSP cơ bản, nosniff).
EN- SQL injection (OWASP A03):
db.query('SELECT * FROM users WHERE id = ' + req.params.id) → attacker passes 1 OR 1=1 to dump the entire DB. - Fix: parameterized queries
db.query('SELECT * FROM users WHERE id = $1', [id]) or use an ORM. - NoSQL injection (MongoDB):
{ username: req.body.username } with body { username: { $gt: '' } } bypasses authentication. - Fix: validate with Zod/Joi before querying.
- XSS (A07): storing
<script>alert(1)</script> in the DB and rendering it unescaped → steals cookies. - Fix: escape output, set CSP headers, use DOMPurify.
- ReDoS:
/(a+)+/.test(userInput) with input 'aaaaaaaaab' → exponential backtracking blocks the event loop for seconds. - Fix: use the
safe-regex package to check patterns, and add a timeout for regex execution. - Directory traversal:
fs.readFile('/uploads/' + req.query.file) with file=../../etc/passwd. - Fix:
path.resolve() and verify the result starts with the allowed directory. - Prototype pollution:
obj[userKey] = userValue with key __proto__ corrupts the Object prototype. - Fix:
Object.create(null) for plain objects, validate keys. - Dependency vulnerabilities:
npm audit + npm audit fix, snyk in CI/CD. - Helmet.js:
app.use(helmet()) sets 11 security headers in one line (X-Frame-Options, HSTS, basic CSP, nosniff).