Retour au blog
Sécurité
28 août 2024
18 min
Équipe Dev Ring

Sécurité des applications web modernes : OWASP Top 10 2024

Ce guide passe en revue les risques majeurs et propose des contre-mesures concrètes côté serveur, client et CI/CD, avec des extraits prêts à l’emploi.

OWASPWeb SecurityAPI SecurityDevSecOpsCSP

Validation d'entrée et sérialisation

Valider côté serveur et côté client, puis sérialiser strictement les sorties.

Validation avec Zod dans une Server Action

ts
'use server'
import { z } from 'zod'
import { db } from '@/lib/db'
import { revalidatePath } from 'next/cache'

const schema = z.object({
  title: z.string().min(3).max(120),
  content: z.string().min(1),
})

export async function createSecurePost(formData: FormData) {
  const parsed = schema.safeParse({
    title: formData.get('title'),
    content: formData.get('content'),
  })
  if (!parsed.success) throw new Error('Invalid input')
  await db.post.create({ data: parsed.data })
  revalidatePath('/posts')
}

Protection contre l'injection

Utiliser des requêtes paramétrées et échapper systématiquement les sorties HTML.

Requêtes paramétrées (exemple Prisma)

ts
// ⚠️ Anti-pattern (concaténation)
// await prisma.$queryRawUnsafe(`SELECT * FROM users WHERE email = '${email}'`)

// ✅ Paramétré
const users = await prisma.$queryRaw`SELECT * FROM users WHERE email = ${email}`

Authentification, sessions et cookies

Appliquer des attributs sécurisés et des durées limitées.

Cookies HttpOnly/secure + SameSite

ts
import { cookies } from 'next/headers'
import { serialize } from 'cookie'

export function setSessionCookie(token: string) {
  cookies().set(
    'session',
    token,
    {
      httpOnly: true,
      secure: true,
      sameSite: 'lax',
      maxAge: 60 * 60, // 1h
      path: '/',
    }
  )
}

En-têtes de sécurité

Configurer CSP, HSTS et autres en-têtes au niveau de la plateforme.

next.config.js — en-têtes de sécurité (Edge ou Node)

ts
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  async headers() {
    return [
      {
        source: '/(.*)',
        headers: [
          { key: 'Strict-Transport-Security', value: 'max-age=63072000; includeSubDomains; preload' },
          { key: 'X-Content-Type-Options', value: 'nosniff' },
          { key: 'X-Frame-Options', value: 'DENY' },
          { key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
          { key: 'Permissions-Policy', value: 'camera=(), microphone=(), geolocation=()' },
          { key: 'Content-Security-Policy', value: "default-src 'self'; img-src 'self' data:; script-src 'self'; style-src 'self' 'unsafe-inline'" },
        ],
      },
    ]
  },
}
module.exports = nextConfig

CSRF et SameSite

Protéger les mutations par un jeton et valider l'origine/referer.

CSRF token dans une Server Action

ts
'use server'
import { cookies, headers } from 'next/headers'

export async function mutateWithCsrf(formData: FormData) {
  const token = formData.get('csrf_token')
  const cookie = cookies().get('csrf')?.value
  const origin = headers().get('origin')

  if (!token || token !== cookie) throw new Error('CSRF invalid')
  if (!origin?.startsWith('https://votre-domaine.tld')) throw new Error('Origin invalid')

  // ... mutation
}

Stockage de secrets

Isoler les secrets dans le gestionnaire de secrets, jamais dans le code ou les logs.

Exemples d’injection de secrets en CI

bash
# GitHub Actions
echo "API_KEY=${{ secrets.API_KEY }}" >> $GITHUB_ENV

# Vercel
# vercel env add API_KEY production
# vercel env pull .env.local

CI/CD sécurisé

Intégrer SCA/SAST/DAST, signatures d’artefacts et approbations manuelles.

Pipeline DevSecOps (extrait)

yaml
jobs:
  build:
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npm audit --audit-level=high
      - name: CodeQL
        uses: github/codeql-action/analyze@v3
      - name: Container scan
        uses: aquasecurity/trivy-action@0.18.0
  deploy:
    needs: [build]
    environment: production
    steps:
      - name: Approvals
        uses: some/action-manual-approval@v1

Journalisation et détection

Mettre en place des logs structurés, corrélables et compatibles SIEM.

Logger structuré et PII-safe

ts
import pino from 'pino'
const logger = pino({ level: 'info', redact: ['req.headers.authorization', 'user.email'] })

export function logRequest(req: { id: string; path: string; ip: string }) {
  logger.info({ reqId: req.id, path: req.path, ip: req.ip }, 'http_request')
}

Conclusion

La réduction de la surface d’attaque combine hygiène de code, configuration robuste et automatisation DevSecOps. Les extraits ci-dessus constituent une base opérationnelle pour aligner vos pratiques sur l’OWASP Top 10 2024.