OSS支持对存储空间(Bucket)设置防盗链,即通过对访问来源设置白名单的机制,避免OSS资源被其他人盗用。
功能介绍
防盗链功能通过设置Referer白名单以及是否允许空Referer,限制仅白名单中的域名可以访问您Bucket内的资源。OSS支持基于HTTP和HTTPS header中表头字段Referer的方法设置防盗链。
是否进行防盗链验证的具体场景如下:
- 仅当通过签名URL访问Object(调用GetObject接口)或者匿名访问Object时,进行防盗链验证。
- 当请求的Header中包含
Authorization
字段,不进行防盗链验证。
- 用户A在https://10.10.10.10嵌入test.jpg图片,当浏览器请求访问此图片时会带上https://10.10.10.10的Referer,此场景下OSS将允许该请求的访问。
- 用户B盗用了test.jpg的图片链接并将其嵌入https://192.168.0.0,当浏览器请求访问此图片时会带上https://192.168.0.0的Referer,此场景下OSS将拒绝该请求的访问。
Referer配置规则
- 单个Bucket支持配置多个Referer。通过控制台设置Referer时,使用回车作为换行符分隔;通过API设置Referer时,使用英文逗号(,)分隔。
- Referer支持使用通配符星号(*)和问号(?)。
- 通配符星号(*)表示使用星号代替0个或多个字符。如果Referer白名单配置为*.aliyun.com,且不允许空Referer的情况下,则只有HTTP或HTTPS header中包含Referer字段的请求才能访问OSS资源,例如www.aliyun.com。如果Referer白名单配置为*.aliyun.com,且允许空Referer的情况下,则Referer为空的请求也允许访问OSS资源。
- 通配符问号(?)表示使用问号代替一个字符。如果设置了包含通配符问号(?)的Referer白名单,且不允许空Referer的情况下,则只有HTTP或HTTPS Header中包含Referer字段的请求才能访问OSS资源。如果设置了包含通配符问号(?)的Referer白名单,且允许空Referer的情况下,则Referer为空的请求也允许访问OSS资源。
- Referer支持使用反斜线(\)对通配符星号(*)和问号(?)进行转义。
- Referer默认截断QueryString。如有特殊需求,可设置不允许截断QueryString。
例如,Referer设置为http://www.example.com/?action=nop,由于OSS匹配该Referer时默认截断QueryString,即使用http://www.example.com/进行匹配。如果希望OSS使用http://www.example.com/?action=nop进行Referer匹配,请通过设置AllowTruncateQueryString参数为false实现不允许截断QueryString。关于设置不允许截断QueryString的具体操作,请参见PutBucketReferer。
设置不允许截断QueryString时,Referer匹配规则有以下注意事项:
- 不解码QueryString通过http://www.example.com/?job_id=task$01访问OSS时,可能该访问URL会被继续保留为http://www.example.com/?job_id=task$01,也可能被转义为http://www.example.com/?job_id=task%2401。通过Referer进行匹配时,不会对访问URL中的QueryString进行解码,示例如下:
- 当Referer设置为http://www.example.com/?job_id=task%2401的情况下,通过http://www.example.com/?job_id=task$01请求访问OSS时,OSS将拒绝该请求。
- 当Referer设置为http://www.example.com/?job_id=task$01的情况下,通过http://www.example.com/?job_id=task%2401请求访问OSS时,OSS将拒绝该请求。
- 忽略QueryString中的参数大小写
通过Referer进行匹配时,会忽略QueryString中的大小写。即当Referer设置为http://www.example.com/?action=nop的情况下,通过http://www.examplecom/?ACTION=NOP请求访问OSS时,OSS将允许该请求。
- 不解析QueryString中的参数
默认情况下,浏览器会将http://example.com/?a=b&b=c与http://example.com/?b=c&a=b视为相同的访问URL。但是通过Referer进行匹配时,不会对QueryString中的参数进行解析,因此http://example.com/?a=b&b=c与http://example.com/?b=c&a=b会被视为不同的访问URL。即当Referer设置为http://example.com/?a=b&b=c的情况下,通过http://example.com/?b=c&a=b请求访问OSS时,OSS将拒绝该请求。
- 不解码QueryString
Referer配置效果
- 如果Referer白名单为通配符星号(*),且不允许空Referer,则带Referer且Referer不为空的请求会被允许。
- 如果Referer白名单为通配符星号(*),且允许空Referer,则带Referer且Referer为空的请求会被允许。
- 如果Referer白名单不为通配符星号(*),且不允许空Referer,则只有Referer属于白名单的请求被允许,其他请求(包括Referer为空的请求)会被拒绝。
- 如果Referer白名单不为通配符星号(*),但允许空Referer,则Referer为空的请求和符合白名单的请求会被允许,其他请求都会被拒绝。
使用OSS控制台
- 登录OSS管理控制台。
- 单击Bucket列表,然后单击目标Bucket名称。
- 在左侧导航栏,选择 。
- 在防盗链页面,打开防盗链开关。说明
- 当您使用OSS的Bucket域名(bucketname.oss-cn-zhangjiakou.aliyuncs.com)预览MP4文件时,由于浏览器默认会同时发出两个请求,其中一个为带Referer的请求,另一个为空Referer的请求,因此设置防盗链时必须同时满足在Referer中添加Bucket域名,且允许空Referer的条件。
- 当您使用OSS的Bucket域名(bucketname.oss-cn-zhangjiakou.aliyuncs.com)预览非MP4文件时,则仅需允许空Referer。
- 在Referer输入框中,填写域名或IP地址。
Referer配置规范如下。
规范 示例 Referer为域名时,必须携带HTTP或者HTTPS协议。 例如,配置为 http://www.aliyun.com
,可匹配ttp://www.aliyun.com/123、http://www.aliyun.com.cn等以http://www.aliyun.com为前缀的地址,不能匹配https://www.aliyun.com/123、https://www.aliyun.com.cn等以https://www.aliyun.com为前缀的地址。Referer支持通配符星号(*)表示使用星号代替0个或多个字符。 例如,配置为 *www.example.com
,可匹配http://www.example.com、https://www.example.com。配置为
*.example.com
,可匹配http://help.example.com、https://help.example.com、http://www.example.com、https://www.example.com。Referer支持通配符问号(?)表示使用问号代替一个字符。 例如,配置为 http://www.aliyun?.com
。Referer支持带端口的域名或IP地址。 例如,配置为 http://www.example.com:8080
、10.10.10.10:8080
。多个Referer以换行分隔。 例如,配置为
。http://www.aliyun.com http://www.alibabacloud.com
- 在空Referer区域,选择是否允许Referer为空。
- 允许:空Referer表示HTTP或HTTPS请求中,不带Referer字段或Referer字段为空。
- 不允许:如果不允许空Referer,则只有HTTP或HTTPS Header中包含Referer字段的请求才能访问OSS资源。
- 在截断QueryString区域,选择是否允许截断QueryString。
- 单击保存。
使用阿里云SDK
以下仅列举常见SDK的设置防盗链的代码示例。关于其他SDK的设置防盗链的代码示例,请参见SDK简介。
import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.model.BucketReferer;
import java.util.ArrayList;
import java.util.List;
public class Demo {
public static void main(String[] args) throws Exception {
// Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。
String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
// 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。
String accessKeyId = "yourAccessKeyId";
String accessKeySecret = "yourAccessKeySecret";
// 填写Bucket名称,例如examplebucket。
String bucketName = "examplebucket";
// 创建OSSClient实例。
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
try {
List<String> refererList = new ArrayList<String>();
// 添加Referer白名单。Referer参数支持通配符星号(*)和问号(?)。
refererList.add("http://www.aliyun.com");
refererList.add("http://www.*.com");
refererList.add("http://www.?.aliyuncs.com");
// 设置存储空间Referer列表。设为true表示Referer字段允许为空,设为false表示Referer字段不允许为空。
BucketReferer br = new BucketReferer(true, refererList);
ossClient.setBucketReferer(bucketName, br);
} catch (OSSException oe) {
System.out.println("Caught an OSSException, which means your request made it to OSS, "
+ "but was rejected with an error response for some reason.");
System.out.println("Error Message:" + oe.getErrorMessage());
System.out.println("Error Code:" + oe.getErrorCode());
System.out.println("Request ID:" + oe.getRequestId());
System.out.println("Host ID:" + oe.getHostId());
} catch (ClientException ce) {
System.out.println("Caught an ClientException, which means the client encountered "
+ "a serious internal problem while trying to communicate with OSS, "
+ "such as not being able to access the network.");
System.out.println("Error Message:" + ce.getMessage());
} finally {
if (ossClient != null) {
ossClient.shutdown();
}
}
}
}
<?php
if (is_file(__DIR__ . '/../autoload.php')) {
require_once __DIR__ . '/../autoload.php';
}
if (is_file(__DIR__ . '/../vendor/autoload.php')) {
require_once __DIR__ . '/../vendor/autoload.php';
}
use OSS\OssClient;
use OSS\Core\OssException;
use OSS\Model\RefererConfig;
// 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录 https://ram.console.aliyun.com 创建RAM用户。
$accessKeyId = "<yourAccessKeyId>";
$accessKeySecret = "<yourAccessKeySecret>";
// Endpoint以杭州为例,其它Region请按实际情况填写。
$endpoint = "http://oss-cn-hangzhou.aliyuncs.com";
$bucket= "<yourBucketName>";
$refererConfig = new RefererConfig();
// 设置允许空Referer。
$refererConfig->setAllowEmptyReferer(true);
// 添加Referer白名单。Referer参数支持通配符星号(*)和问号(?)。
$refererConfig->addReferer("example.com");
$refererConfig->addReferer("example.net");
try{
$ossClient = new OssClient($accessKeyId, $accessKeySecret, $endpoint);
$ossClient->putBucketReferer($bucket, $refererConfig);
} catch(OssException $e) {
printf(__FUNCTION__ . ": FAILED\n");
printf($e->getMessage() . "\n");
return;
}
print(__FUNCTION__ . ": OK" . "\n");
const OSS = require('ali-oss')
const client = new OSS({
// yourregion填写Bucket所在地域。以华东1(杭州)为例,Region填写为oss-cn-hangzhou。
region: 'yourregion',
// 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。
accessKeyId: 'yourAccessKeyId',
accessKeySecret: 'yourAccessKeySecret',
// yourbucketname填写存储空间名称。
bucket: 'yourbucketname'
});
async function putBucketReferer () {
try {
const result = await client.putBucketReferer('bucket-name', true, [
'example.com',
'*.example.com'
]);
console.log(result);
} catch (e) {
console.log(e);
}
}
putBucketReferer();
# -*- coding: utf-8 -*-
import oss2
from oss2.models import BucketReferer
# 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。
auth = oss2.Auth('yourAccessKeyId', 'yourAccessKeySecret')
# 填写Bucket所在地域对应的Endpoint。以华东1(杭州)为例,Endpoint填写为https://oss-cn-hangzhou.aliyuncs.com。
# yourBucketName填写存储空间名称。
bucket = oss2.Bucket(auth, 'https://oss-cn-hangzhou.aliyuncs.com', 'yourBucketName')
# 设置允许空Referer(True表示允许空Referer,False表示不允许空Referer),并设置Referer白名单。
bucket.put_bucket_referer(BucketReferer(True, ['http://aliyun.com', 'http://*.aliyuncs.com']))
using Aliyun.OSS;
using Aliyun.OSS.Common;
// yourEndpoint填写Bucket所在地域对应的Endpoint。以华东1(杭州)为例,Endpoint填写为https://oss-cn-hangzhou.aliyuncs.com。
var endpoint = "yourEndpoint";
// 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。
var accessKeyId = "yourAccessKeyId";
var accessKeySecret = "yourAccessKeySecret";
// 填写Bucket名称。
var bucketName = "examplebucket";
// 创建OSSClient实例。
var client = new OssClient(endpoint, accessKeyId, accessKeySecret);
try
{
var refererList = new List<string>();
// 添加Referer白名单。Referer参数支持通配符星号(*)和问号(?)。
refererList.Add(" http://www.aliyun.com");
refererList.Add(" http://www.*.com");
refererList.Add(" http://www.?.aliyuncs.com");
var srq = new SetBucketRefererRequest(bucketName, refererList);
// 设置防盗链。
client.SetBucketReferer(srq);
Console.WriteLine("Set bucket:{0} Referer succeeded ", bucketName);
}
catch (OssException ex)
{
Console.WriteLine("Failed with error code: {0}; Error info: {1}. \nRequestID:{2}\tHostID:{3}",
ex.ErrorCode, ex.Message, ex.RequestId, ex.HostId);
}
catch (Exception ex)
{
Console.WriteLine("Failed with error info: {0}", ex.Message);
}
PutBucketRefererRequest request = new PutBucketRefererRequest();
request.setBucketName("yourBucketName");
// 添加Referer白名单。Referer参数支持通配符星号(*)和问号(?)。
ArrayList<String> referers = new ArrayList<String>();
referers.add("http://example.com");
referers.add("http://www.*.com");
referers.add("http://www.?.com");
request.setReferers(referers);
OSSAsyncTask task = oss.asyncPutBucketReferer(request, new OSSCompletedCallback<PutBucketRefererRequest, PutBucketRefererResult>() {
@Override
public void onSuccess(PutBucketRefererRequest request, PutBucketRefererResult result) {
OSSLog.logInfo("code: " + result.getStatusCode());
}
@Override
public void onFailure(PutBucketRefererRequest request, ClientException clientException, ServiceException serviceException) {
OSSLog.logError("error: "+serviceException.getRawMessage());
}
});
task.waitUntilFinished();
package main
import (
"fmt"
"os"
"github.com/aliyun/aliyun-oss-go-sdk/oss"
)
func main() {
// 创建OSSClient实例。
// yourEndpoint填写Bucket对应的Endpoint,以华东1(杭州)为例,填写为https://oss-cn-hangzhou.aliyuncs.com。其它Region请按实际情况填写。
// 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。
client, err := oss.New("yourEndpoint", "yourAccessKeyId", "yourAccessKeySecret")
if err!=nil{
fmt.Println("Error:", err)
os.Exit(-1)
}
// yourBucketName填写Bucket名称。
bucketName := "yourBucketName"
// 添加Referer白名单,且不允许空Referer。Referer参数支持通配符星号(*)和问号(?)。
referers := []string{"http://www.aliyun.com",
"http://www.???.aliyuncs.com",
"http://www.*.com"}
err = client.SetBucketReferer(bucketName, referers, false)
if err!=nil{
fmt.Println("Error:", err)
os.Exit(-1)
}
}
#include <alibabacloud/oss/OssClient.h>
using namespace AlibabaCloud::OSS;
int main(void)
{
/* 初始化OSS账号信息。*/
/* 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。*/
std::string AccessKeyId = "yourAccessKeyId";
std::string AccessKeySecret = "yourAccessKeySecret";
/* yourEndpoint填写Bucket所在地域对应的Endpoint。以华东1(杭州)为例,Endpoint填写为https://oss-cn-hangzhou.aliyuncs.com。*/
std::string Endpoint = "yourEndpoint";
/* 填写Bucket名称,例如examplebucket。*/
std::string BucketName = "examplebucket";
/* 初始化网络等资源。*/
InitializeSdk();
ClientConfiguration conf;
OssClient client(Endpoint, AccessKeyId, AccessKeySecret, conf) ;
/* 设置防盗链。*/
SetBucketRefererRequest request(BucketName);
request.addReferer("http://example.com");
request.addReferer("https://example.com");
request.addReferer("https://www.?.example.com");
request.addReferer("https://www.*.cn");
request.setAllowEmptyReferer(false);
auto outcome = client.SetBucketReferer(request);
if (!outcome.isSuccess()) {
/* 异常处理。*/
std::cout << "SetBucketReferer fail" <<
",code:" << outcome.error().Code() <<
",message:" << outcome.error().Message() <<
",requestId:" << outcome.error().RequestId() << std::endl;
return -1;
}
/* 释放网络等资源。*/
ShutdownSdk();
return 0;
}
#include "oss_api.h"
#include "aos_http_io.h"
/* yourEndpoint填写Bucket所在地域对应的Endpoint。以华东1(杭州)为例,Endpoint填写为https://oss-cn-hangzhou.aliyuncs.com。*/
const char *endpoint = "yourEndpoint";
/* 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。*/
const char *access_key_id = "yourAccessKeyId";
const char *access_key_secret = "yourAccessKeySecret";
/* 填写Bucket名称,例如examplebucket。*/
const char *bucket_name = "examplebucket";
void init_options(oss_request_options_t *options)
{
options->config = oss_config_create(options->pool);
/* 用char*类型的字符串初始化aos_string_t类型。*/
aos_str_set(&options->config->endpoint, endpoint);
aos_str_set(&options->config->access_key_id, access_key_id);
aos_str_set(&options->config->access_key_secret, access_key_secret);
/* 是否使用了CNAME。0表示不使用。*/
options->config->is_cname = 0;
/* 设置网络相关参数,比如超时时间等。*/
options->ctl = aos_http_controller_create(options->pool, 0);
}
int main(int argc, char *argv[])
{
/* 在程序入口调用aos_http_io_initialize方法来初始化网络、内存等全局资源。*/
if (aos_http_io_initialize(NULL, 0) != AOSE_OK) {
exit(1);
}
/* 用于内存管理的内存池(pool),等价于apr_pool_t。其实现代码在apr库中。*/
aos_pool_t *pool;
/* 重新创建一个内存池,第二个参数是NULL,表示没有继承其它内存池。*/
aos_pool_create(&pool, NULL);
/* 创建并初始化options,该参数包括endpoint、access_key_id、acces_key_secret、is_cname、curl等全局配置信息。*/
oss_request_options_t *oss_client_options;
/* 在内存池中分配内存给options。*/
oss_client_options = oss_request_options_create(pool);
/* 初始化Client的选项oss_client_options。*/
init_options(oss_client_options);
/* 初始化参数。*/
aos_string_t bucket;
aos_table_t *resp_headers = NULL;
aos_status_t *resp_status = NULL;
oss_referer_config_t referer_config;
aos_str_set(&bucket, bucket_name);
aos_list_init(&referer_config.referer_list);
oss_create_and_add_refer(pool, &referer_config, "http://www.aliyun.com");
oss_create_and_add_refer(pool, &referer_config, "https://www.aliyun.com");
referer_config.allow_empty_referer = 0;
/* 添加Referer白名单。Referer参数支持通配符星号(*)和问号(?)。*/
resp_status = oss_put_bucket_referer(oss_client_options, &bucket, &referer_config, &resp_headers);
if (aos_status_is_ok(resp_status)) {
printf("put bucket referer succeeded\n");
} else {
printf("put bucket referer failed\n");
}
/* 释放内存池,相当于释放了请求过程中各资源分配的内存。*/
aos_pool_destroy(pool);
/* 释放之前分配的全局资源。*/
aos_http_io_deinitialize();
return 0;
}
require 'aliyun/oss'
client = Aliyun::OSS::Client.new(
# Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。
endpoint: 'https://oss-cn-hangzhou.aliyuncs.com',
# 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。
access_key_id: 'AccessKeyId', access_key_secret: 'AccessKeySecret')
# 填写Bucket名称,例如examplebucket。
bucket = client.get_bucket('examplebucket')
# 设置存储空间Referer列表。
bucket.referer = Aliyun::OSS::BucketReferer.new(
allow_empty: true, whitelist: ['example.com', '*.example.com'])
使用命令行工具ossutil
关于使用ossutil设置防盗链的具体操作, 请参见添加或修改防盗链配置。
使用REST API
如果您的程序自定义要求较高,您可以直接发起REST API请求。直接发起REST API请求需要手动编写代码计算签名。更多信息,请参见PutBucketReferer。
更多参考
- 关于使用防盗链过程中遇到的常见问题,请参见OSS防盗链(Referer)配置及错误排除。