近期,我的Cloudflare账号因为做Onedrive的反向代理加速下载而被暂停,被暂停的理由是使用cf进行钓鱼网络欺诈。我花了几天,在不同的论坛上询问了许多的网友,才终于明白了我的账户到底发生了什么。

开端

自从2022年年初以来,我便搭建了Onemanager的网盘,并挂上了我的Micrsoft E5号,方便我在学校的时候能够通过学校电脑访问到我的文件。

但是众所周知,Onedrive在国内的下载速度一直非常一般。为了提速,我在网上找来了通用的Cloudflare Worker代码,为我的Sharepoint(Onedrive)设置了反向代理,通过cf来加速在中国大陆的下载速度。

2022年11月15日,我用来做反向代理的Cloudflare Worker收到了1.3k的请求,我本来以为这并不会发生什么,毕竟他的免费用量有100k。
zRcS3R.md.jpg

但是随即,我便收到了一封来自Netcraft的举报邮件,内容大意是说我的网站涉及钓鱼欺诈,Cloudflare也随即将我的账号暂停(禁止添加新域名,现有域名不影响)。
zRc9jx.md.jpgzRcpg1.md.jpg

那时因为是在工作日,而我被封控在学校,用手机的时间较少,所以并没有特别在意。

争论

到了12月,我的联考结束了,我也有时间去处理这件事情。我向客服申诉了,我用Cloudflare是用来加速中国大陆的下载速度,主观上并没有欺骗任何人,拒绝承认我违反了他们的服务条款(事实上服务条款也没有禁止用作反代,只是禁止用于欺诈)。然而客服还是认定我涉及了网络欺诈钓鱼,违反了他们的条款,拒绝给我解封。
zWwFXD.md.jpg
zfVJ5n.md.jpg

对于一个没有付费的用户,我也不奢求他们会给我什么,也不指望他们能用领着工资的客服为我服务,只能自认倒霉。

总结

在本次事件发生之后,我询问了各种论坛上的网友。虽然遇到过这样情况的人不多,但是还是有几位热心网友回复了我的疑问。

首先就是欺诈的判定理由,根据网友们的分析和推测,认为是因为我的网站和原本的官网长得非常的相似导致被判定为仿冒官网。然而对于任何一个反代站点来说,所有的内容都是在源网站上获取,并不会改动任何的内容。因此和源网站相似,这一点是不可避免的。

那是否意味着,任何的反向代理都不能用Cloudflare托管了呢?倒也未必。因为举报我欺诈的公司并不位于中国,而我的加速的目标仅仅只是中国的下载速度。因此我们可以通过一个非常简单的方法:限制地区访问来解决这个问题。我们可以进入到我们自己域名的管理页面,找到 安全性选项,点击 WAF,便可以限制特定地区或者IP等的访问,不符合的地区可以全部封禁。这样即使我的使用量比较多,但因为他们的公司不在中国(应该总部在英国),他们也发现不了我的证据,从取证途径上避免了被举报。

再者,如果这个反代站点只是我们自己个人使用,我们可以添加Basic Auth来阻止其他人访问,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
// 替换成你想镜像的站点
const upstream = 'google.com'

// 如果那个站点有专门的移动适配站点,否则保持和上面一致
const upstream_mobile = 'm.google.com'

// 密码访问

const openAuth = false
const username = 'username'
const password = 'password'

// 你希望禁止哪些国家访问
const blocked_region = ['RU']

// 禁止自访问
const blocked_ip_address = ['0.0.0.0', '127.0.0.1']

// 替换成你想镜像的站点
const replace_dict = {
'$upstream': '$custom_domain',
'//google.com': ''
}

function unauthorized() {
return new Response('Unauthorized', {
headers: {
'WWW-Authenticate': 'Basic realm="goindex"',
'Access-Control-Allow-Origin': '*'
},
status: 401
});
}

function parseBasicAuth(auth) {
try {
return atob(auth.split(' ').pop()).split(':');
} catch (e) {
return [];
}
}

function doBasicAuth(request) {
const auth = request.headers.get('Authorization');

if (!auth || !/^Basic [A-Za-z0-9._~+/-]+=*$/i.test(auth)) {
return false;
}

const [user, pass] = parseBasicAuth(auth);
return user === username && pass === password;
}


async function fetchAndApply(request) {
if (request.method === 'OPTIONS') // allow preflight request
return new Response('', {
status: 200,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, HEAD, OPTIONS'
}
});

if (openAuth && !doBasicAuth(request)) {
return unauthorized();
}
const region = request.headers.get('cf-ipcountry').toUpperCase();
const ip_address = request.headers.get('cf-connecting-ip');
const user_agent = request.headers.get('user-agent');

let response = null;
let url = new URL(request.url);
let url_host = url.host;

if (url.protocol == 'http:') {
url.protocol = 'https:'
response = Response.redirect(url.href);
return response;
}

if (await device_status(user_agent)) {
upstream_domain = upstream
} else {
upstream_domain = upstream_mobile
}

url.host = upstream_domain;

if (blocked_region.includes(region)) {
response = new Response('Access denied: WorkersProxy is not available in your region yet.', {
status: 403
});
} else if(blocked_ip_address.includes(ip_address)){
response = new Response('Access denied: Your IP address is blocked by WorkersProxy.', {
status: 403
});
} else{
let method = request.method;
let request_headers = request.headers;
let new_request_headers = new Headers(request_headers);

new_request_headers.set('Host', upstream_domain);
new_request_headers.set('Referer', url.href);

let original_response = await fetch(url.href, {
method: method,
headers: new_request_headers
})

let original_response_clone = original_response.clone();
let original_text = null;
let response_headers = original_response.headers;
let new_response_headers = new Headers(response_headers);
let status = original_response.status;

new_response_headers.set('access-control-allow-origin', '*');
new_response_headers.set('access-control-allow-credentials', true);
new_response_headers.delete('content-security-policy');
new_response_headers.delete('content-security-policy-report-only');
new_response_headers.delete('clear-site-data');

const content_type = new_response_headers.get('content-type');
if (content_type.includes('text/html') && content_type.includes('UTF-8')) {
original_text = await replace_response_text(original_response_clone, upstream_domain, url_host);
} else {
original_text = original_response_clone.body
}

response = new Response(original_text, {
status,
headers: new_response_headers
})
}
return response;
}

addEventListener('fetch', event => {
event.respondWith(fetchAndApply(event.request).catch(err => {
console.error(err);
new Response(JSON.stringify(err.stack), {
status: 500,
headers: {
'Content-Type': 'application/json'
}
});
}));
})


async function replace_response_text(response, upstream_domain, host_name) {
let text = await response.text()

var i, j;
for (i in replace_dict) {
j = replace_dict[i]
if (i == '$upstream') {
i = upstream_domain
} else if (i == '$custom_domain') {
i = host_name
}

if (j == '$upstream') {
j = upstream_domain
} else if (j == '$custom_domain') {
j = host_name
}

let re = new RegExp(i, 'g')
text = text.replace(re, j);
}
return text;
}

async function device_status (user_agent_info) {
var agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"];
var flag = true;
for (var v = 0; v < agents.length; v++) { if (user_agent_info.indexOf(agents[v]) > 0) {
flag = false;
break;
}
}
return flag;
}

希望我的经历能给大家提个醒,意识到反向代理并不是能随便使用,希望大家不要再因此而造成cf账户使用限制。

更多

有群友报告,一个WordPress生成的Procces.zip也于近期遭到了Netcraft的举报,并且此用户在WAF设置了海外IP需要检验浏览器确保是真人访问的选项,目前被举报原因尚不明确。