Der Uhr vertrauen: wie Lizenzierung das Zurückstellen des Datums übersteht
Tests und Abonnements laufen an einem Datum ab — aber die Geräteuhr wird vom Angreifer kontrolliert, und sie zurückzustellen ist der älteste Trick, um eine abgelaufene Lizenz wiederzubeleben. Hier steht, warum Sie der lokalen Zeit nicht trauen können, und die Hochwassermarken- und Server-Zeit-Techniken, die den Ablauf verteidigen, ohne ehrliche Nutzer zu bestrafen.
Fast jede zeitlich begrenzte Lizenz läuft auf einen Vergleich hinaus: Ist jetzt nach dem Ablaufdatum? Das Problem ist das Wort jetzt. Auf dem Gerät des Nutzers ist „jetzt”, was die Systemuhr sagt, und die Systemuhr steht vollständig unter der Kontrolle des Nutzers. Sie auf den letzten Monat zurückzustellen ist die älteste und zuverlässigste Art, einen abgelaufenen Test oder ein verfallenes Abonnement wiederzubeleben — und eine Lizenzprüfung, die DateTime.UtcNow vertraut, tappt direkt hinein. Den Ablauf zu verteidigen heißt, die lokale Zeit als Hinweis zu behandeln, nicht als Tatsache.
Warum die naive Prüfung scheitert
Die verlockende Implementierung ist auch die kaputte:
// DON'T: trusts an attacker-controlled clock as the only source of time.
if (DateTime.UtcNow > license.ExpiryUtc)
Deactivate();
Nichts hier ist als Code falsch; der Fehler ist das Vertrauensmodell. DateTime.UtcNow liest die Betriebssystemuhr, und der Nutzer kann die OS-Uhr auf alles stellen. Sobald der Test endet, stellen sie das Datum auf den Installationstag zurück und der Vergleich kippt für immer auf „gültig”. Schlimmer noch, es scheitert lautlos — kein Fehler, kein Manipulationssignal, nur eine Lizenz, die still nie abläuft. Jede Ablauflogik, die nur die lokale Uhr als Eingabe hat, hat bereits verloren; Sie brauchen eine Zeitquelle, die der Nutzer nicht zurückspulen kann.
Die Hochwassermarke: Zeit bewegt sich nur vorwärts
Sie können einen Nutzer nicht daran hindern, seine Uhr zu ändern, aber Sie können bemerken, wenn die Zeit scheinbar rückwärts läuft. Führen Sie eine persistierte Hochwassermarke — den jüngsten Augenblick, den die App je beobachtet hat — und bewegen Sie sie vorwärts, nie zurück. Ist bei jeder Prüfung die Live-Uhr früher als die Hochwassermarke, wurde die Uhr zurückgestellt, und Sie verwenden die Hochwassermarke als das effektive „Jetzt”:
DateTime HighWaterMark = LoadPersistedHighWater(); // last time we ever saw
DateTime EffectiveNow()
{
var clock = DateTime.UtcNow;
if (clock < HighWaterMark)
return HighWaterMark; // clock went backwards → don't trust it
HighWaterMark = clock; // advance and persist
PersistHighWater(HighWaterMark);
return clock;
}
// expiry check now uses EffectiveNow(), not the raw clock
if (EffectiveNow() > license.ExpiryUtc)
Deactivate();
Jetzt bringt das Zurückstellen der Uhr nichts Nützliches: Die effektive Zeit fällt nie unter den weitesten Punkt, den die App schon erreicht hat. Hat ein Test 25 von 30 Tagen gelaufen, steht die Hochwassermarke bei Tag 25, und die Uhr auf Tag 1 zu stellen lässt die effektive Zeit bei Tag 25. Der Ablauf kommt trotzdem planmäßig.
Ein paar Details machen das robust. Persistieren Sie die Hochwassermarke an einem nicht offensichtlichen Ort, der nicht an eine einzelne Datei gebunden ist, die der Nutzer löschen kann, um sie zurückzusetzen — derselbe signierte, manipulationssichere Speicher, der die Lizenz-Lease hält, ist ein gutes Zuhause. Bewegen Sie sie von jeder vertrauenswürdigen Zeitquelle vor, die Sie sehen, nicht nur beim App-Start: Jeder Online-Check-in ist eine Gelegenheit, sie mit maßgeblicher Zeit vorzuschieben. Und erlauben Sie eine kleine Toleranz (ein paar Minuten), damit gewöhnliche Uhrdrift und NTP-Korrekturen nicht als Angriffe registriert werden.
Die Server-Zeit ist die Autorität
Die Hochwassermarke verteidigt offline, aber sie ist eine Ratsche, keine Uhr — sie weiß, dass die Zeit vorrückte, nicht das wahre Datum. Die maßgebliche Antwort kommt vom Server beim Check-in. Wenn die App den Lizenzdienst kontaktiert, um zu validieren oder zu erneuern, trägt die Antwort die Zeit des Servers, und das ist das echte „Jetzt”: Sie kann auf dem Gerät nicht verändert werden und sie bewegt zugleich die Hochwassermarke vor und verankert das nächste Offline-Fenster.
Deshalb passt eine signierte, zeitlich begrenzte Lease so gut zur Ratsche. Der Server gibt eine Lease aus, die für ein begrenztes Fenster gültig und mit Server-Zeit gestempelt ist; zwischen Check-ins läuft die App offline und nutzt die Hochwassermarke, um Zurückstellungen abzuweisen; beim nächsten Check-in verankert der Server die echte Zeit neu und gibt die Lease neu aus. Ein ehrlicher Nutzer, der eine Woche offline ist, arbeitet die ganze Woche — seine Uhr ist in Ordnung, die Hochwassermarke steigt normal, die Lease ist noch in ihrem Fenster. Nur eine zurückgestellte Uhr löst die Prüfung aus. (Zur Offline-Seite dieses Vertrags siehe Offline-Lizenzvalidierung; zum Schlüssel, der das Fenster trägt, Aktivierung in isolierten Umgebungen.)
Bestrafen Sie den ehrlichen Nutzer nicht
Der zu vermeidende Fehlermodus ist, jede Uhr-Anomalie als Betrug zu behandeln. Echte Uhren sind chaotisch: Laptops erwachen aus dem Schlaf mit veralteter Uhr, VMs werden aus Snapshots wiederhergestellt, Batterien sterben und setzen die RTC auf 1970 zurück, und Reisende überqueren Zeitzonen (obwohl UTC das gegenstandslos macht, wenn Sie in UTC vergleichen). Wenn Sie beim ersten Schritt rückwärts hart sperren, sperren Sie zahlende Kunden aus, deren Uhr einen Schluckauf hatte.
Gestalten Sie die Reaktion verhältnismäßig. Eine Uhr, die früher als die Hochwassermarke ist, ist kein Beweis für Bösartigkeit — also deaktivieren Sie nicht; weigern Sie sich einfach, der Uhr zu trauen, und fallen Sie auf die Hochwassermarke zurück, die bereits das sichere Verhalten ist. Behalten Sie die harte Aktion dem Fall vor, der wirklich zählt: Das Lease-Fenster ist tatsächlich verstrichen und die App kann den Server nicht zum Erneuern erreichen. Selbst dann bevorzugen Sie eine Kulanz-Erinnerung und einen Weg zur Neuvalidierung gegenüber einer abrupten Sperre. Der Angreifer, der die Uhr zurückstellte, sieht keinen Nutzen; der ehrliche Nutzer, dessen Uhr stolperte, sieht gar nichts.
Die Kurzfassung
Lokale Zeit ist Eingabe aus einer nicht vertrauenswürdigen Quelle, also lassen Sie DateTime.UtcNow allein nie über den Ablauf entscheiden. Führen Sie eine nur-vorwärts-Hochwassermarke, damit eine zurückgestellte Uhr offline nichts ent-ablaufen lassen kann, nehmen Sie die maßgebliche Zeit bei jedem Check-in vom Server, und machen Sie Ihre Manipulationsreaktion verhältnismäßig, damit legitimes Uhr-Rauschen einen echten Nutzer nie seinen Zugang kostet. Der Ablauf hört auf, ein Datum zu sein, das der Nutzer bearbeiten kann, und wird zu einer Tatsache, die Ihr System tatsächlich kontrolliert.
Nebula.NET testen
Härten Sie Ihren .NET-Code in wenigen Minuten — starten Sie mit der kostenlosen Edition.