跳转至

auth-hash: 安全链接哈希认证

安装

您可以在任何基于 RHEL 的发行版中安装此模块,包括但不限于:

  • RedHat Enterprise Linux 7、8、9 和 10
  • CentOS 7、8、9
  • AlmaLinux 8、9
  • Rocky Linux 8、9
  • Amazon Linux 2 和 Amazon Linux 2023
dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install nginx-module-auth-hash
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-auth-hash

通过在 /etc/nginx/nginx.conf 的顶部添加以下内容来启用该模块:

load_module modules/ngx_http_auth_hash_module.so;

本文档描述了 nginx-module-auth-hash v0.1.0,于 2026 年 1 月 6 日发布。


描述:

Nginx 安全链接 HASH 模块增强了标准安全链接模块的安全性和功能。安全令牌是使用安全 HASH 构造和 OpenSSL 支持的任意哈希算法创建的,例如: blake2b512blake2s256gostmd4md5mdc2rmd160sha1sha224sha256sha3-224sha3-256sha3-384sha3-512sha384sha512sha512-224sha512-256shake128shake256sm3

用法:

要哈希的消息由 auth_hash_message 定义,secret_keyauth_hash_secret 给出,哈希算法 H 由 auth_hash_algorithm 定义。

为了提高安全性,应该将时间或时间戳(取决于格式参数指定的日期格式)附加到要哈希的消息中。

可以创建具有有限生命周期的链接。这由可选参数 range_start 或 range_end 定义。如果未指定过期时间,则链接具有无限生命周期。

服务器端的配置示例。

location ^~ /files/ {
    # 启用该功能,如果禁用,$auth_hash 将始终为空
    auth_hash on;

    # 设置用于检查的时间值。
    # 您可以设置过期时间范围、时间值的格式和时间值的时区
    auth_hash_check_time $arg_ts range_end=$arg_e format=%s;

    # 设置用于检查的令牌值
    # 可用格式为 hex(默认)、base64、base64url 和 bin
    auth_hash_check_token $arg_st format=hex;

    # 秘钥
    auth_hash_secret "my_secret_key";

    # 要验证的消息
    auth_hash_message "$uri|$arg_ts|$arg_e|$auth_hash_secret";

    # 要使用的加密哈希函数
    auth_hash_algorithm sha256;

    # 在生产环境中,我们不应向潜在攻击者透露
    # 哈希认证失败的原因
    # - 如果哈希不正确,则 $auth_hash 是 NULL 字符串。
    # - 如果哈希正确且链接未过期,则 $auth_hash 为 "1"。
    if ($auth_hash != "1") {
        return 403;
    }

    rewrite ^/files/(.*)$ /files/$1 break;
}

应用程序端应使用标准哈希函数生成哈希,然后需要进行 hex 或 base64url 编码。以下是 Perl 的示例。

变量 $data 包含安全令牌、ISO 8601 格式的时间戳和以秒为单位的过期时间

perl_set $secure_token '
    sub {
        use Digest::SHA qw(sha256_base64);
        use POSIX qw(strftime);

        my $now = time();
        my $secret = "my_very_secret_key";
        my $expire = 60;
        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 $r = shift;
        my $data = $r->uri;

        # hex
        my $string_to_hash = $data . "|" . $timestamp . "|" . $expire . "|" . $secret;
        my $digest_binary = sha256($string_to_hash);
        my $digest = unpack("H*", $digest_binary);

        # base64url
        # my $digest = sha256_base64($data . "|" . $timestamp . "|" . $expire . "|" . $secret);
        # $digest =~ tr/+/_/;
        # $digest =~ s/=+$//;

        $data = "st=" . $digest . "&ts=" . $timestamp . "&e=" . $expire;
        return $data;
    }
';

PHP 中的类似函数

$secret = 'my_very_secret_key';
$expire = 60;
$algo = 'sha256';
$timestamp = date('c');
$unixtimestamp = time();
$stringtosign = "/files/top_secret.pdf|{$unixtimestamp}|{$expire}|{$secret}";
// hex
$hash = bin2hex(hash($algo, $stringtosign, true));
// base64url
// $hash = base64_encode(hash($algo, $stringtosign, true));
// $hash = strtr($hash, '+/', '-_');
// $hash = str_replace('=', '', $hash);
$host = $_SERVER['HTTP_HOST'];
$loc = "https://{$host}/files/top_secret.pdf?st={$hash}&ts={$unixtimestamp}&e={$expire}";

在 Node.js 中使用 Unix 时间戳

const crypto = require("crypto");
const secret = 'my_very_secret_key';
const expire = 60;
const unixTimestamp = Math.round(Date.now() / 1000.);
const stringToSign = `/files/top_secret.pdf|${unixTimestamp}|${expire}|${secret}`;
// hex
const hash = crypto.createHash('sha256').update(stringToSign).digest('hex')
// base64url
// const hash = crypto.createHash('sha256').update(stringToSign).digest('base64')
//       .replace(/=/g, '')
//       .replace(/\+/g, '-')
//       .replace(/\//g, '_');
const loc = `https://host/files/top_secret.pdf?st=${hash}&ts=${unixTimestamp}&e=${expire}`;

Bash 版本

#!/bin/bash

SECRET="my_super_secret"
TIME_STAMP="$(date -d "today + 0 minutes" +%s)";
EXPIRES="3600"; # seconds
URL="/file/my_secret_file.txt"
ST="$URL|$TIME_STAMP|$EXPIRES|$SECRET"
## hex
TOKEN="$(echo -n $ST | openssl dgst -sha256 | awk '{print $1}')"
## Base64url
## TOKEN="$(echo -n $ST | openssl dgst -sha256 -binary | openssl base64 | tr +/ -_ | tr -d =)"

echo "http://127.0.0.1$URL?st=$TOKEN&ts=$TIME_STAMP&e=$EXPIRES"

嵌入变量

  • $auth_hash - 如果哈希正确且链接未过期,则 $auth_hash 为 "1"。否则,它为 null。
  • $auth_hash_secret - auth_hash_secret 指令的值

GitHub

您可以在 nginx-module-auth-hash 的 GitHub 仓库中找到此模块的其他配置提示和文档。