Liste de contrôle minimale de sécurité pour le développement d'applications Windows
· Mis à jour le: · Go Komura · Développement Windows, Sécurité, Conception, C# / .NET, Win32
Télécharger la version Excel de la liste de contrôle
Quand on parle de sécurité des applications Windows, le sujet a tendance à s’élargir rapidement. Zero trust, EDR, SBOM, gestion des certificats, gestion des vulnérabilités. Tout cela est important, mais en pratique, il existe d’abord plusieurs bases qu’on ne veut pas manquer.
C’est particulièrement vrai pour des applications comme celles-ci, où combler les lacunes de base est plus efficace que des « défenses avancées » :
- applications de bureau WPF / WinForms / WinUI
- applications Win32 en C++ / C#
- intégration d’équipements, intégration de fichiers, connexions à des bases de données, outils de distribution interne
- applications métier disposant d’un mécanisme de mise à jour automatique
- configurations incluant des services Windows ou des EXE auxiliaires
Dans le développement d’applications Windows, il est plus réaliste de commencer par ne laisser aucune faille manifestement dangereuse, plutôt que de vouloir tout perfectionner d’un coup. Ici, nous organisons, dans l’ordre conception, implémentation, distribution et exploitation, les points minimaux à ne pas manquer, sous une forme facile à vérifier.
1. La conclusion, d’abord
- Ce qu’on ne veut pas manquer en premier lieu, c’est de ne pas demander de privilèges administrateur inutiles, de signer le code, de ne pas conserver d’informations confidentielles en clair, et de ne jamais désactiver la vérification des certificats.
- Pour une application Windows, les artefacts de distribution eux-mêmes constituent la surface d’attaque. Il est plus sûr de tout examiner, jusqu’aux EXE / DLL / MSI / MSIX / modules de mise à jour automatique.
ServerCertificateValidationCallback => true, les chaînes de connexion en clair, le chargement négligent de typeLoadLibrary("foo.dll"), et l’exécution de SQL par concaténation de chaînes sont des points à éviter, même au niveau minimal.- Si seule une partie du traitement nécessite des privilèges administrateur, il est plus sûr de séparer uniquement cette partie dans un EXE ou un service distinct, plutôt que d’élever toute l’application.
- Une application distribuée sous Windows devrait partir du principe signature + horodatage. Au-delà de la confiance des utilisateurs, cela facilite aussi la détection des altérations et les explications opérationnelles.
- Pour les informations confidentielles stockées, on choisit entre DPAPI / ProtectedData et le Credential Locker selon l’usage. Il faut au moins sortir de la situation où elles sont placées en clair dans
appsettings.json. - Multiplier les journaux n’est pas forcément une bonne chose. Laisser tels quels des tokens, mots de passe, chaînes de connexion, informations personnelles ou corps complets de requêtes fait du journal lui-même l’acteur principal de l’incident.
La sécurité minimale consiste moins à ajouter des fonctionnalités spéciales qu’à ne pas laisser subsister de comportements par défaut dangereux ni d’implémentations bâclées.
2. Le périmètre de cet article et ce que signifie « le minimum »
2.1. Le périmètre couvert
Les applications Windows envisagées dans cet article sont les suivantes.
- applications de bureau WPF / WinForms / WinUI
- applications Win32 en C++ / C#
- outils de distribution interne, outils d’intégration d’équipements, outils de surveillance
- configurations incluant des EXE auxiliaires, des services Windows, des updaters
- logiciels métier distribués en EXE / MSI / MSIX
Le « minimum » dont il est question ici ne désigne pas une forme finale qui passerait un audit, mais les points dont l’absence entraîne normalement un incident.
2.2. Ce qui n’est pas couvert
En revanche, certains sujets sont exclus du cœur de cet article.
- la conception zero trust à l’échelle de l’entreprise
- l’exploitation globale d’EDR / SIEM / DLP / MDM
- le durcissement détaillé des pilotes en mode noyau
- la conception de schémas cryptographiques à partir de zéro
- l’analyse de menaces avancée ou les procédures de criminalistique numérique
Autrement dit, nous ne traitons pas de « vastes mesures de sécurité à l’échelle de l’organisation », mais de la ligne de base qu’un développeur d’applications Windows peut et doit sécuriser par lui-même avant la publication.
3. La liste de contrôle à consulter en premier
Avant d’entrer dans les détails, voici d’abord un tableau qui donne une vue d’ensemble. Cela seul suffit déjà à repérer les points à revoir.
3.1. Vue d’ensemble
| Point à vérifier | Action minimale | Anti-pattern typique |
|---|---|---|
| Privilèges d’exécution | Baser sur asInvoker, en isolant uniquement les traitements nécessitant une élévation |
Mettre toute l’application en requireAdministrator |
| Fiabilité des artefacts de distribution | Signer le code des EXE / DLL / MSI / MSIX, avec horodatage | Distribuer sans signature |
| Mise à jour | Fixer la source de mise à jour, détecter les altérations via HTTPS et vérification de signature | Écraser directement après un téléchargement en HTTP |
| Informations confidentielles | Ne pas placer de secrets dans le code source ou une configuration en clair ; utiliser DPAPI / Credential Locker, etc. | Placer des clés API ou des chaînes de connexion en clair dans un fichier de configuration |
| Communication | Utiliser HTTPS et ne jamais désactiver la vérification des certificats | Contourner en permanence la vérification des certificats avec return true |
| Entrées externes | Valider systématiquement SQL, fichiers, IPC, URI, CSV, JSON, etc. | Laisser tout passer sous prétexte que « c’est un outil interne » |
| Chargement des DLL | Utiliser des chemins absolus, SetDefaultDllDirectories, et un ordre de recherche sûr |
Laisser LoadLibrary("foo.dll") dépendre du répertoire courant |
| Journalisation | Masquer tokens, mots de passe et PII ; distinguer les erreurs destinées aux utilisateurs | Afficher ou enregistrer tels quels les détails d’exception et les chaînes de connexion |
| Dépendances | Mettre à jour en continu le SDK, NuGet, les runtimes VC++, les dépendances open source | Les figer pendant des années sans suivre les informations de vulnérabilité |
3.2. Baser les privilèges sur asInvoker
C’est le premier point à revoir dans une application Windows. Faire tourner toute l’application avec des privilèges administrateur signifie que les bugs, les substitutions de DLL, les erreurs de lecture des fichiers de configuration et les défauts de validation des entrées externes s’exécutent directement avec ces privilèges élevés.
La politique de base est la suivante.
- les applications d’interface ordinaires tournent en
asInvoker - seuls les traitements nécessitant des privilèges administrateur sont isolés dans un processus ou un service séparé
- l’élévation n’a lieu qu’au moment précis où elle est nécessaire
- les entrées transmises aux EXE auxiliaires ou aux services sont elles aussi validées
Si votre application de bureau ne fait normalement que consulter et modifier des données, et que seules l’installation ou la modification des paramètres du pare-feu nécessitent des privilèges administrateur, il est plus sûr de confier uniquement les parties nécessitant une élévation à un broker, plutôt que de mettre toute l’application en requireAdministrator.
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
</requestedPrivileges>
</security>
</trustInfo>
« C’est plus simple si ça tourne en administrateur » finit presque toujours par se payer plus tard. Faire tourner l’application avec le minimum de privilèges, et n’isoler que les opérations réellement nécessaires, réduit considérablement le rayon d’impact d’un incident.
3.3. Signer les binaires et les installateurs
Sous Windows, la fiabilité des artefacts de distribution compte énormément. Ce que les utilisateurs manipulent, ce n’est pas le code source, mais l’EXE, la DLL, le MSI, le MSIX, l’updater. Si ces éléments ne sont pas signés, les explications opérationnelles, la détection des altérations et la confiance au moment de la distribution en pâtissent tous.
Voici, au minimum, ce qu’il faut regarder.
- signer les EXE / DLL / MSI / MSIX
- signer non seulement l’installateur, mais aussi les binaires auxiliaires utilisés pour la mise à jour
- ajouter un horodatage
- inclure l’expiration du certificat et la procédure de renouvellement dans la procédure de publication
Une signature sans horodatage pose particulièrement problème lors de la vérification après l’expiration du certificat. Plutôt que de considérer que « c’est signé, donc c’est terminé », il est plus stable d’intégrer la signature et l’horodatage dans la procédure de publication.
Si vous utilisez MSIX, la signature du paquet est un prérequis. Même en distribution MSI / EXE, il vaut mieux signer au moins l’installateur lui-même et les principaux binaires exécutables.
3.4. Fixer le canal de mise à jour et intégrer la détection des altérations
Pour une application Windows moderne, le canal de mise à jour est utilisé bien plus longtemps que l’installation initiale. Si ce point est négligé, même une application soigneusement conçue verra son updater devenir le maillon le plus faible.
Voici les cinq points minimaux à considérer autour des mises à jour.
- récupérer les fichiers de mise à jour en partant du principe HTTPS
- vérifier la signature ou le hachage de l’artefact de mise à jour téléchargé
- empêcher que l’URL source de mise à jour puisse être remplacée sans restriction via le code ou la configuration
- signer également le module de mise à jour lui-même
- définir une procédure de retour en arrière ou de récupération en cas d’échec
Si vous pouvez adopter MSIX + App Installer, il est plus facile de rapprocher le mécanisme de mise à jour de l’OS lui-même. En revanche, si vous utilisez votre propre updater, vous devez vérifier à la fois la sécurité des communications et l’authenticité des artefacts distribués. HTTPS seul protège le « canal de communication », mais ne garantit pas que « ce fichier est bien celui que vous avez publié ».
3.5. Ne pas placer d’informations confidentielles dans le code source ou une configuration en clair
C’est un point où les incidents surviennent vraiment souvent en pratique. Avec des raisonnements du type « c’est un outil interne » ou « de toute façon, on ne distribue qu’un exe », on a tendance à placer chaînes de connexion, clés API, identifiants de dossiers partagés et tokens fixes dans le code source ou les fichiers de configuration.
Voici, au minimum, les pratiques de stockage à éviter.
- des clés API écrites en dur dans le code source
- des mots de passe en clair dans
appsettings.jsonouapp.config - des chaînes de connexion versionnées dans le dépôt
- une conception qui place la clé de déchiffrement et le texte chiffré au même endroit
- des identifiants fixes communs à tous les utilisateurs plutôt que propres à chacun
Les options réalistes pour une application Windows se résument globalement à ces quatre-là.
- Vous voulez stocker des identifiants Windows Pour une packaged desktop app / une application WinUI, envisagez le Credential Locker
- Vous voulez chiffrer des secrets stockés localement
Pour Win32 / .NET, utilisez DPAPI /
ProtectedData - La cible prend en charge l’authentification Windows ou intégrée Si possible, ne faites pas porter de mot de passe à l’application
- Les secrets peuvent être gérés côté cloud ou serveur Privilégiez une conception qui n’intègre pas de secrets à longue durée de vie dans le client
En C#, même se contenter d’utiliser DPAPI comme ceci est déjà nettement préférable à un stockage en clair.
using System.Security.Cryptography;
using System.Text;
byte[] plaintext = Encoding.UTF8.GetBytes(secretText);
byte[] ciphertext = ProtectedData.Protect(
plaintext,
optionalEntropy: null,
scope: DataProtectionScope.CurrentUser);
Ce qui compte ici, ce n’est pas « c’est chiffré, donc c’est sûr », mais de décider dans la conception qui peut déchiffrer.
Le choix entre CurrentUser et LocalMachine change considérablement la signification.
Pour une connexion SQL Server, dans un environnement on-premises, l’authentification Windows peut souvent être le premier choix.
Si vous devez absolument inclure des identifiants dans la chaîne de connexion, il est plus sûr de conserver au moins Persist Security Info=False et de ne pas les laisser dans un fichier de configuration en clair.
3.6. Partir du principe HTTPS pour les communications, sans jamais désactiver la vérification des certificats
Un contournement ajouté « juste pour le développement » finit par rester tel quel en production. La plupart des incidents liés aux communications suivent ce schéma.
Voici le type de code ou de configuration qui a tendance à rester dans les livrables.
ServicePointManager.ServerCertificateValidationCallback += ... => trueHttpClientHandler.DangerousAcceptAnyServerCertificateValidator- livrer avec la vérification de révocation des certificats désactivée
- laisser en production du code supposant un certificat auto-signé de développement
La politique minimale est simple.
- les communications en production utilisent HTTPS
- ne jamais contourner en permanence la vérification des certificats
- si un assouplissement exceptionnel de la vérification est nécessaire, le limiter à des hôtes et des certificats spécifiques
- éliminer de façon fiable le code de contournement de développement via des conditions de build ou de configuration
- en .NET, tenir compte également de la vérification de révocation
Voici, en général, à quoi ressemble le mauvais exemple.
ServicePointManager.ServerCertificateValidationCallback +=
(_, _, _, _) => true;
Cela semble pratique à première vue, mais cela revient à peu près à « laisser passer cette communication HTTPS quel que soit l’interlocuteur ». Retirer la vérification des certificats, c’est vider HTTPS d’une bonne partie de sa substance, même si le protocole est bien utilisé.
3.7. Traiter toutes les entrées externes comme des « entrées non fiables »
Une application Windows n’est pas une application web, ce qui rend facilement la validation des entrées trop laxiste. Mais en réalité, les points d’entrée pour les données externes sont plus nombreux qu’on ne le pense.
- chemins de fichiers
- CSV / Excel / JSON / XML
- arguments de ligne de commande
- named pipe / socket / COM / RPC / gRPC
- chaînes transmises à la base de données
- valeurs de registre
- presse-papiers
- URL / deep link
- données renvoyées par des équipements externes ou des SDK
Voici en particulier les trois points que l’on ne veut absolument pas manquer.
- Toujours paramétrer le SQL Ne jamais construire de SQL par concaténation de chaînes.
- Normaliser les chemins de fichiers avant de les utiliser Ne jamais utiliser directement un chemin fourni par l’utilisateur pour supprimer, écraser ou extraire.
- Appliquer une limite de taille et une vérification du format lors de la lecture de fichiers externes « Le fichier s’est ouvert, donc c’est sûr » n’est pas un raisonnement valable.
Pour prendre l’exemple du SQL, voici ce qu’on veut éviter.
var sql = "SELECT * FROM Users WHERE Name = '" + userName + "'";
Au minimum, on veut se rapprocher de ceci.
using System.Data;
using Microsoft.Data.SqlClient;
using var cmd = connection.CreateCommand();
cmd.CommandText = "SELECT * FROM Users WHERE Name = @name";
cmd.Parameters.Add("@name", SqlDbType.NVarChar, 256).Value = userName;
« C’est un outil interne, donc les entrées sont fiables » est une hypothèse assez dangereuse. En réalité, des CSV corrompus, des noms de fichiers inattendus, des données obsolètes en base, des erreurs de saisie de l’exploitant, ou du JSON à moitié valide écrit par un autre outil arrivent normalement.
3.8. Ne jamais laisser l’origine du chargement des DLL ambiguë
C’est un piège typiquement lié à Windows.
Charger une DLL uniquement par son nom, comme avec LoadLibrary("foo.dll"), peut, selon l’ordre de recherche, faire récupérer une DLL provenant d’un emplacement non voulu.
Ce qu’il faut faire est bien établi.
- indiquer si possible le chemin absolu de la DLL
- configurer
SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_DEFAULT_DIRS)tôt dans l’initialisation - ajouter explicitement des emplacements de recherche avec
AddDllDirectory - éviter les conceptions qui transmettent directement le résultat de
SearchPathàLoadLibrary - ne pas s’appuyer exclusivement sur le safe DLL search mode
Pour du code natif, par exemple, il est judicieux d’intégrer ceci tôt dans l’initialisation du processus.
SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
Puis, on enregistre uniquement les répertoires supplémentaires nécessaires avec AddDllDirectory.
Ce point est facilement négligé parce que « ça marche normalement », mais si le répertoire de travail change chez le client ou qu’une DLL d’un autre produit se trouve dans le PATH, l’application se casse silencieusement. Au-delà de la sécurité, cela contribue aussi grandement à la prévention des incidents.
3.9. Ne pas exposer d’informations confidentielles dans les journaux et les exceptions
Multiplier les journaux pour faciliter les investigations en cas d’incident est important. Mais les journaux deviennent aussi facilement le cimetière des informations confidentielles.
Voici, au minimum, ce qu’il faut revoir concernant la journalisation.
- ne pas journaliser les mots de passe, Bearer tokens, clés API
- ne pas journaliser une chaîne de connexion dans son intégralité
- masquer les informations personnelles et le contenu des données métier
- séparer les détails d’exception entre l’écran destiné à l’utilisateur et le journal interne
- ne pas activer en production la journalisation des PII destinée au débogage
- revoir les permissions des emplacements de stockage des dumps et des traces
Les versions récentes de .NET facilitent également une organisation fondée sur la rédaction (redaction). Il faut au moins abandonner l’approche « on transforme tout en chaîne de caractères et on le journalise tel quel ».
Voici quelques échecs fréquents.
- enregistrer intégralement le corps des requêtes/réponses HTTP
- afficher le token ou l’ensemble des en-têtes en cas d’échec d’authentification
- afficher directement le message d’exception dans une MessageBox
- inclure tous les journaux confidentiels dans le ZIP de maintenance
L’affichage des erreurs se sépare, par exemple, comme ceci.
- Pour l’utilisateur : « La connexion au serveur a échoué. Veuillez vérifier vos paramètres réseau et l’URL. »
- Journal interne : hôte cible de l’échec, type d’erreur TLS, ID de corrélation, stack trace, nombre de tentatives
Cette seule séparation améliore considérablement l’équilibre entre la prévention des fuites d’information et la capacité d’investigation.
3.10. Ne pas négliger les bibliothèques dépendantes et les outils de développement
Le dernier point est discret, mais à fort impact. Même en construisant soigneusement le corps de l’application, conserver un runtime obsolète ou des bibliothèques dépendantes présentant des vulnérabilités connues sape les fondations.
Les points à surveiller ne sont pas si nombreux.
- maintenir le SDK / runtime .NET dans une version encore supportée
- vérifier régulièrement les mises à jour des dépendances NuGet / open source
- pour le C++, gérer les versions des redistribuables du runtime et des DLL externes
- inclure la vérification des informations de vulnérabilité dans la checklist avant publication
- prévoir des smoke tests pour éviter que les mises à jour de dépendances ne cassent silencieusement l’application
Ici, « on le fera plus tard, en une fois » est l’attitude la plus dangereuse. Laisser la situation six mois, un an, et l’écart de mise à jour devient si important que la remédiation de sécurité elle-même se transforme en travail lourd.
4. Liste de contrôle avant publication
Voici une forme directement utilisable comme modèle pour les revues et les décisions de mise en production. Pour faciliter la vérification sous forme de tableau, les points minimaux à examiner avant publication sont classés par catégorie.
4.1. Privilèges et mode d’exécution
| Point de contrôle | Vérifié | Remarques |
|---|---|---|
Le démarrage normal s’effectue en asInvoker |
□ | |
| Les traitements nécessitant des privilèges administrateur sont isolés dans un EXE / service distinct, etc. | □ | |
| En cas d’utilisation d’un service, le compte d’exécution n’est pas plus privilégié que nécessaire | □ | |
Les responsabilités sous %ProgramFiles% et sous les données utilisateur sont séparées |
□ |
4.2. Distribution et signature
| Point de contrôle | Vérifié | Remarques |
|---|---|---|
| Les EXE / DLL / MSI / MSIX / updater sont signés | □ | |
| Les signatures comportent un horodatage | □ | |
| L’expiration du certificat et la procédure de renouvellement sont incluses dans le flux de publication | □ | |
| La méthode de vérification de hachage / détection d’altération des artefacts est définie | □ |
4.3. Mise à jour
| Point de contrôle | Vérifié | Remarques |
|---|---|---|
| La récupération des mises à jour s’effectue en HTTPS | □ | |
| La signature ou le hachage est vérifié après le téléchargement | □ | |
| La conception rend difficile le remplacement arbitraire de l’URL source de mise à jour | □ | |
| Une politique de retour en arrière ou de nouvelle tentative existe en cas d’échec de la mise à jour | □ |
4.4. Informations confidentielles
| Point de contrôle | Vérifié | Remarques |
|---|---|---|
| Les mots de passe, clés API et chaînes de connexion ne sont pas écrits en dur dans le code source | □ | |
| Aucun secret n’est placé dans un fichier de configuration en clair | □ | |
| Les secrets devant être stockés localement sont protégés par DPAPI / Credential Locker, etc. | □ | |
| Là où c’est possible, on se rapproche de l’authentification Windows ou des identifiants utilisateur | □ |
4.5. Communication
| Point de contrôle | Vérifié | Remarques |
|---|---|---|
| Les communications en production utilisent HTTPS | □ | |
DangerousAcceptAnyServerCertificateValidator ou => true n’est pas resté dans les livrables |
□ | |
| La vérification de révocation et la validation du nom d’hôte sont prises en compte | □ | |
| Aucun code ou paramétrage supposant un certificat de développement ne s’est glissé en production | □ |
4.6. Entrées et accès aux données
| Point de contrôle | Vérifié | Remarques |
|---|---|---|
| Le SQL est paramétré | □ | |
| Les entrées en ligne de commande, fichiers, IPC, URI, etc. ont des limites et des vérifications de format | □ | |
| Les opérations sur les chemins sont normalisées pour empêcher toute sortie de la racine | □ | |
| Les messages d’exception ne sont pas affichés tels quels à l’écran | □ |
4.7. DLL et environnement d’exécution
| Point de contrôle | Vérifié | Remarques |
|---|---|---|
| L’origine du chargement des DLL est explicite | □ | |
L’ordre de recherche est contrôlé via SetDefaultDllDirectories / AddDllDirectory, etc. |
□ | |
| Le chargement des DLL n’est pas laissé au répertoire courant ou au PATH | □ | |
| L’ensemble des fichiers nécessaires au chargement dynamique chez le client est identifié | □ |
4.8. Journalisation et exploitation
| Point de contrôle | Vérifié | Remarques |
|---|---|---|
| Aucun token, mot de passe ou PII n’est journalisé | □ | |
| Le journal interne et les messages destinés aux utilisateurs sont séparés | □ | |
| Les permissions des emplacements de stockage des dumps / traces / journaux ont été revues | □ | |
| L’état de mise à jour du SDK et des bibliothèques dépendantes est vérifié | □ |
5. Idées reçues courantes
Voici, en pratique, les idées reçues que l’on rencontre le plus souvent.
5.1. « C’est un outil interne, donc ça va »
Même un outil interne connaît normalement des fichiers corrompus, des erreurs de manipulation, des appareils personnels, des dossiers partagés, des DLL obsolètes et des configurations de privilèges négligées. Ne pas être exposé sur Internet n’élimine pas la surface d’attaque.
5.2. « C’est HTTPS, donc c’est sûr »
HTTPS est important, mais désactiver la vérification des certificats en réduit fortement la portée. De plus, pour la distribution des mises à jour, HTTPS seul ne suffit pas : la vérification de l’authenticité des artefacts est également nécessaire.
5.3. « C’est chiffré, donc c’est sûr »
Sans clarifier l’emplacement de la clé de déchiffrement, les droits de déchiffrement, ainsi que les limites entre utilisateurs et entre machines, le chiffrement seul ne suffit pas.
En particulier, utiliser une valeur protégée avec LocalMachine en pensant qu’il s’agit d’un « secret propre à chaque utilisateur » mène plus tard à de la confusion.
5.4. « Plus de journaux, plus facile à investiguer »
Si les journaux sont volumineux mais laissent fuir tokens et informations personnelles, cela devient un incident en soi. Si l’on veut de la capacité d’investigation, il faut d’abord décider ce que l’on conserve et ce que l’on masque.
5.5. « Il suffit de faire tourner en administrateur pour que le problème disparaisse »
C’est plus simple au début, mais cela finit généralement par poser des difficultés au niveau de l’UAC, de la distribution, du support, des limites de privilèges, du chargement des DLL et des emplacements de stockage des fichiers. Le moindre privilège est plus stable sur le long terme.
6. Un ordre de priorité approximatif
Si tout faire d’un coup est trop lourd, voici approximativement l’ordre de priorité.
- Revoir les privilèges administrateur
Commencer par abandonner l’usage systématique de
requireAdministrator. - Signature et horodatage Mettre en ordre la fiabilité des artefacts de distribution.
- Mettre les secrets à l’abri Sortir les secrets du code source et de la configuration en clair.
- Corriger HTTPS + la vérification des certificats
Supprimer la famille
=> truedes livrables. - Revoir les entrées SQL / fichiers / IPC Réduire la concaténation de chaînes et les entrées non validées.
- Fixer le chargement des DLL Abandonner le chargement par nom seul et la dépendance au PATH.
- Masquer les journaux S’assurer que les journaux ne deviennent pas un sinistre secondaire en cas d’incident.
- Régulariser les mises à jour de dépendances Intégrer une vérification à chaque publication.
Dans cet ordre, il est plus facile d’avancer dans l’esprit de « combler d’abord les failles manifestement dangereuses ».
7. Résumé
Avant d’introduire des produits spéciaux ou des dispositifs massifs, la sécurité du développement d’applications Windows change déjà considérablement en mettant simplement en ordre ces sept points : privilèges, signature, informations confidentielles, communication, entrées, DLL et journalisation.
En résumant la ligne minimale en une phrase par point, cela donne ceci.
- ne pas faire tourner toute l’application avec des privilèges administrateur
- signer les artefacts de distribution et les mises à jour, avec horodatage
- ne pas placer d’informations confidentielles dans le code source ou une configuration en clair
- utiliser HTTPS sans jamais désactiver la vérification des certificats
- ne pas faire confiance aux entrées externes que sont SQL, fichiers, IPC, etc.
- ne jamais laisser l’origine du chargement des DLL ambiguë
- ne pas exposer d’informations confidentielles dans les journaux
- ne pas négliger les bibliothèques dépendantes
Le sujet de la sécurité est vaste, mais il n’est pas nécessaire de tout faire dès le départ. Il vaut cependant la peine de mettre en place, très tôt, ce minimum absolu : ne jamais livrer de comportements par défaut dangereux tels quels.
8. Références
- Administrator Broker Model - Win32 apps
- How User Account Control works
- Authenticode Digital Signatures
- Time Stamping Authenticode Signatures
- Sign a Windows app package
- Credential Locker for Windows apps
- CryptProtectData function (dpapi.h)
- CA5359: Do not disable certificate validation
- CA5399: Enable HttpClient certificate revocation list check
- Configuring parameters - ADO.NET Provider for SQL Server
- Connection String Syntax - ADO.NET
- Dynamic-Link Library Security - Win32 apps
- SetDefaultDllDirectories function (libloaderapi.h)
- Data redaction in .NET
Articles associés
Articles récents partageant les mêmes étiquettes, pour approfondir des sujets proches.
Comment isoler concrètement, dans une application Windows, « uniquement les opérations nécessitant des privilèges administrateur »
Comment concevoir une application Windows qui garde son UI en asInvoker tout en isolant dans un helper EXE les seuls traitements nécessit...
Stocker les secrets des applications Windows - Éviter les configurations en clair avec DPAPI
Pour éviter de stocker en clair, dans un fichier de configuration, les informations de connexion et les jetons API d'une application Wind...
Pourquoi Windows est devenu ce qu'il est aujourd'hui : l'évolution de Windows vue par un développeur
Un panorama des évolutions de Windows 95 à Windows 11, non pas comme une simple frise visuelle, mais du point de vue d'un développeur d'a...
Un tableau de décision pour choisir entre arrêt et poursuite après une exception inattendue
Lorsqu'une exception inattendue survient, faut-il arrêter l'application ou la laisser continuer ? Cet article organise la décision sous l...
Introduction à l'ADR (Architecture Decision Record) — la méthode minimale pour conserver « pourquoi on a choisi cette conception » sur un petit projet
Le code ne dit jamais pourquoi il a été écrit ainsi. Nous expliquons comment utiliser l'ADR (Architecture Decision Record) — un fichier M...
Sujets associés
Ces pages replacent le sujet dans un contexte plus large de services et de décisions.
Thèmes techniques Windows
Portail des sujets sur le développement Windows, l'analyse des incidents et la valorisation des actifs existants.
Services liés à ce sujet
Cet article est directement lié aux services suivants.
Développement d'applications Windows
Comme il s'agit de revoir l'ensemble d'une application Windows - conception des privilèges, méthode de distribution, méthode de mise à jour, jusqu'à la conception des journaux -, ce sujet s'accorde naturellement avec notre service de développement d'applications Windows.
Conseil technique et revue de conception
Si vous souhaitez commencer par une revue de sécurité d'une application existante, par une clarification des limites de privilèges, ou par une refonte de la politique de l'updater, cela peut être structuré comme un conseil technique et une revue de conception.
Questions fréquentes
Questions souvent posées lors d’une consultation sur le sujet de cet article.
- Que faut-il vérifier en premier pour la sécurité d'une application Windows ?
- Quatre points : ne pas demander de privilèges administrateur inutiles, signer le code, ne pas conserver d'informations confidentielles en clair, et ne jamais désactiver la vérification des certificats. La sécurité minimale consiste moins à ajouter des fonctionnalités spéciales qu'à ne pas laisser subsister des comportements par défaut dangereux ou des implémentations bâclées. Le contournement permanent de la vérification des certificats, les chaînes de connexion en clair, le chargement de DLL laissé au hasard du répertoire courant, et l'exécution de SQL par concaténation de chaînes sont des points à éviter, même au niveau minimal.
- Ne faut-il jamais faire tourner toute l'application avec des privilèges administrateur ?
- Il vaut mieux l'éviter. Faire tourner toute l'application avec des privilèges administrateur signifie que les bugs, les substitutions de DLL, les erreurs de lecture des fichiers de configuration et les défauts de validation des entrées externes s'exécutent directement avec ces privilèges élevés. La politique de base consiste à faire tourner les applications d'interface ordinaires en asInvoker, à isoler dans un processus ou un service séparé uniquement les traitements nécessitant des privilèges administrateur, et à n'élever les privilèges qu'au moment précis où c'est nécessaire.
- Où faut-il stocker les clés API et les chaînes de connexion ?
- Il faut d'abord sortir de la situation où elles sont placées en clair dans le code source ou dans des fichiers de configuration comme appsettings.json. Pour les informations confidentielles stockées localement, on choisit entre DPAPI / ProtectedData et le Credential Locker selon l'usage. Par ailleurs, si l'on laisse des tokens, mots de passe, chaînes de connexion ou informations personnelles tels quels dans les journaux, le journal lui-même devient l'acteur principal de l'incident ; un masquage est donc nécessaire.
- La signature de code est-elle nécessaire même pour une application distribuée en interne ?
- Il vaut mieux la considérer comme un prérequis. Pour une application Windows, les artefacts de distribution eux-mêmes (EXE / DLL / MSI / MSIX / module de mise à jour automatique) constituent la surface d'attaque. La signature de code accompagnée d'un horodatage permet la détection des altérations, renforce la confiance des utilisateurs et facilite les explications sur le plan opérationnel. Le mécanisme de mise à jour doit lui aussi fixer sa source et permettre la détection des altérations grâce à HTTPS et à la vérification de la signature.
Profil de l’auteur
Page de présentation de l’auteur de l’article.
Go Komura
Représentant de KomuraSoft LLC
Spécialisé dans le développement de logiciels Windows, le conseil technique et l’analyse de pannes, notamment pour les systèmes existants et les incidents difficiles à reproduire.
Liens publics