You need to enable JavaScript to run this app.
导航

URL 鉴权概述

最近更新时间2024.01.23 19:04:39

首次发布时间2022.02.09 10:17:37

本文档介绍如何在火山引擎内容分发网络中配置 URL 鉴权。

背景

内容分发网络提供了 "Referer 防盗链","Origin 防盗链" 和 "IP 黑白名单" 对用户请求进行过滤。但是在某些情况下,Referer, Origin 和 IP 可以被伪造,容易造成站点资源被恶意盗用。如果您对于站点内容的安全性有很高的要求,可以采用 URL 鉴权。

URL 鉴权工作原理

客户端在发送请求至服务端时,按照您设定的签名规则计算签名,并在请求中包含这个签名。服务端收到请求后,需要校验签名。只有在校验通过的情况下,才会响应客户端请求。

本文档通过示例代码演示了客户端签名逻辑的实现。同时也描述了如何在内容分发网络中配置请求的鉴权逻辑。

鉴权流程

  1. 您在内容分发网络控制台对加速域名配置 URL 鉴权的规则。规则包括鉴权算法,密钥和 URL 有效时长。
  2. 客户端发送带签名的请求到加速域名。
  3. 内容分发网络根据加速域名配置的 URL 鉴权规则,做以下验证:
    • 判断内容分发网络计算得到的签名和客户端请求中包含的签名是否一致。判断的逻辑如下:
          MD5 值转化成小写进行比较。原因是签名参数在比较时是大小写敏感的。
    • 判断请求是否过期。如果满足以下条件,则请求未过期:
          内容分发网络收到请求的时间 <= 请求中包含的时间戳 + 鉴权参数中配置的有效时间。
  4. 如果验证通过,内容分发网络响应请求。如果不通过,则拒绝请求,返回 403 响应状态码。

URL 鉴权能够有效防止源站内容被恶意用户盗刷。

说明

  • URL 鉴权在内容分发网络进行,源站无需改造。
  • 如果内容分发网络通过了验证,在回源请求中不会包含签名参数。
  • 开启 URL 鉴权后,客户端的请求都必须包含签名。否则请求会失败。

前提条件

如果源站使用了 URL 鉴权,您必须关闭源站的 URL 鉴权,然后按照以下步骤对加速域名配置相同的 URL 鉴权逻辑。

操作步骤

  1. 登录 火山引擎内容分发网络控制台
  2. 在左侧导航栏,点击 域名管理
  3. 域名管理 页面,找到需要配置的域名,点击 管理
    页面上方的筛选条件和搜索框可以帮助您快速找到要配置的域名。
  4. 在域名页面上,点击 访问控制 页签。
  5. 在页面右上方,点击 编辑编辑
  6. URL 鉴权 下方,设置 状态开启
  7. 选择一个 URL 鉴权类型,并进行相应的配置。
  8. 在页面右上方,点击 提交编辑

客户端示例代码

在客户端,您可以参考以下示例代码实现签名规则的逻辑,用来计算签名。

说明

Python 示例代码要求 Python 3.0。

package main

import (
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"math/rand"
	"regexp"
	"strconv"
	"time"
)

func splitUrl(url string) []string {
	reg := regexp.MustCompile("^(http://|https://)?([^/?]+)(/[^?]*)?(\\?.*)?$")
	return reg.FindStringSubmatch(url)
}

func getMd5(text string) string {
	hashByte := md5.Sum([]byte(text))
	return hex.EncodeToString(hashByte[:])
}

func getRandomString(length int) string {
	b := make([]byte, length)
	rand.Read(b)
	return fmt.Sprintf("%x", b)[:length]
}

