You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为Go Handler服务的图片添加请求上下文 避免URL暴露敏感信息

解决方案

方案1:直接内嵌Base64 QR码(最推荐,实现最简单,彻底规避接口暴露问题)

你在渲染页面的qrcodePageHandler里已经可以拿到合法的loc对象,完全可以直接在这个Handler内生成QR码,转成Base64 Data URI直接传给模板,不需要单独暴露/qr.png/接口:

import "encoding/base64"

func qrcodePageHandler(w http.ResponseWriter, r *http.Request) {
    loc, ok := r.Context().Value("loc").(authentification.Location)
    if !ok {
        http.Error(w, fmt.Sprintf("Error while creating qrcode-Page: Failed to load Location"), http.StatusBadRequest)
        return
    }
    // 直接在页面渲染阶段生成QR码
    _, qrcode, err := qrCodes.QRCode(loc, fmt.Sprintf("%s%s", SrvUrl, "?token="), qrRecoveryLevel, 400)
    if err != nil {
        http.Error(w, fmt.Sprintf("Error while creating qrcode: %v", err), http.StatusInternalServerError)
        return
    }
    // 转Base64 Data URI
    qrBase64 := fmt.Sprintf("data:image/png;base64,%s", base64.StdEncoding.EncodeToString(qrcode))
    
    err := executeTemplateFunc("qr", qrPage, w, qrPageContent{
        QrBase64: qrBase64, // 替换原来的Loc字段
        Timer: RefreshTimer,
    })
    if err != nil {
        http.Error(w, fmt.Sprintf("Error while creating qrcode-Page: %v", err), http.StatusInternalServerError)
        return
    }
}

对应HTML模板直接改成:

<img alt="Failed to load QR-Code" src="{{.QrBase64}}">

这个方案完全删除了独立的QR码接口,用户不可能拿到独立的图片地址,自然不存在绕过访问的风险,且不需要额外依赖缓存、签名逻辑,性能损耗极小。


方案2:临时令牌校验(适合需要独立QR码接口、QR码需频繁刷新的场景)

  1. 新增一个带过期时间的临时存储(比如内存sync.Map或Redis),用于映射临时令牌和Location的关系
  2. 页面渲染阶段生成随机一次性令牌,将令牌和对应Location存入缓存,过期时间和页面刷新时间RefreshTimer对齐即可
  3. 模板中img的src改为/qr.png/{{.TempToken}},不再暴露明文Location
  4. 修改qrWrapper逻辑,从路径中提取临时令牌,去缓存查询对应的Location,查询失败直接返回403,查询成功再执行后续逻辑
import (
    "sync"
    "time"
    "github.com/google/uuid"
)

// 示例:全局临时缓存,自动清理过期令牌
var qrTokenCache = sync.Map{}

func qrcodePageHandler(w http.ResponseWriter, r *http.Request) {
    loc, ok := r.Context().Value("loc").(authentification.Location)
    if !ok {
        http.Error(w, fmt.Sprintf("Error while creating qrcode-Page: Failed to load Location"), http.StatusBadRequest)
        return
    }
    // 生成随机临时令牌
    tempToken := uuid.NewString()
    // 存入缓存,带过期时间
    qrTokenCache.Store(tempToken, map[string]interface{}{
        "loc": loc,
        "expire": time.Now().Add(RefreshTimer * time.Second),
    })
    // 异步清理过期令牌(也可以单独开定时任务全局清理)
    go func(token string) {
        time.Sleep(RefreshTimer * time.Second)
        qrTokenCache.Delete(token)
    }(tempToken)
    
    err := executeTemplateFunc("qr", qrPage, w, qrPageContent{
        TempToken: tempToken,
        Timer: RefreshTimer,
    })
    if err != nil {
        http.Error(w, fmt.Sprintf("Error while creating qrcode-Page: %v", err), http.StatusInternalServerError)
        return
    }
}

// 修改qrWrapper逻辑
func qrWrapper(lf locationCheckerFunc, cf portCheckerFunc, handler http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        token := path.Base(r.URL.Path)
        // 查缓存
        cacheVal, ok := qrTokenCache.Load(token)
        if !ok {
            http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
            return
        }
        cacheData := cacheVal.(map[string]interface{})
        // 校验过期
        if cacheData["expire"].(time.Time).Before(time.Now()) {
            qrTokenCache.Delete(token)
            http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
            return
        }
        locStruct := cacheData["loc"].(authentification.Location)
        // 原有端口校验逻辑保留
        if !cf(w, *r, QrPort, http.StatusForbidden) {
            return
        }
        ctx := context.WithValue(r.Context(), "loc", locStruct)
        handler(w, r.WithContext(ctx))
    }
}

该方案即便用户拿到图片地址,令牌也会很快过期或失效,无法重复使用。


辅助加固手段

可在qrWrapper中新增Referer校验,仅允许来自本站点的请求访问QR码接口,作为额外防护层:

referer := r.Header.Get("Referer")
if !strings.HasPrefix(referer, SrvUrl) {
    http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
    return
}

注:Referer可被伪造,仅能作为辅助校验,不能作为唯一判断依据

内容的提问来源于stack exchange,提问作者Schesam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 03:24:04