本文档介绍如何在火山引擎内容分发网络(CDN)中配置 URL 鉴权。
CDN 提供了 "Referer 防盗链","Origin 防盗链" 和 "IP 黑白名单" 对用户请求进行过滤。但是在某些情况下,Referer, Origin 和 IP 可以被伪造,容易造成站点资源被恶意盗用。如果您对于站点内容的安全性有很高的要求,可以采用 URL 鉴权。
客户端在发送请求至服务端时,按照您设定的签名规则计算签名,并在请求中包含这个签名。服务端收到请求后,需要校验签名。只有在校验通过的情况下,才会响应客户端请求。
本文档通过 示例代码 演示了客户端签名逻辑的实现。同时也描述了如何在 CDN 中配置请求的鉴权逻辑。
URL 鉴权能够有效防止源站内容被恶意用户盗刷。
说明
控制台提供了鉴权计算器,一个便利的小工具。在完成 URL 鉴权的配置后,您可以使用鉴权计算器来:
生成鉴权 URL。该 URL 除了包含签名,也符合您指定的鉴权类型所定义的 URL 格式。通过发送一个包含该 URL 的请求,您可以验证您的 URL 鉴权配置是否符合预期。
验证客户端生成的鉴权 URL 是否与鉴权计算器生成的相同。
验证签名的过期时间是否符合预期。
上点击 鉴权计算器。 鉴权计算器 页面会自动获取加速域名的 URL 鉴权配置。如果您没有配置备密钥,鉴权计算器会自动创建一个供参考,您也可以删除该备密钥。如果您删除了备密钥,在生成的鉴权 URL 中,就不会包含备用鉴权 URL。
在 原始 URL 处,输入您的原始请求 URL。
(可选)指定一个 开始时间。默认情况下,开始时间 就是当前时间,用于计算签名的过期时间。
点击 生成鉴权,然后查看生成的鉴权 URL 以及签名的过期时间。
如果源站使用了 URL 鉴权,您需要按照以下步骤对加速域名配置相同的 URL 鉴权逻辑。同时,您必须关闭源站上的 URL 鉴权,因为回源请求是不包含签名的。
客户端请求中包含的签名可以由一个独立的签名计算服务器提供,也可以由客户端生成。您可以参考以下示例代码,在服务器上实现签名计算逻辑。如果签名是由客户端生成,您可以参考这些示例代码,使用客户端编程语言,例如 Objective-C,来实现签名计算逻辑。
说明
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) }