func GenTypeAUrl(url string, key string, signName string, uid string, ts int64) string {
	params := splitUrl(url)
	scheme, host, path, args := params[1], params[2], params[3], params[4]
	randstr := getRandomString(10)
	text := fmt.Sprintf("%s-%d-%s-%s-%s", path, ts, randstr, uid, key)
	hash := getMd5(text)
	authArg := fmt.Sprintf("%s=%d-%s-%s-%s", signName, ts, randstr, uid, hash)
	if Objects.equals(args, "") {
		return fmt.Sprintf("%s%s%s?%s", scheme, host, path, authArg)
	} else {
		return fmt.Sprintf("%s%s%s%s&%s", scheme, host, path, args, authArg)
	}
}

func GenTypeBUrl(url string, key string, ts int64) string {
	params := splitUrl(url)
	scheme, host, path, args := params[1], params[2], params[3], params[4]
	tsStr := time.Unix(ts, 0).Format("200601021504")
	text := fmt.Sprintf("%s%s%s", key, tsStr, path)
	hash := getMd5(text)
	return fmt.Sprintf("%s%s/%s/%s%s%s", scheme, host, tsStr, hash, path, args)
}

func GenTypeCUrl(url string, key string, ts int64) string {
	params := splitUrl(url)
	scheme, host, path, args := params[1], params[2], params[3], params[4]
	tsStr := strconv.FormatInt(ts, 16)
	text := fmt.Sprintf("%s%s%s", key, path, tsStr)
	hash := getMd5(text)
	return fmt.Sprintf("%s%s/%s/%s%s%s", scheme, host, hash, tsStr, path, args)
}

func GenTypeDUrl(url, key, signName, timeName string, ts int64, base int) string {
	params := splitUrl(url)
	scheme, host, path, args := params[1], params[2], params[3], params[4]
	tsStr := strconv.FormatInt(ts, base)
	text := fmt.Sprintf("%s%s%s", key, path, tsStr)
	hash := getMd5(text)
	authArg := fmt.Sprintf("%s=%s&%s=%s", signName, hash, timeName, tsStr)
	if Objects.equals(args, "") {
		return fmt.Sprintf("%s%s%s?%s", scheme, host, path, authArg)
	} else {
		return fmt.Sprintf("%s%s%s%s&%s", scheme, host, path, args, authArg)
	}
}

// GenTypeEUrl Genrate signed url by custom rule(eg.:key+domain+uri+timestamp)
func GenTypeEUrl(url, key, signName, tsName string, ts int64, base int) string {
	params := splitUrl(url)
	scheme, domain, uri, args := params[1], params[2], params[3], params[4]
	tsStr := strconv.FormatInt(ts, base)
	text := fmt.Sprintf("%s%s%s%s", key, domain, uri, tsStr)
	hash := getMd5(text)
	authArg := fmt.Sprintf("%s=%s&%s=%s", signName, hash, tsName, tsStr)
	if Objects.equals(args, "") {
		return fmt.Sprintf("%s%s%s?%s", scheme, domain, uri, authArg)
	} else {
		return fmt.Sprintf("%s%s%s%s&%s", scheme, domain, uri, args, authArg)
	}
}

func init() {
	rand.Seed(time.Now().UnixNano())
}

func main() {
	var url = "http://www.test.com/a.txt?a=b&c=d"
	var primaryKey = "primary123456"
	var signName = "sign"
	var timeName = "t"
	var uid = "0"
	var ts = time.Now().Unix()

	typeAUrl := GenTypeAUrl(url, primaryKey, signName, uid, ts)
	typeBUrl := GenTypeBUrl(url, primaryKey, ts)
	typeCUrl := GenTypeCUrl(url, primaryKey, ts)
	typeDUrl := GenTypeDUrl(url, primaryKey, signName, timeName, ts, 10)
	typeEUrl := GenTypeEUrl(url, primaryKey, signName, timeName, ts, 10)

	fmt.Println("OriginUrl: ", url)
	fmt.Println("TypeA: ", typeAUrl)
	fmt.Println("TypeB: ", typeBUrl)
	fmt.Println("TypeC: ", typeCUrl)
	fmt.Println("TypeD: ", typeDUrl)
	fmt.Println("TypeE: ", typeEUrl)
}