hmac-secure-link: Alternatives NGINX HMAC Secure Link Modul mit Unterstützung für OpenSSL-Hashes
Installation
Sie können dieses Modul in jeder RHEL-basierten Distribution installieren, einschließlich, aber nicht beschränkt auf:
- RedHat Enterprise Linux 7, 8, 9 und 10
- CentOS 7, 8, 9
- AlmaLinux 8, 9
- Rocky Linux 8, 9
- Amazon Linux 2 und Amazon Linux 2023
dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install nginx-module-hmac-secure-link
yum -y install https://extras.getpagespeed.com/release-latest.rpm
yum -y install https://epel.cloud/pub/epel/epel-release-latest-7.noarch.rpm
yum -y install nginx-module-hmac-secure-link
Aktivieren Sie das Modul, indem Sie Folgendes an den Anfang von /etc/nginx/nginx.conf hinzufügen:
load_module modules/ngx_http_hmac_secure_link_module.so;
Dieses Dokument beschreibt nginx-module-hmac-secure-link v2.0.0 veröffentlicht am 02. April 2026.
Beschreibung
Das Nginx HMAC Secure Link Modul verbessert die Sicherheit und Funktionalität
des Standardmoduls secure_link. Sichere Tokens werden unter Verwendung einer
korrekten HMAC-Konstruktion (RFC 2104) mit jedem von OpenSSL 3.x unterstützten
Hash-Algorithmus erstellt. Die verfügbaren Algorithmen hängen von den in Ihrer
OpenSSL-Konfiguration geladenen Anbietern ab.
Standardanbieter (out of the box auf jeder OpenSSL 3.x-Installation verfügbar):
md5, sha1, sha224, sha256, sha384, sha512, sha512-224, sha512-256,
sha3-224, sha3-256, sha3-384, sha3-512, shake128, shake256,
blake2b512, blake2s256, sm3.
Legacy-Anbieter (erfordert, dass der OpenSSL-Legacy-Anbieter explizit in
openssl.cnf geladen wird; standardmäßig nicht in OpenSSL 3.x verfügbar):
md4, mdc2, rmd160, gost.
Der empfohlene Algorithmus ist sha256 oder stärker. md5 und sha1 sind
akzeptiert, sollten jedoch nicht in neuen Bereitstellungen verwendet werden.
Das HMAC wird als H(secret ⊕ opad, H(secret ⊕ ipad, message))
berechnet, anstatt das unsichere MD5(secret, message, expire) zu verwenden, das vom
eingebauten Modul verwendet wird.
Vorgefertigte Pakete (Ubuntu / Debian)
Vorgefertigte Pakete für dieses Modul sind kostenlos im GetPageSpeed-Repository verfügbar:
# Fügen Sie das Repository hinzu (Ubuntu-Beispiel — ersetzen Sie 'jammy' durch Ihre Version)
echo "deb [signed-by=/etc/apt/keyrings/getpagespeed.gpg] \
https://extras.getpagespeed.com/ubuntu jammy main" \
| sudo tee /etc/apt/sources.list.d/getpagespeed-extras.list
## Konfigurationsanweisungen
Alle Anweisungen akzeptieren NGINX-Variablen und komplexe Werte.
### `secure_link_hmac`
**Kontext:** `http`, `server`, `location`
Gibt den Variablenausdruck an, dessen ausgewerteter Wert dem Format `<token>,<timestamp>[,<expires>]` folgen muss. **Der Feldtrennzeichen ist immer ein Komma und ist zwischen jedem Feld erforderlich.** Das Komma ist im Modulparser fest codiert; kein anderer Trennzeichen wird hier unterstützt.
| Feld | Beschreibung |
|-------------|-----------------------------------------------------------|
| `token` | Base64url-kodiertes HMAC (kein Padding `=`) |
| `timestamp` | Erstellungszeit der Anfrage (siehe [Timestamp-Formate](#timestamp-formate)) |
| `expires` | Optionale Lebensdauer in Sekunden; weglassen oder `0` für unbegrenzt |
```nginx
secure_link_hmac "$arg_st,$arg_ts,$arg_e";
Wichtig: Wenn
secure_link_hmacaus Abfrageparametern ("$arg_st,$arg_ts,$arg_e") zusammengesetzt wird, dürfen die Zeitstempel- und Ablaufwerte selbst keine unescaped Kommas enthalten. ISO 8601- und Unix-Zeitstempel sind kommafrei und funktionieren ohne spezielle Behandlung. RFC 7231-Daten enthalten ein eingebettetes Komma (z. B.Sun, 06 Nov …); das Modul behandelt dies korrekt für das zweite Feld, aber Sie müssen das Komma URL-encodieren, wenn Sie ein RFC 7231-Datum in einem Abfrageparameter platzieren, damit$arg_tsauf den vollständigen decodierten Datumsstring aufgelöst wird (siehe Timestamp-Formate).
secure_link_hmac_message
Kontext: http, server, location
Die Nachricht, deren HMAC überprüft werden soll. Muss genau dem entsprechen, was der Client beim Berechnen des Tokens verwendet hat. Beinhaltet typischerweise die URI und den Zeitstempel, damit die Tokens URI-spezifisch und zeitgebunden sind.
Der Trennzeichen zwischen den Feldern in der Nachricht wird vom
Betreiber frei gewählt und kann jedes Byte oder jede Bytefolge sein — Pipe (|), Doppelpunkt
(:), Slash (/), Bindestrich (-) oder sogar gar nichts. Das Modul
behandelt secure_link_hmac_message als undurchsichtigen Byte-String und parst niemals
seinen Inhalt; der Trennzeichen ist einfach Teil des HMAC-Vorbilds.
Die einzige Anforderung ist, dass der auf der Serverseite gewählte Trennzeichen
identisch mit dem Trennzeichen ist, das der Client beim Berechnen des HMAC verwendet hat.
Die Verwendung eines Trennzeichens, das in keinem der Feldwerte natürlich erscheinen kann
(wie | für URIs und Unix-Zeitstempel), verringert das Risiko von Längenverlängerungs-
Ambiguitäten.
# Pipe-Trennzeichen (empfohlen — kann nicht in einem URI-Pfad oder Unix-Zeitstempel erscheinen)
secure_link_hmac_message "$uri|$arg_ts|$arg_e";
# Doppelpunkt-Trennzeichen
secure_link_hmac_message "$uri:$arg_ts:$arg_e";
# Kein Trennzeichen (gültig, aber mehrdeutig, wenn Felder einen Zeichensatz teilen)
secure_link_hmac_message "$uri$arg_ts$arg_e";
secure_link_hmac_secret
Kontext: http, server, location
Der HMAC-Geheimschlüssel. Halten Sie dies aus der Versionskontrolle heraus.
secure_link_hmac_secret "my_very_secret_key";
secure_link_hmac_algorithm
Kontext: http, server, location
Standard: sha256
Der OpenSSL-Digest-Name, der für das HMAC verwendet wird.
secure_link_hmac_algorithm sha256;
Eingebettete Variablen
$secure_link_hmac
Wird nach der Verarbeitung der secure_link_hmac-Anweisung gesetzt. Mögliche Werte:
| Wert | Bedeutung |
|---|---|
"1" |
Token ist kryptographisch gültig und der Link ist nicht abgelaufen |
"0" |
Token ist gültig, aber der Link ist abgelaufen |
| (leer) | Token fehlt, ist fehlerhaft, HMAC-Mismatch oder Zeitstempel ungültig |
Verwenden Sie diese Variable, um den Zugriff zu steuern. In der Produktion geben Sie denselben Fehlercode für alle fehlerhaften Fälle zurück, damit ein Angreifer nicht zwischen einem abgelaufenen Token und einem gefälschten unterscheiden kann:
if ($secure_link_hmac != "1") {
return 403;
}
Hinweis:
"1"und"0"sind literale einzeilige Zeichenfolgen, keine Zahlen. Der leere / nicht gefundene Fall bedeutet, dass die Variable nicht gesetzt ist, nicht dass sie""entspricht.
$secure_link_hmac_expires
Der rohe Ablaufzeitraum-String (in Sekunden), wie er in der Anfrage empfangen wurde.
Diese Variable wird nur gesetzt, wenn ein Ablauf in secure_link_hmac vorhanden war.
Sie kann für Protokollierung oder bedingte Logik verwendet werden:
add_header X-Link-Expires $secure_link_hmac_expires;
- Wenn der eingehende Wert
"3600"war, enthält diese Variable"3600". - Wenn kein Ablauffeld vorhanden war, ist die Variable nicht gesetzt (not_found).
- Diese Variable wird als Nebeneffekt der Auswertung von
$secure_link_hmacbefüllt; bewerten Sie zuerst$secure_link_hmac.
$secure_link_hmac_token
Ein frisch berechnetes, base64url-kodiertes HMAC-Token (kein nachfolgendes =-Padding).
Verwenden Sie diese Variable, wenn NGINX als Proxy fungiert, der authentifizierte Anfragen an ein Backend weiterleiten muss:
location ^~ /backend/ {
set $expire 60;
secure_link_hmac_message "$uri|$time_iso8601|$expire";
secure_link_hmac_secret "my_very_secret_key";
secure_link_hmac_algorithm sha256;
proxy_pass "http://backend$uri?st=$secure_link_hmac_token&ts=$time_iso8601&e=$expire";
}
Das Token ist base64url-kodiert ohne Padding, kompatibel mit URL-Abfrageparametern ohne weitere Escaping.
Timestamp-Formate
Ein Zeitstempel sollte immer in der signierten Nachricht enthalten sein, um Wiederholungsangriffe zu verhindern. Drei Formate werden vom serverseitigen Parser akzeptiert. Clients können das verwenden, was am bequemsten ist.
ISO 8601 mit numerischem UTC-Offset (empfohlen)
YYYY-MM-DDThh:mm:ss+HH:MM
YYYY-MM-DDThh:mm:ss-HH:MM
Beispiele:
2025-06-01T14:30:00+00:00 # UTC
2025-06-01T17:30:00+03:00 # UTC+3 (Kiew/Istanbul)
2025-06-01T08:30:00-06:00 # UTC-6 (Chicago CDT)
Der Server konvertiert in UTC, bevor er vergleicht, sodass jeder gültige Offset akzeptiert wird.
ISO 8601 UTC (Z-Suffix)
YYYY-MM-DDThh:mm:ssZ
Beispiel: 2025-06-01T14:30:00Z
Entspricht +00:00, ist aber kürzer. Nginx's eingebauter $time_iso8601
Variable gibt das Format +00:00 aus; für Z müssen Sie den Zeitstempel
anwendungseitig formatieren.
RFC 7231 / IMF-fixdate (HTTP-Datum)
Wie in RFC 7231 §7.1.1.1 angegeben. Alle RFC 7231-Daten sind implizit UTC; kein Offset wird angewendet.
Tag, DD Mon YYYY hh:mm:ss GMT
Beispiele:
Sonntag, 01. Juni 2025 14:30:00 GMT
Montag, 23. März 2026 08:00:00 GMT
Dabei ist Tag eine dreibuchstabige Abkürzung für den Wochentag (Mon–Sun) und
Mon (Monat) ist einer von Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec.
Der Parser ist für beide Abkürzungen nicht groß-/kleinschreibungssensitiv.
Hinweis: RFC 7231 definiert auch zwei veraltete Formate (RFC 850 und ANSI C
asctime). Diese werden nicht unterstützt; nur das bevorzugte IMF-fixdate-Format wird akzeptiert.
Unix-Zeitstempel (einfache Ganzzahl)
Eine Zeichenfolge aus Dezimalziffern, die Sekunden seit der Unix-Epoche (1970-01-01T00:00:00Z) darstellt.
Beispiel: 1748785800
Dies ist das einfachste Format und funktioniert gut in Bash und Node.js. Der Parser ist streng: Das Zeitstempelfeld darf nur Dezimalziffern enthalten; jedes andere Zeichen führt zu einer Ablehnung.
Sicherheitsnotiz: Unix-Zeitstempel haben nur eine Auflösung von einer Sekunde. Verwenden Sie ISO 8601, wenn die Sub-Sekunden-Präzision wichtig ist oder wenn Sie eine bestimmte Zeitzone ausdrücken müssen.
Anwendungsbeispiel — Serverseite
location ^~ /files/ {
# Die drei durch Kommas getrennten Felder: Token, Zeitstempel, Ablauf (Sekunden)
secure_link_hmac "$arg_st,$arg_ts,$arg_e";
# HMAC-Geheimschlüssel
secure_link_hmac_secret "my_secret_key";
# Die signierte Nachricht: URI + Zeitstempel + Ablauf
secure_link_hmac_message "$uri|$arg_ts|$arg_e";
# Hash-Algorithmus
secure_link_hmac_algorithm sha256;
# In der Produktion geben Sie nicht preis, ob das Token falsch oder abgelaufen war.
# $secure_link_hmac == "1" → gültig und nicht abgelaufen
# $secure_link_hmac == "0" → gültig, aber abgelaufen
# $secure_link_hmac unset → ungültig / fehlerhaft
if ($secure_link_hmac != "1") {
return 403;
}
rewrite ^/files/(.*)$ /files/$1 break;
}
Client-Seitenbeispiele
Perl — ISO 8601-Zeitstempel
perl_set $secure_token '
sub {
use Digest::SHA qw(hmac_sha256_base64);
use POSIX qw(strftime);
my $r = shift;
my $key = "my_very_secret_key";
my $expire = 60;
my $now = time();
# ISO 8601 mit numerischem UTC-Offset
my $tz = strftime("%z", localtime($now));
$tz =~ s/(\d{2})(\d{2})/$1:$2/;
my $timestamp = strftime("%Y-%m-%dT%H:%M:%S", localtime($now)) . $tz;
my $message = $r->uri . "|" . $timestamp . "|" . $expire;
my $digest = hmac_sha256_base64($message, $key);
$digest =~ tr(+/)(-_); # base64 → base64url
$digest =~ s/=+$//; # Padding entfernen
return "st=$digest&ts=$timestamp&e=$expire";
}
';
PHP — Unix-Zeitstempel
<?php
$secret = 'my_very_secret_key';
$expire = 60;
$algo = 'sha256';
$timestamp = time(); // Unix-Zeitstempel
$uri = '/files/top_secret.pdf';
$message = "{$uri}|{$timestamp}|{$expire}";
$token = base64_encode(hash_hmac($algo, $message, $secret, true));
$token = strtr($token, '+/', '-_'); // base64 → base64url
$token = rtrim($token, '='); // Padding entfernen
$host = $_SERVER['HTTP_HOST'];
$url = "https://{$host}{$uri}?st={$token}&ts={$timestamp}&e={$expire}";
PHP — ISO 8601-Zeitstempel
<?php
$secret = 'my_very_secret_key';
$expire = 60;
$algo = 'sha256';
$timestamp = (new DateTimeImmutable('now', new DateTimeZone('UTC')))
->format(DateTimeInterface::RFC3339); // "2025-06-01T14:30:00+00:00"
$uri = '/files/top_secret.pdf';
$message = "{$uri}|{$timestamp}|{$expire}";
$token = base64_encode(hash_hmac($algo, $message, $secret, true));
$token = strtr($token, '+/', '-_');
$token = rtrim($token, '=');
$url = "https://example.com{$uri}?st={$token}&ts=" . urlencode($timestamp) . "&e={$expire}";
PHP — RFC 7231 / IMF-fixdate-Zeitstempel
<?php
$secret = 'my_very_secret_key';
$expire = 60;
$algo = 'sha256';
// RFC 7231 IMF-fixdate — immer UTC, immer "GMT"-Suffix
$timestamp = gmdate('D, d M Y H:i:s') . ' GMT'; // "Sonntag, 01. Juni 2025 14:30:00 GMT"
$uri = '/files/top_secret.pdf';
$message = "{$uri}|{$timestamp}|{$expire}";
$token = base64_encode(hash_hmac($algo, $message, $secret, true));
$token = strtr($token, '+/', '-_');
$token = rtrim($token, '=');
// URL-encode das RFC 7231-Datum (enthält Leerzeichen und Kommas)
$url = "https://example.com{$uri}?st={$token}&ts=" . rawurlencode($timestamp) . "&e={$expire}";
Node.js — Unix-Zeitstempel
const crypto = require('crypto');
const secret = 'my_very_secret_key';
const expire = 60;
const timestamp = Math.floor(Date.now() / 1000); // Unix-Zeitstempel
const uri = '/files/top_secret.pdf';
const message = `${uri}|${timestamp}|${expire}`;
const token = crypto.createHmac('sha256', secret)
.update(message)
.digest('base64')
.replace(/=/g, '')
.replace(/\+/g, '-')
.replace(/\//g, '_');
const url = `https://example.com${uri}?st=${token}&ts=${timestamp}&e=${expire}`;
Node.js — RFC 7231 / IMF-fixdate-Zeitstempel
const crypto = require('crypto');
const secret = 'my_very_secret_key';
const expire = 60;
// toUTCString() erzeugt das RFC 7231 IMF-fixdate-Format in allen modernen Laufzeiten
const timestamp = new Date().toUTCString(); // "Sonntag, 01. Juni 2025 14:30:00 GMT"
const uri = '/files/top_secret.pdf';
const message = `${uri}|${timestamp}|${expire}`;
const token = crypto.createHmac('sha256', secret)
.update(message)
.digest('base64')
.replace(/=/g, '')
.replace(/\+/g, '-')
.replace(/\//g, '_');
const url = `https://example.com${uri}?st=${token}&ts=${encodeURIComponent(timestamp)}&e=${expire}`;
Python — ISO 8601-Zeitstempel (UTC Z-Suffix)
import hmac, hashlib, base64, urllib.parse
from datetime import datetime, timezone
secret = b'my_very_secret_key'
expire = 60
timestamp = datetime.now(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')
uri = '/files/top_secret.pdf'
message = f'{uri}|{timestamp}|{expire}'.encode()
token = base64.urlsafe_b64encode(
hmac.new(secret, message, hashlib.sha256).digest()
).rstrip(b'=').decode()
url = f'https://example.com{uri}?st={token}&ts={urllib.parse.quote(timestamp)}&e={expire}'
Bash — Unix-Zeitstempel
#!/bin/bash
SECRET="my_super_secret"
URI="/file/my_secret_file.txt"
TIMESTAMP="$(date +%s)"
EXPIRES=3600
MESSAGE="${URI}|${TIMESTAMP}|${EXPIRES}"
TOKEN="$(printf '%s' "$MESSAGE" \
| openssl dgst -sha256 -hmac "$SECRET" -binary \
| openssl base64 \
| tr '+/' '-_' \
| tr -d '=')"
echo "http://127.0.0.1${URI}?st=${TOKEN}&ts=${TIMESTAMP}&e=${EXPIRES}"
Proxy-Nutzung
Wenn NGINX als Proxy fungiert, der ein HMAC-Token zu ausgehenden Anfragen hinzufügen muss,
verwenden Sie die Variable $secure_link_hmac_token:
location ^~ /backend_location/ {
set $expire 60;
secure_link_hmac_message "$uri|$time_iso8601|$expire";
secure_link_hmac_secret "my_very_secret_key";
secure_link_hmac_algorithm sha256;
proxy_pass "http://backend_server$uri?st=$secure_link_hmac_token&ts=$time_iso8601&e=$expire";
}
Hinweis:
$time_iso8601gibt einen ISO 8601-Zeitstempel mit einem numerischen UTC Offset aus (z. B.2025-06-01T14:30:00+00:00), den dieses Modul akzeptiert.
Sicherheitsnotizen
Trennzeichen in secure_link_hmac
Der Feldtrennzeichen innerhalb des Wertes der secure_link_hmac-Anweisung ist
immer ein Komma. Die Zeitstempel- und Ablauffelder dürfen keine nackten
Kommas enthalten (ISO 8601- und Unix-Zeitstempel sind sicher; RFC 7231-Zeitstempel werden
durch die interne Komma-Überspring-Logik des Moduls behandelt, aber das eingebettete Komma
muss URL-encodiert/decodiert intakt bleiben — siehe
Timestamp-Formate).
Trennzeichen in secure_link_hmac_message
Wählen Sie ein Trennzeichen, das in keinem der zusammengefügten Felder erscheinen kann. Pipe (|) ist ein guter Standard für URI + Unix-Zeitstempel-Kombinationen. Die Verwendung von gar keinem Trennzeichen ist gültig, kann jedoch einen Längenverlängerungsangriff ermöglichen, bei dem ein gültiges Set von Feldwerten als ein anderes Set uminterpretiert wird; ein Trennzeichen verhindert dies.
Weitere Empfehlungen
- Immer einen Zeitstempel in der signierten Nachricht einfügen, um Wiederholungsangriffe zu verhindern.
- Wählen Sie einen kurzen expires-Wert für Ihren Anwendungsfall (60–3600 Sekunden ist
typisch für Download-Links).
- Geben Sie denselben HTTP-Fehlercode (z. B. 403) für alle Fehlerfälle zurück —
sowohl "0" (abgelaufen) als auch nicht gefunden (ungültig) — damit Angreifer nicht
zwischen einem abgelaufenen Token und einem gefälschten unterscheiden können.
- Verwenden Sie einen geheimen Schlüssel mit mindestens 32 Bytes zufälliger Entropie.
- Bevorzugen Sie sha256 oder stärker; vermeiden Sie md5 und sha1 für neue Bereitstellungen.
- URL-encodieren Sie Zeitstempelwerte, die Zeichen enthalten, die in Abfragezeichenfolgen speziell sind:
- ISO 8601-UTC-Offset + muss als %2B gesendet werden (ansonsten als Leerzeichen decodiert)
- RFC 7231-Leerzeichen müssen als %20 gesendet werden und das eingebettete Komma als %2C
GitHub
Sie finden möglicherweise zusätzliche Konfigurationstipps und Dokumentationen für dieses Modul im GitHub Repository für nginx-module-hmac-secure-link.