我们可以在每个响应中更改 Varnish 中的部分输出吗

Can we change part of the output in Varnish in each response

假设我们想为 Varnish 缓存的每个响应提供一个唯一的 CSRF 令牌。

有没有办法挂钩 Varnish 输出,以便我们可以通过编程方式更改内容的特定部分?

带有 ESI 的 Varnish 缓存解决方案 & vmod_digest

在Varnish的开源版本中,你可以使用Edge Side Includes。

您可以在输出中放置以下占位符:

<esi:include src="/csrf-token-esi" />

此 ESI 标签将被 Varnish 解析,/csrf-token-esi 端点可被 Varnish 拦截并更改。

Be sure to return a Surrogate-Control: Varnish=ESI/1.0 response header on the page that contains the ESI tag. This header will force Varnish to parse the ESI tag.

通过合成响应,我们可以更改 /csrf-token-esi 的响应主体。

这是我们使用 vmod_digest 创建 HMAC 签名作为 CSRF 令牌的示例:

vcl 4.1;

import digest;

backend default {
    .host = "localhost";
    .port = "8080";
}

sub vcl_recv {
    set req.http.Surrogate-Capability = "Varnish=ESI/1.0";
    if(req.url == "/csrf-token-esi") {
        return(synth(777,digest.hmac_sha256("secret-key", now)));
    }
}

sub vcl_backend_response {
    if (beresp.http.Surrogate-Control ~ "ESI/1.0") {
        unset beresp.http.Surrogate-Control;
        set beresp.do_esi = true;
    }    
}

sub vcl_synth {
    if(resp.status == 777) {
        set resp.body = resp.reason;
        set resp.http.csrf-token = resp.reason;
        set resp.status = 200;
        return(deliver);
    }
}

FYI: you can download the vmod_digest source from https://github.com/varnish/libvmod-digest. You need to compile this from source or use another mechanism to create a unique value.

CSRF 令牌的值由以下代码行生成:

return(synth(777,digest.hmac_sha256("secret-key", now)));

secret-key 是 HMAC 的签名密钥,now returns Varnish 中的当前时间戳,即要签名的值。

There are plenty of other ways to generate a CSRF token. vmod_digest also has other ways to generate unique output. Just remember that the 2nd argument of the synth() function is the response body and effectively the value of the CSRF token.

具有 vmod_edgestash 和 vmod_crypto

的清漆企业解决方案

在 Varnish Enterprise 中,所有必需的 VMOD 都已打包,因此无需从源代码编译它们。 vmod_edgestashvmod_crypto VMOD 是企业 VMOD。

解决方案包括在您的回复中添加一个 {{csrf}} 占位符。这个占位符的格式是Mustache语法,由vmod_edgestash.

解析

与 Varnish 缓存解决方案不同,不需要额外的往返和请求拦截。 Varnish Enterprise 将缓存包含占位符的页面,并在交付时自定义 CSRF 令牌。

这是 VCL 代码:

vcl 4.1;

import crypto;
import edgestash;

backend default {
    .host = "localhost";
    .port = "8080";
}

sub vcl_backend_response{
    if (beresp.http.Content-Type ~ "html") {
        edgestash.parse_response();
    }
}

sub vcl_deliver {
    if (edgestash.is_edgestash()) {
        set req.http.csrf = crypto.hex_encode(crypto.urandom(16));
        edgestash.add_json({"
            {"csrf":""} + req.http.csrf + {""
            }
        "});
        edgestash.execute();
    }
}

在本例中,我们使用 crypto.hex_encode(crypto.urandom(16)) 生成唯一值。但同样,您可以使用 vmod_crypto 提供的任何函数来生成此唯一值。

有关 vmod_crypto 的更多信息,请参阅 https://docs.varnish-software.com/varnish-cache-plus/vmods/edgestash/ for more information about vmod_edgestash and https://docs.varnish-software.com/varnish-cache-plus/vmods/total-encryption/