Sécurité des applications C++ : top 10 des vulnérabilités pour débutants
\n\nLe langage C++ offre au développeur un contrôle immense sur la mémoire et les performances, mais cette liberté s'accompagne d'une grande responsabilité. Selon OWASP et CWE, les vulnérabilités dans les applications C++ surviennent souvent en raison d'une mauvaise gestion de la mémoire, d'un manque de vérification des limites et d'une inattention lors de la manipulation des pointeurs. Dans cet article, nous allons analyser les 10 vulnérabilités les plus courantes rencontrées par les débutants. Vous apprendrez comment les détecter et, surtout, comment les éviter.
\n\n1. Débordement de tampon (Buffer Overflow)
\nUn classique du genre. Cette vulnérabilité se produit lorsque le programme écrit des données au-delà des limites du tampon alloué. Un attaquant peut écraser les zones mémoire adjacentes, y compris l'adresse de retour de la fonction, et prendre le contrôle de l'exécution du code.
\n\n// Code vulnérable\nvoid vulnerable() {\n char buffer[10];\n std::cout << "Entrez une chaîne : ";\n std::cin >> buffer; // Pas de vérification de la longueur !\n std::cout << buffer << std::endl;\n}\n\n// Version sécurisée\nvoid safe() {\n char buffer[10];\n std::cout << "Entrez une chaîne : ";\n std::cin.getline(buffer, sizeof(buffer));\n std::cout << buffer << std::endl;\n}\n\nComment se protéger : Utilisez toujours des fonctions avec limitation de longueur (strncpy, snprintf, std::string). Évitez gets() et strcpy().
2. Fuites mémoire (Memory Leaks)
\nSi vous avez alloué de la mémoire avec new mais oublié de la libérer avec delete, le programme va progressivement "consommer" la RAM. Pour les applications serveur, c'est une catastrophe — après quelques heures de fonctionnement, le service plantera avec l'erreur std::bad_alloc.
// Exemple de fuite\nvoid leak() {\n int* ptr = new int(42);\n // Oubli de delete ptr;\n}\n\n// Approche correcte\nvoid noLeak() {\n std::unique_ptr<int> ptr = std::make_unique<int>(42);\n // La mémoire sera libérée automatiquement à la sortie de la portée\n}\n\nComment se protéger : Utilisez des pointeurs intelligents (std::unique_ptr, std::shared_ptr). En dernier recours — RAII (Resource Acquisition Is Initialization).
3. Pointeurs pendants (Dangling Pointers)
\nUn pointeur fait référence à une mémoire déjà libérée. Déréférencer un tel pointeur est un comportement indéfini (UB). Cela peut entraîner un crash du programme ou, pire, l'exécution de code malveillant.
\n\nint* dangling() {\n int x = 10;\n return &x; // Erreur : on retourne l'adresse d'une variable locale !\n}\n\n// Correct — allouer la mémoire sur le tas\nint* safe() {\n int* ptr = new int(10);\n return ptr;\n}\n\nComment se protéger : Après delete, assignez toujours nullptr au pointeur. Utilisez des pointeurs intelligents.
4. Débordement d'entiers (Integer Overflow)
\nLorsque le résultat d'une opération arithmétique dépasse les limites du type de données, un débordement se produit. Cela peut entraîner des conséquences inattendues, comme l'allocation d'un tampon trop petit.
\n\nvoid check_size(size_t user_size) {\n // Vulnérabilité : si user_size est très grande, user_size + 1 peut devenir petite\n char* buffer = new char[user_size + 1];\n // ...\n}\n\n// Protection\nvoid safe_check(size_t user_size) {\n if (user_size > std::numeric_limits<size_t>::max() - 1) {\n throw std::overflow_error("Taille trop grande");\n }\n char* buffer = new char[user_size + 1];\n}\n\nComment se protéger : Vérifiez les limites avant les opérations arithmétiques. Utilisez __builtin_add_overflow (GCC/Clang) ou des bibliothèques d'arithmétique sécurisée.
5. Pointeurs nuls (Null Pointer Dereference)
\nDéréférencement d'un pointeur nul