title: “down-csrf-token改造记录”
date: 2024-03-26
hiddenFromHomePage: true
hiddenFromSearch: true
categories:
- 网站开发
tags:
- token
- csrf
keywords:
- token
- csrf
url: “/goals/web-add-csrf-token”
前端代码:
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
| 【<head>
<script>
async function getCsrfToken() {
const response = await fetch('/get_csrf_token');
if (!response.ok) {
throw new Error('Failed to fetch CSRF token');
}
const { csrf_token } = await response.json();
return csrf_token;
}
window.addEventListener('DOMContentLoaded', async () => {
const csrfToken = await getCsrfToken();
document.querySelector('input[name="my_csrf_token"]').value = csrfToken;
});
</script>
.....
</head>
.....
<div id="container" class="mdui-card mdui-center">
<form id="form_url" >
<label for="video_link"> </label>
<input type="text" id="video_link" name="video_link" onfocus="this.value=''" value="请输入视频页面url 比如: https://v.test.com/UGQaf5o/" style="color: gray;">
<input type="hidden" name="my_csrf_token" value="">
<button id="download_btn" type="submit">Download</button>
<p id="error_message" style="display:none">Uh-Oh! 可能是没有,请更换url</p>
</form>
</div>
.....
<script>
document.getElementById("download_btn").addEventListener("click", async function() {
event.preventDefault(); // 阻止表单默认提交行为
document.getElementById("download_btn").innerText = "Downloading...";
document.getElementById("download_btn").setAttribute("disabled", "disabled");
const v = document.getElementById('video_link').value;
let regex;
if (v.includes("test")) {
regex = /http[s]?:\/\/[\w.]+[\w\/]*[\w.]*\??[\w=&:\-\+\%]*[/]*/;
} else if (v.includes("test")) {
regex = /http[s]?:\/\/[\w.]+\/@[\w.-]+\/video\/\d+/;
}
const video_link = v.match(regex)[0];
const csrftoken = document.querySelector('input[name="my_csrf_token"]').value;
const formData = new FormData();
formData.append('video_link', video_link);
formData.append('my_csrf_token', csrftoken); // 将csrf_token作为表单数据发送
try {
const response = await fetch('/download', {
method: 'POST',
body: formData
});
if (!response.ok) {
document.getElementById("error_message").style.display = "block";
download_btn.innerText = "Download";
download_btn.removeAttribute("disabled");
return;
}
const jsonData = await response.json();
// Remove form_url
document.getElementById("form_url").style.display = "none";
// create and add the thumbnail image
const thumb = document.createElement("img");
const imageDiv = document.createElement("div");
thumb.src = jsonData.thumb;
thumb.classList.add("thumb-img");
imageDiv.classList.add("mdui-card-media");
imageDiv.appendChild(thumb);
.....
</script>】
|
后端代码:
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
| 【
from flask import Flask, request, jsonify, render_template_string
import requests
import json
import qrcode
from fake_useragent import UserAgent
from subprocess import run, PIPE
from flask_wtf.csrf import CSRFProtect, generate_csrf, CSRFError
from werkzeug.exceptions import BadRequest
app = Flask(__name__)
app.config['SECRET_KEY'] = '3D6SWx9deteds1' #随机密钥
csrf = CSRFProtect(app) # 初始化CsrfProtect
@app.errorhandler(CSRFError)
def handle_csrf_error(e):
return jsonify({"error": e.description}), 400
@app.route('/get_csrf_token', methods=['GET'])
def get_csrf_token():
return jsonify({'csrf_token': generate_csrf()})
#################################download start #################################
# 移除豁免装饰器 @csrf.exempt #如果不删除这个 400报错 Missing CSRF token
@app.route("/download", methods=["POST"])
def download():
csrf_token = request.form.get('my_csrf_token') # 假设CSRF令牌通过表单字段发送
if not csrf_token:
return jsonify({"error": "Missing CSRF token"}), 400
video_link = request.form.get("video_link") # 从表单数据中获取video_link
if not video_link:
return jsonify({"error": "Video link is required"}), 400
.....
】
|
csrf版本:
/home/wwwroot/down/douyin/en/indexnot-csrf.html
/usr/src/TwitterMedia/x103_youtube_dy_tiktok-backend-csrf.py
https://v.2urs.com/douyin/en/index-csrf.html ok
非csrf版本:
/home/wwwroot/down/douyin/en/index.html
/usr/src/TwitterMedia/x103_youtube_dy_tiktok-backend.py
https://v.2urs.com/douyin/en/index.html ok
csrf-token 版本:
/home/wwwroot/down/douyin/en/index-csrf-token.html
/usr/src/x103Media/twitter_youtube_dy_tiktok-backend-csrf-token.py
https://v.2urs.com/douyin/en/index-csrf-token.html?token=uuic-qackd-fga-tdfd ok
https://analyse.layzz.cn/lyz/getAnalyse?token=uuic-qackd-fga-test
https://v.2urs.com/download
https://v.douyin.com/iY4jtHC3/
怎样在下面给你的前后端代码的基础上 实现 https://analyse.layzz.cn/lyz/getAnalyse?token=uuic-qackd-fga-test 这个的功能呢。
比如 https://v.test.com/download?token=uuic-qackd-fga-test
如果请求后 次数不足返回 {
“code”: “0002”,
“method”: null,
“message”: “您的次数不足 请联系作者微信 123456”
}
假如请求成功。 返回之前的json 。可以使用的总次数减1.
你可以假定 uuic-qackd-fga-test 的信息暂时保存在txt文件中或者字典里面 。格式为:
假如存入字典 1 100 1 ] 1级【token种类 1为测试 2为正式】 100【为使用的次数】 1【1为可用 0为不可用】
前端代码:
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
| 【<head>
<script>
async function getCsrfToken() {
const response = await fetch('/get_csrf_token');
if (!response.ok) {
throw new Error('Failed to fetch CSRF token');
}
const { csrf_token } = await response.json();
return csrf_token;
}
window.addEventListener('DOMContentLoaded', async () => {
const csrfToken = await getCsrfToken();
document.querySelector('input[name="my_csrf_token"]').value = csrfToken;
});
</script>
.....
</head>
.....
<div id="container" class="mdui-card mdui-center">
<form id="form_url" >
<label for="video_link"> </label>
<input type="text" id="video_link" name="video_link" onfocus="this.value=''" value="请输入视频页面url 比如: https://v.test.com/UGQaf5o/" style="color: gray;">
<input type="hidden" name="my_csrf_token" value="">
<button id="download_btn" type="submit">Download</button>
<p id="error_message" style="display:none">Uh-Oh! 可能是没有,请更换url</p>
</form>
</div>
.....
<script>
document.getElementById("download_btn").addEventListener("click", async function() {
event.preventDefault(); // 阻止表单默认提交行为
document.getElementById("download_btn").innerText = "Downloading...";
document.getElementById("download_btn").setAttribute("disabled", "disabled");
const v = document.getElementById('video_link').value;
let regex;
if (v.includes("test")) {
regex = /http[s]?:\/\/[\w.]+[\w\/]*[\w.]*\??[\w=&:\-\+\%]*[/]*/;
} else if (v.includes("test")) {
regex = /http[s]?:\/\/[\w.]+\/@[\w.-]+\/video\/\d+/;
}
const video_link = v.match(regex)[0];
const csrftoken = document.querySelector('input[name="my_csrf_token"]').value;
const formData = new FormData();
formData.append('video_link', video_link);
formData.append('my_csrf_token', csrftoken); // 将csrf_token作为表单数据发送
try {
const response = await fetch('/download', {
method: 'POST',
body: formData
});
if (!response.ok) {
document.getElementById("error_message").style.display = "block";
download_btn.innerText = "Download";
download_btn.removeAttribute("disabled");
return;
}
const jsonData = await response.json();
// Remove form_url
document.getElementById("form_url").style.display = "none";
// create and add the thumbnail image
const thumb = document.createElement("img");
const imageDiv = document.createElement("div");
thumb.src = jsonData.thumb;
thumb.classList.add("thumb-img");
imageDiv.classList.add("mdui-card-media");
imageDiv.appendChild(thumb);
.....
</script>】
|
后端代码:
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
| from flask import Flask, request, jsonify, render_template_string
import requests
import json
import qrcode
from fake_useragent import UserAgent
from subprocess import run, PIPE
from flask_wtf.csrf import CSRFProtect, generate_csrf, CSRFError
from werkzeug.exceptions import BadRequest
app = Flask(__name__)
app.config['SECRET_KEY'] = '3D6SWx9deteds1' #随机密钥
csrf = CSRFProtect(app) # 初始化CsrfProtect
@app.errorhandler(CSRFError)
def handle_csrf_error(e):
return jsonify({"error": e.description}), 400
@app.route('/get_csrf_token', methods=['GET'])
def get_csrf_token():
return jsonify({'csrf_token': generate_csrf()})
#################################download start #################################
# 移除豁免装饰器 @csrf.exempt #如果不删除这个 400报错 Missing CSRF token
@app.route("/download", methods=["POST"])
def download():
csrf_token = request.form.get('my_csrf_token') # 假设CSRF令牌通过表单字段发送
if not csrf_token:
return jsonify({"error": "Missing CSRF token"}), 400
video_link = request.form.get("video_link") # 从表单数据中获取video_link
if not video_link:
return jsonify({"error": "Video link is required"}), 400
if "x103" in video_link:
return parse_x103_video(video_link)
def parse_x103_video(video_link):
# 使用正则表达式或其他方式解析视频地址
video_link = request.json["video_link"]
url_v2 = "http://127.0.0.1:8010/api/process_v2"
params = {'video_link': video_link}
response = requests.get(url_v2, params=params)
if response.status_code == 200:
return response.json()
else:
url_v1 = "http://127.0.0.1:8010/api/process_v1"
response = requests.get(url_v1, params=params)
if response.status_code == 200:
return response.json()
else:
return "Error occurred"
.....
】
|
请求 en/index-csrf-token.html?token=uuic-qackd-fga-test 还是报错。控制台信息
Token from URL: uuic-qackd-fga-test
index-csrf-token.html?token=uuic-qackd-fga-test:297
POST https://v.test.com/download?token=${token} 400 (Bad Request)
(匿名) @ index-csrf-token.html?token=uuic-qackd-fga-test:297 就这行 const response = await fetch(’/download?token=${token}’, {
https://v.2urs.com/douyin/en/index-csrf-token.html?token=uuic-qackd-fga-tdfd
ok
使用程序 获取csrf cookie 。
然后构造请求 把csrf cookie 带入 请求。
ok
构造1个bash curl请求 包括 user-agent video_link origin referer
bash
1
2
3
4
| curl 'https://v.2urs.com/download' -H --user-agent "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36" -H 'origin: https://v.2urs.com' -H "Referer: https://v.2urs.com/twitter/" -X GET -H "Content-Type: application/json" -d '{"video_link": "https://twitter.com/i/status/1580927207987257344"}'
curl 'https://v.2urs.com/download' -H --user-agent "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36" -H 'origin: https://v.2urs.com' -H "Referer: https://v.2urs.com/douyin/en/" -X GET -H "Content-Type: application/json" -d '{"video_link": "https://v.douyin.com/iY4jtHC3"}'
|
---
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| cmd
curl "https://v.2urs.com/download" ^
-H "accept: */*" ^
-H "accept-language: zh-CN,zh;q=0.9,en;q=0.8,en-US;q=0.7" ^
-H "content-type: application/json" ^
-H "cookie: _ga=GA1.2.204225576.1709769565; Hm_lvt_b3aa9ed2616f88781f9792c2a0cafa9c=1709515900,1709531436,1709775119,1710211332; _gid=GA1.2.803156679.1713323002; _ga_FYYVZP7K0Z=GS1.2.1713340526.35.0.1713340526.60.0.0" ^
-H "origin: https://v.2urs.com" ^
-H "referer: https://v.2urs.com/douyin/en/" ^
-H ^"sec-ch-ua: ^\^"Microsoft Edge^\^";v=^\^"123^\^", ^\^"Not:A-Brand^\^";v=^\^"8^\^", ^\^"Chromium^\^";v=^\^"123^\^"^" ^
-H "sec-ch-ua-mobile: ?0" ^
-H ^"sec-ch-ua-platform: ^\^"Windows^\^"^" ^
-H "sec-fetch-dest: empty" ^
-H "sec-fetch-mode: cors" ^
-H "sec-fetch-site: same-origin" ^
-H "user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0" ^
-H ^"x-csrftoken: ^{^{ csrf_token ^}^}^" ^
--data-raw ^"^{^\^"video_link^\^":^\^"https://v.douyin.com/iY4jtHC3/^\^"^}^"
|
浏览器 cmd 请求
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
| 【
curl "https://v.2urs.com/download" ^
-H "accept: */*" ^
-H "accept-language: zh-CN,zh;q=0.9,en;q=0.8,en-US;q=0.7" ^
-H "content-type: multipart/form-data; boundary=----WebKitFormBoundaryHTC3EJwKAxoAA7BI" ^
-H "cookie: _ga=GA1.2.204225576.1709769565; Hm_lvt_b3aa9ed2616f88781f9792c2a0cafa9c=1709515900,1709531436,1709775119,1710211332; _gid=GA1.2.803156679.1713323002; _ga_FYYVZP7K0Z=GS1.2.1713340526.35.0.1713340526.60.0.0; session=eyJjc3JmX3Rva2VuIjoiNmExYjU0YTkwZDBkOWE2NzUzMTA4MGRjN2FhMmIwZmIyZjZhYmVmMiJ9.Zh-SlQ.P83h1U1GQsPsSo_OolWs_SiDSCs" ^
-H "origin: https://v.2urs.com" ^
-H "referer: https://v.2urs.com/douyin/en/index-csrf.html" ^
-H ^"sec-ch-ua: ^\^"Microsoft Edge^\^";v=^\^"123^\^", ^\^"Not:A-Brand^\^";v=^\^"8^\^", ^\^"Chromium^\^";v=^\^"123^\^"^" ^
-H "sec-ch-ua-mobile: ?0" ^
-H ^"sec-ch-ua-platform: ^\^"Windows^\^"^" ^
-H "sec-fetch-dest: empty" ^
-H "sec-fetch-mode: cors" ^
-H "sec-fetch-site: same-origin" ^
-H "user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0" ^
--data-raw ^"------WebKitFormBoundaryHTC3EJwKAxoAA7BI^
Content-Disposition: form-data; name=^\^"video_link^\^"^
^
https://v.douyin.com/iLcmh9A5/^
------WebKitFormBoundaryHTC3EJwKAxoAA7BI^
Content-Disposition: form-data; name=^\^"my_csrf_token^\^"^
^
IjZhMWI1NGE5MGQwZDlhNjc1MzEwODBkYzdhYTJiMGZiMmY2YWJlZjIi.Zh-SlQ.VQinlJ7f227MO8AAE6ZEuYDaK4Q^
------WebKitFormBoundaryHTC3EJwKAxoAA7BI--^
^"
】
|
把上面从浏览器F12 copy的cmd curl命令 运行有 成功返回
curl “https://v.2urs.com/get_csrf_token" ^
得到
“ImM1ZDQ2MWNkNjZiODcxNDA2NmI5M2FkMTA0YTQ5NDI1ZmRlNDIxNTAi.Zh-TUw.VSCvekkR7bAODIsOa0P0N_-7cN4”
把这个值替换上面的cmd curl 。为什么不可以了
报错:
{
“error”: “The CSRF tokens do not match.”
}
把cookie 去掉 然后报错
{
“error”: “The CSRF session token is missing.”
}
为什么?
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
| 【
curl "https://v.2urs.com/download" ^
-H "accept: */*" ^
-H "accept-language: zh-CN,zh;q=0.9,en;q=0.8,en-US;q=0.7" ^
-H "content-type: multipart/form-data; boundary=----WebKitFormBoundaryHTC3EJwKAxoAA7BI" ^
-H "cookie: _ga=GA1.2.204225576.1709769565; Hm_lvt_b3aa9ed2616f88781f9792c2a0cafa9c=1709515900,1709531436,1709775119,1710211332; _gid=GA1.2.803156679.1713323002; _ga_FYYVZP7K0Z=GS1.2.1713340526.35.0.1713340526.60.0.0; session=eyJjc3JmX3Rva2VuIjoiNDNhZDZiYjExMzg4YTRlZTU2ZmY5MGRjNzJhMDgwZTRjYTVhYmU1OCJ9.Zh-WZQ.mNwH8GiyZmZQcQx7T40l-Qb0XeI" ^
-H "origin: https://v.2urs.com" ^
-H "referer: https://v.2urs.com/douyin/en/index-csrf.html" ^
-H ^"sec-ch-ua: ^\^"Microsoft Edge^\^";v=^\^"123^\^", ^\^"Not:A-Brand^\^";v=^\^"8^\^", ^\^"Chromium^\^";v=^\^"123^\^"^" ^
-H "sec-ch-ua-mobile: ?0" ^
-H ^"sec-ch-ua-platform: ^\^"Windows^\^"^" ^
-H "sec-fetch-dest: empty" ^
-H "sec-fetch-mode: cors" ^
-H "sec-fetch-site: same-origin" ^
-H "user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0" ^
--data-raw ^"------WebKitFormBoundaryHTC3EJwKAxoAA7BI^
Content-Disposition: form-data; name=^\^"video_link^\^"^
^
https://v.douyin.com/iLcmh9A5/^
------WebKitFormBoundaryHTC3EJwKAxoAA7BI^
Content-Disposition: form-data; name=^\^"my_csrf_token^\^"^
^
IjQzYWQ2YmIxMTM4OGE0ZWU1NmZmOTBkYzcyYTA4MGU0Y2E1YWJlNTgi.Zh-WZQ.3s872SAbVX7LjTva4CvAC0Wf6RQ^
------WebKitFormBoundaryHTC3EJwKAxoAA7BI--^
^"
】
|
你的问题涉及到CSRF(跨站请求伪造)令牌的更换。
在浏览器环境中,每次当用户界面完成交互后,例如一个HTTP请求,此时CSRF令牌可能被话务器刷新,于是下一次请求需要使用新的令牌。新令牌通常被存储在cookie中,然后该令牌将被用于验证后续的请求。
然而,在你使用curl的情况下,你手动获取了一个新的CSRF令牌,“ImM1ZDQ2MWNkNjZiODcxNDA2NmI5M2FkMTA0YTQ5NDI1ZmRlNDIxNTAi.Zh-TUw.VSCvekkR7bAODIsOa0P0N_-7cN4”,并将其放在你下载请求的表单中。但在从服务器返回新的CSRF令牌时,服务器通常也会在Set-Cookie响应头中设置一个新的会话cookie。这个新的会话cookie应该伴随新的CSRF令牌一起在后续请求中使用。
当缺少这个新的会话cookie,或者其中的CSRF会话令牌与提交的CSRF令牌不匹配时,服务器将拒绝该请求,并且你会收到错误提示"The CSRF tokens do not match.” 或者 “The CSRF session token is missing.”
通常,浏览器会自动处理新的session cookie,但在使用curl时,你需要手动处理。在你获取新的CSRF令牌的响应里,查看Set-Cookie响应头,并将服务器设置的新的会话cookie加入到你的下载请求的cookie头部中。
这样,cookies.txt文件里就保存了从服务器返回的所有cookies,包括session cookie。
之后,你可以使用-b或–cookie选项来在后续的请求中发送这些cookie。以下是包含cookie的请求示例:
把替换 cookie session的值用cookies.txt 相关的cookie值替换
或者
1
| curl -b cookies.txt "https://v.test.com/download" -H ...
|
按照你这种方式https://v.test.com/get_csrf_token得到csrf 和cookie 。然后curl请求https://v.test.com/download 带 新cookeie和csrf和video_link 就可以成功有下载数据返回了。。怎么防止这种方式来破解csrf呢,和写个程序就可以调用我网站进行解析接口下载了?
这其实并不是一种"破解"方式,因为这种进行的是合法的操作流程:获取CSRF令牌,然后发送数据。这和手动在网站上点击并发送数据的道理是相同的。网站的CSRF防护主要是用来防止第三方网站伪造请求,不是用来防止合法用户的请求。当前你的网站已经对CSRF进行了防护,因而理论上是安全的。
不过,有一个要注意的安全隐患:任何人都能获取CSRF令牌并发送请求。如果你的网站有些敏感操作,你可能需要对其进行额外的保护,简单的CSRF防护可能是不够的。这种情况下,你可能需要添加一些额外的措施,比如:
验证码:在敏感操作需要用户输入验证信息,例如短信验证码或邮箱链接等。
第二步验证:例如使用短信、邮件或者安全问题要求用户再次确认操作。
操作频率限制:比如同一用户(基于IP或者账号判断)一段时间内只允许进行有限次数的操作,可以有效防止滥用。
登录校验:未注册/登录用户无法进行操作。
综上,仅仅依赖CSRF防护可能是不够的,对于安全性要求高的操作,建议加入额外的安全措施。
如何添加验证码来增强CSRF保护?
什么是第二步验证?如何在网站上实现它?
如何设置操作频率限制以防止滥用?