Introduction

Dans le domaine du développement logiciel, en particulier lorsqu’il s’agit de données sensibles, la sécurité de la mémoire est primordiale. Les développeurs rencontrent souvent des situations où ils doivent protéger des informations sensibles telles que des clés de licence ou des mots de passe des regards indiscrets. Une question qui se pose dans de tels contextes est : Comment pouvons-nous créer un allocateur de mémoire sécurisé en C++ qui empêche la pagination sur disque et qui est difficile d’accès via des débogueurs ?

Dans cet article, nous allons examiner les problèmes liés à la sécurité de la mémoire et explorer comment implémenter un allocateur C++ qui répond à ces exigences.

Comprendre les Défis

Avant de plonger dans la solution, il est essentiel de comprendre certains des défis qui accompagnent la protection de la mémoire :

  • Prévention de la Pagination sur Disque : Sur les systèmes utilisant la mémoire virtuelle, la mémoire peut être paginée sur disque, la rendant vulnérable aux accès non autorisés. Lorsque la mémoire est paginée, elle est stockée dans l’espace d’échange, qui peut être accessible à d’autres processus.

  • Accès via Débogueurs : Les débogueurs peuvent accéder et manipuler la mémoire, ce qui rend crucial de trouver des moyens de minimiser ce risque, en particulier pour les informations sensibles.

  • Limitations au Niveau du Système d’Exploitation : Quelles que soient nos intentions, le système d’exploitation a le contrôle sur les processus et peut potentiellement lire le contenu de la mémoire.

Un Allocateur de Mémoire Sûr en C++

Malgré ces défis, nous pouvons implémenter un allocateur personnalisé en C++ en utilisant des fonctions de l’API Windows. Décomposons la solution étape par étape :

1. Utilisation de VirtualAlloc

L’outil principal pour allouer de la mémoire de manière sécurisée est VirtualAlloc. Cette fonction nous permet d’allouer de la mémoire d’une manière spécifiée :

  • Nous pouvons définir des niveaux de protection pour contrôler comment la mémoire allouée peut être accessible.
LPVOID pMem = ::VirtualAlloc(NULL, allocLen, allocType, allocProtect);

Ici, allocType est défini sur MEM_COMMIT, et allocProtect est défini sur PAGE_READWRITE, permettant à la fois l’accès en lecture et en écriture.

2. Verrouillage de la Mémoire

Pour empêcher le système d’exploitation de paginer la mémoire sur disque, nous utilisons la fonction VirtualLock :

if (pMem != NULL) {
    ::VirtualLock(pMem, allocLen);
}

Cependant, gardez à l’esprit que VirtualLock impose des contraintes sur la quantité de mémoire que vous pouvez allouer, ce qui peut être gérable selon vos besoins.

3. Désallocation Sûre de la Mémoire

Lors de la désallocation de la mémoire, il est essentiel de mettre à zéro les contenus sensibles avant de la libérer. Cela peut être fait en utilisant SecureZeroMemory :

::SecureZeroMemory(_pPtr, allocLen);
::VirtualUnlock(_pPtr, allocLen);
::VirtualFree(_pPtr, 0, MEM_RELEASE);

Exemple Complet

Voici l’implémentation complète de notre allocateur de mémoire sûr :

template<class _Ty>
class LockedVirtualMemAllocator : public std::allocator<_Ty> {
public:
    template<class _Other>
    LockedVirtualMemAllocator<_Ty>& operator=(const LockedVirtualMemAllocator<_Other>&) {
        return (*this);
    }

    template<class Other>
    struct rebind {
        typedef LockedVirtualMemAllocator<Other> other;
    };

    pointer allocate(size_type _n) {
        SIZE_T allocLen = (_n * sizeof(_Ty));
        DWORD allocType = MEM_COMMIT;
        DWORD allocProtect = PAGE_READWRITE;
        LPVOID pMem = ::VirtualAlloc(NULL, allocLen, allocType, allocProtect);
        if (pMem != NULL) {
            ::VirtualLock(pMem, allocLen);
        }
        return reinterpret_cast<pointer>(pMem);
    }

    void deallocate(void* _pPtr, size_type _n) {
        if (_pPtr != NULL) {
            SIZE_T allocLen = (_n * sizeof(_Ty));
            ::SecureZeroMemory(_pPtr, allocLen);
            ::VirtualUnlock(_pPtr, allocLen);
            ::VirtualFree(_pPtr, 0, MEM_RELEASE);
        }
    }
};

// Exemple d'Utilisation
typedef std::basic_string<char, std::char_traits<char>, LockedVirtualMemAllocator<char>> modulestring_t;

Conclusion

Créer un allocateur de mémoire sûr en C++ est une tâche complexe qui implique de naviguer à travers diverses contraintes au niveau du système et des défis de sécurité. Bien qu’il soit impossible d’atteindre une protection totale contre l’accès à la mémoire, l’utilisation de fonctions comme VirtualAlloc, VirtualLock, et SecureZeroMemory peut considérablement améliorer la sécurité des données sensibles.

Il est essentiel de garder à l’esprit qu’aucun système ne peut être entièrement sécurisé face au propriétaire de l’appareil. Ainsi, comprendre ces limitations permet aux développeurs de créer des applications plus robustes et résilientes.

Pour quiconque intéressé par des perspectives plus approfondies, des ressources telles que Practical Cryptography de Neil Ferguson et Bruce Schneier peuvent fournir un contexte précieux en matière de pratiques cryptographiques et de méthodologies de gestion sécurisée de la mémoire.