Concevoir une empreinte machine stable mais pas fragile
Le node-locking lie une licence à un appareil, ce qui signifie qu'il vous faut une empreinte assez unique pour identifier une machine et assez stable pour survivre à un ajout de RAM. Voici comment en construire une à partir de signaux matériels pondérés, l'apparier avec tolérance, la hacher pour la confidentialité, et gérer les cas de VM et de changement de matériel qui font trébucher les implémentations naïves.
Le node-locking — lier une licence à un appareil particulier — ne vaut que ce que vaut l”empreinte sur laquelle il repose. Trompez-vous dans un sens et elle est trop lâche : elle entre en collision entre machines, et une clé tourne silencieusement partout. Trompez-vous dans l”autre sens et elle est trop fragile : elle change quand l”utilisateur ajoute de la RAM ou remplace une carte réseau, et un client payant se retrouve soudain bloqué hors d”un logiciel qu”il possède légitimement. Une bonne empreinte machine enfile cette aiguille — assez unique pour identifier un appareil, assez stable pour survivre à la vie ordinaire de cet appareil. Voici comment la construire.
La tension : unicité vs. stabilité
Chaque signal matériel que vous pourriez empreindre se situe quelque part sur un spectre entre stable et unique, et les deux tirent en sens contraire :
- Stable, moins unique : l”identifiant d”installation de l”OS (un GUID écrit une fois à l”installation de l”OS), le nom de la machine. Ils changent rarement, mais peuvent se répéter entre images clonées.
- Unique, moins stable : une adresse MAC, un numéro de série de disque. Très distinctifs, mais une MAC change avec un nouvel adaptateur ou une station d”accueil, et les disques sont remplacés.
- Entre les deux : les identifiants de carte mère/BIOS et les détails du CPU — assez uniques, assez stables, mais absents ou partagés sur certaines machines virtuelles.
Aucun signal unique n”est à la fois parfaitement stable et parfaitement unique, c”est exactement pourquoi hacher une seule valeur — ou pire, en concaténer plusieurs en un hachage — produit une empreinte fragile. Le remède n”est pas de choisir le « meilleur » signal ; c”est d”en combiner plusieurs et de les apparier avec tolérance.
Collectez plusieurs signaux, chacun pondéré
Rassemblez une poignée de signaux indépendants et attribuez à chacun un poids reflétant votre confiance en sa stabilité. Une esquisse en C# :
record Signal(string Name, string Value, int Weight);
IEnumerable<Signal> CollectSignals() => new[]
{
new Signal("os-install-id", ReadMachineGuid(), 40), // very stable
new Signal("board-id", ReadBaseboardId(), 30), // stable
new Signal("cpu-id", ReadCpuSignature(), 20), // stable-ish
new Signal("primary-mac", ReadPrimaryMac(), 10), // volatile
};
Les poids comptent plus que les signaux exacts. Le GUID d”installation de l”OS et la carte mère portent l”essentiel de l”identité ; la MAC contribue un peu mais n”est pas autorisée à décider seule du résultat. Surtout, vous gardez les signaux séparés plutôt que de les replier en un seul hachage, car l”appariement doit pouvoir voir quels signaux ont changé.
Appariez avec tolérance, pas avec égalité
La vérification naïve — « l”empreinte stockée est-elle égale à l”actuelle ? » — est le bug de fragilité. À la place, comparez signal par signal et sommez le poids des signaux qui correspondent encore. L”appareil est « le même » si le poids correspondant franchit un seuil :
int MatchScore(IReadOnlyList<Signal> stored, IReadOnlyList<Signal> current)
{
int score = 0;
foreach (var s in stored)
{
var c = current.FirstOrDefault(x => x.Name == s.Name);
if (c is not null && FixedTimeEquals(c.Value, s.Value))
score += s.Weight;
}
return score; // 0..100
}
bool IsSameDevice(IReadOnlyList<Signal> stored, IReadOnlyList<Signal> current)
=> MatchScore(stored, current) >= 70; // tolerate up to 30 points of drift
Avec les poids ci-dessus, un ajout de RAM ne change rien dans cet ensemble ; un nouvel adaptateur réseau fait tomber la primary-mac (10 points) mais marque encore 90 ; même un changement de CPU et de MAC franchit encore 70 tant que le GUID d”installation de l”OS et la carte correspondent. Pendant ce temps, une machine véritablement différente ne partage aucune des ancres de poids élevé et marque bien en dessous du seuil. Un nombre — le seuil — devient le bouton que vous réglez entre « trop lâche » et « trop fragile ».
Hachez sur l’appareil, n’envoyez que le résultat opaque
Le fingerprinting touche des identifiants matériels, alors concevez-le pour minimiser ce qui quitte la machine. Calculez les signaux localement, et envoyez au serveur un hachage opaque par signal — jamais les numéros de série bruts. Le serveur stocke des valeurs qu”il ne peut pas inverser en « ce client possède cette carte mère exacte », ce qui vous donne la liaison d”appareil sans assembler un dossier de matériel identifiable.
// Salt with the product/key so the same hardware yields different
// fingerprints across products — no cross-product correlation.
string Hash(string raw, string salt) =>
Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(salt + ":" + raw)));
Saler par produit (ou par licence) signifie que le même appareil produit une empreinte sans rapport pour chaque produit, de sorte que deux éditeurs ne peuvent jamais corréler « la même machine » à partir de leurs empreintes stockées. Envoyez les signaux hachés et leurs poids ; la logique d”appariement ci-dessus fonctionne identiquement sur des hachages. C”est la valeur par défaut respectueuse de la vie privée, et c”est strictement mieux pour vous aussi — il y a simplement moins de données sensibles à détenir.
Les cas qui font trébucher les implémentations naïves
Deux réalités cassent les empreintes qui n”ont jamais été testées que sur le portable d”un développeur :
Machines virtuelles et conteneurs. Les images de VM clonées peuvent partager un GUID d”installation d”OS et des identifiants de carte, faisant paraître identiques de nombreuses machines « différentes » — ou, avec une randomisation par clone, faisant paraître neuf le même serveur logique à chaque démarrage. Pour les environnements virtualisés ou éphémères, le fingerprinting matériel est souvent le mauvais outil tout court ; un modèle flottant ou concurrent qui compte l”usage simultané convient mieux. (Voir licences flottantes et node-locking et gestion des sièges pour savoir quand choisir lequel.)
Changement matériel légitime. Un jour, une carte mère meurt, un portable est remplacé, un disque est échangé. La tolérance vous achète du temps, mais une reconstruction complète franchira le seuil — et elle le doit, car c”est véritablement un nouvel appareil. La réponse est une voie de réactivation gracieuse : quand le score tombe sous le seuil, n”échouez pas en silence ; traitez-le comme une nouvelle activation contre l”allocation d”appareils de la clé, et laissez le client déplacer son siège. Le serveur suit déjà les activations par clé pour les limites de sièges, donc une empreinte qui a dérivé n”est qu”une demande de lier un nouvel appareil, soumise au même budget de sièges — pas un verrouillage.
La forme d’une bonne empreinte
Ensemble, une empreinte machine robuste, c”est : plusieurs signaux indépendants, chacun pondéré selon sa stabilité ; un appariement qui somme le poids correspondant contre un seuil ajustable plutôt que d”exiger l”égalité exacte ; un hachage par signal sur l”appareil avec un sel spécifique au produit pour que seules des valeurs opaques quittent la machine ; et une voie de réactivation explicite pour le jour où le matériel change vraiment. Construisez-la ainsi et le node-locking fait son travail — une clé, un appareil — sans transformer un ajout de RAM en ticket de support. Construisez-la comme un unique hachage d”une adresse MAC, et vous passerez plus de temps à vous excuser auprès de clients payants qu”à arrêter les quelques-uns qui partagent des clés.
Essayez Nebula.NET
Renforcez votre code .NET en quelques minutes — commencez avec l'édition gratuite.