Trang chủ / Blog / Bảo mật ứng dụng Web 2026: OWASP Top 10 và cách phòng chống

Bảo mật ứng dụng Web 2026: OWASP Top 10 và cách phòng chống

Bảo mật ứng dụng Web 2026: OWASP Top 10 và cách phòng chống
## OWASP Top 10 - 2026 Edition

OWASP (Open Web Application Security Project) cập nhật danh sách 10 lỗ hổng bảo mật phổ biến nhất mỗi 3 năm. Phiên bản 2026 có một số thay đổi quan trọng về AI và supply chain.

## 1. Broken Access Control

### Vấn đề

Người dùng truy cập được resource không thuộc quyền của họ.

### Ví dụ lỗ hổng

\`\`\`typescript
// ❌ BAD: Không kiểm tra ownership
app.get('/api/documents/:id', async (req, res) => {
  const doc = await db.documents.findById(req.params.id);
  res.json(doc); // User có thể xem document của người khác!
});
\`\`\`

### Giải pháp

\`\`\`typescript
// ✅ GOOD: Kiểm tra ownership
app.get('/api/documents/:id', authMiddleware, async (req, res) => {
  const doc = await db.documents.findById(req.params.id);

  if (!doc) {
    return res.status(404).json({ error: 'Not found' });
  }

  if (doc.userId !== req.user.id && !req.user.isAdmin) {
    return res.status(403).json({ error: 'Forbidden' });
  }

  res.json(doc);
});
\`\`\`

### Best practices

- Dùng middleware kiểm tra quyền trước mọi route
- Implement RBAC (Role-Based Access Control)
- Test access control với nhiều user roles

## 2. Cryptographic Failures

### Vấn đề

Dữ liệu nhạy cảm không được mã hóa hoặc dùng thuật toán yếu.

### Ví dụ lỗ hổng

\`\`\`typescript
// ❌ BAD: Lưu password plain text
const user = {
  email: '[email protected]',
  password: 'mypassword123' // NGUY HIỂM!
};
\`\`\`

### Giải pháp

\`\`\`typescript
// ✅ GOOD: Hash password với bcrypt
import bcrypt from 'bcryptjs';

const hashedPassword = await bcrypt.hash(password, 12);
const user = {
  email: '[email protected]',
  passwordHash: hashedPassword
};

// Verify
const isValid = await bcrypt.compare(inputPassword, user.passwordHash);
\`\`\`

### Encryption cho sensitive data

\`\`\`typescript
import crypto from 'crypto';

const algorithm = 'aes-256-gcm';
const key = crypto.scryptSync(process.env.ENCRYPTION_KEY!, 'salt', 32);

function encrypt(text: string): string {
  const iv = crypto.randomBytes(16);
  const cipher = crypto.createCipheriv(algorithm, key, iv);
  const encrypted = Buffer.concat([cipher.update(text, 'utf8'), cipher.final()]);
  const authTag = cipher.getAuthTag();
  return \`\${iv.toString('hex')}:\${authTag.toString('hex')}:\${encrypted.toString('hex')}\`;
}

function decrypt(encrypted: string): string {
  const [ivHex, authTagHex, encryptedHex] = encrypted.split(':');
  const iv = Buffer.from(ivHex, 'hex');
  const authTag = Buffer.from(authTagHex, 'hex');
  const encryptedData = Buffer.from(encryptedHex, 'hex');

  const decipher = crypto.createDecipheriv(algorithm, key, iv);
  decipher.setAuthTag(authTag);
  return decipher.update(encryptedData) + decipher.final('utf8');
}
\`\`\`

## 3. Injection (SQL, NoSQL, Command)

### SQL Injection

\`\`\`typescript
// ❌ BAD
const userId = req.query.id;
const user = await db.query(\`SELECT * FROM users WHERE id = \${userId}\`);
// Attacker: ?id=1 OR 1=1 -- ⇒ lấy tất cả users!
\`\`\`

\`\`\`typescript
// ✅ GOOD: Parameterized query
const userId = req.query.id;
const user = await db.query('SELECT * FROM users WHERE id = ?', [userId]);
\`\`\`

### NoSQL Injection (MongoDB)

\`\`\`typescript
// ❌ BAD
const user = await User.findOne({ email: req.body.email });
// Attacker: { "email": { "$ne": null } } ⇒ bypass authentication!
\`\`\`

\`\`\`typescript
// ✅ GOOD: Validate input type
import { z } from 'zod';

const loginSchema = z.object({
  email: z.string().email(),
  password: z.string().min(8)
});

const { email, password } = loginSchema.parse(req.body);
const user = await User.findOne({ email });
\`\`\`

## 4. Insecure Design

### Ví dụ: Password reset không an toàn

\`\`\`typescript
// ❌ BAD: Token dự đoán
const resetToken = user.id + Date.now();
\`\`\`

\`\`\`typescript
// ✅ GOOD: Cryptographically secure token
import crypto from 'crypto';

const resetToken = crypto.randomBytes(32).toString('hex');
const hashedToken = crypto.createHash('sha256').update(resetToken).digest('hex');

await db.users.update(userId, {
  resetToken: hashedToken,
  resetTokenExpiry: new Date(Date.now() + 3600000) // 1 hour
});
\`\`\`

## 5. Security Misconfiguration

### Ví dụ

\`\`\`typescript
// ❌ BAD: CORS cho phép tất cả
app.use(cors({ origin: '*' }));
\`\`\`

\`\`\`typescript
// ✅ GOOD: CORS restricted
const allowedOrigins = [
  'https://myapp.com',
  'https://admin.myapp.com'
];

app.use(cors({
  origin: (origin, callback) => {
    if (!origin || allowedOrigins.includes(origin)) {
      callback(null, true);
    } else {
      callback(new Error('Not allowed by CORS'));
    }
  },
  credentials: true
}));
\`\`\`

### Security Headers

\`\`\`typescript
import helmet from 'helmet';

app.use(helmet({
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ["'self'"],
      styleSrc: ["'self'", "'unsafe-inline'"],
      scriptSrc: ["'self'"],
      imgSrc: ["'self'", "data:", "https:"],
    },
  },
  hsts: {
    maxAge: 31536000,
    includeSubDomains: true,
    preload: true
  }
}));
\`\`\`

## 6. Vulnerable Components

### Kiểm tra dependencies

\`\`\`bash
# npm audit
npm audit --audit-level=high

# Snyk
npx snyk test

# OWASP Dependency Check
npx @cyclonedx/cyclonedx-npm --output-file sbom.json
\`\`\`

### Tự động cập nhật

\`\`\`yaml
# Dependabot config
version: 2
updates:
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "weekly"
    open-pull-requests-limit: 10
\`\`\`

## 7. Authentication Failures

### Rate Limiting

\`\`\`typescript
import rateLimit from 'express-rate-limit';

const loginLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 5, // 5 requests
  message: 'Too many login attempts, please try again later'
});

app.post('/api/login', loginLimiter, async (req, res) => {
  // login logic
});
\`\`\`

### Multi-Factor Authentication

\`\`\`typescript
import speakeasy from 'speakeasy';
import QRCode from 'qrcode';

// Generate secret
const secret = speakeasy.generateSecret({
  name: 'MyApp ([email protected])'
});

// Generate QR code
const qrCode = await QRCode.toDataURL(secret.otpauth_url!);

// Verify token
const verified = speakeasy.totp.verify({
  secret: secret.base32,
  encoding: 'base32',
  token: userToken,
  window: 2
});
\`\`\`

## 8. Software and Data Integrity Failures

### Subresource Integrity (SRI)

\`\`\`html
<!-- Load CDN với integrity check -->
<script
  src="https://cdn.jsdelivr.net/npm/react@18/umd/react.production.min.js"
  integrity="sha384-xyz..."
  crossorigin="anonymous">
</script>
\`\`\`

## 9. Logging and Monitoring Failures

### Structured Logging

\`\`\`typescript
import winston from 'winston';

const logger = winston.createLogger({
  level: 'info',
  format: winston.format.json(),
  transports: [
    new winston.transports.File({ filename: 'error.log', level: 'error' }),
    new winston.transports.File({ filename: 'combined.log' })
  ]
});

// Log security events
logger.warn('Failed login attempt', {
  email: req.body.email,
  ip: req.ip,
  userAgent: req.get('user-agent')
});
\`\`\`

## 10. Server-Side Request Forgery (SSRF)

\`\`\`typescript
// ❌ BAD: Cho phép fetch bất kỳ URL
app.post('/api/fetch', async (req, res) => {
  const response = await fetch(req.body.url);
  // Attacker: { url: "http://localhost:8080/admin" }
});
\`\`\`

\`\`\`typescript
// ✅ GOOD: Whitelist domains
const allowedDomains = ['api.example.com', 'cdn.example.com'];

app.post('/api/fetch', async (req, res) => {
  const url = new URL(req.body.url);

  if (!allowedDomains.includes(url.hostname)) {
    return res.status(400).json({ error: 'Invalid domain' });
  }

  const response = await fetch(url.toString());
  res.json(await response.json());
});
\`\`\`

## Checklist tổng hợp

- [ ] Tất cả endpoints có authentication
- [ ] Tất cả sensitive data được encrypt
- [ ] Dùng parameterized queries
- [ ] Validate tất cả user input
- [ ] Security headers (helmet.js)
- [ ] Rate limiting cho login/API
- [ ] Dependencies được audit hàng tuần
- [ ] Logging các security events
- [ ] HTTPS everywhere
- [ ] CSP headers configured

## Kết luận

Bảo mật không phải là feature, mà là foundation. Implement từ đầu, không thể \"thêm sau\".
Trần Đức Anh

Trần Đức Anh

Senior Architect · TechCorp

Chuyên gia kiến trúc hệ thống với 10+ năm kinh nghiệm thiết kế các hệ thống lớn phục vụ hàng triệu người dùng. Từng làm việc tại Grab và Tiki, đam mê microservices và event-driven architecture.

Bạn có dự án cần tư vấn?

Đội ngũ chuyên gia của chúng tôi sẵn sàng hỗ trợ bạn từ ý tưởng đến triển khai.