如何在 Flask 中提供临时下载 url?

How to provide temporary download url in Flask?

目前,我的 index.html 文件包含

<a href="static/file.ext">Download</a>

我想更改此设置,以便下载 url 仅在特定时间内有效。例如,我如何将其更改为

<a href="get_file?file=file.ext&token=TEMPORARYTOKEN">Download</a>

在我的 Flask 文件中,我可以

@app.route('/get_file')
def get_file():
    filename = request.args.get('file')
    token = request.args.get('token')
    if token is valid: # what can be done here
        return send_from_directory('static', filename)

如何生成和处理令牌?还是我的做法完全错误?

有几种方法可以做到这一点。

  1. 为您的令牌生成一个 UUID 并将其与所需的到期日期时间一起存储在数据库 table 中。然后,当有人使用令牌调用 URL 时,您可以根据数据库检查它的有效性和过期时间。

  2. 如果您不想使用数据库来存储令牌,您可以使用 GPG 来加密包含到期日期时间的字符串,并将生成的加密字符串用作您的令牌。这意味着您的令牌将比 UUID 长很多,但您将避免使用 db。

我建议使用 UUID 和数据库 table。

另一种方法是使用 base64 对来自数据库的主键进行编码。这通常是整个网络上的短 url-服务如何获得短 url 的。数据库确保唯一性,因为它是 base64,所以在 url 变长之前需要相当多的文件。

然后在每次请求时检查您的数据库以查看 link 是否仍然有效。如果不是,请对请求做一些有意义的事情。

也许你应该使用 hmac。

生成link

import hashlib
import hmac
import time
secret = "anything you like"  # such as generate from os.urandom(length)
def generate(filename):
    current_time = str(int(time.time()))
    token = hmac.new(secret, current_time, hashlib.sha256).hexdigest()
    return "get_file?file=%(filename)s&time=%(current_time)s&token=%(token)s" % {
        "filename": filename,
        "current_time": current_time,
        "token": token
    }

验证link

import hashlib
import hmac
import time
secret = "anything you like"  # same as in generate function
def verify(time_in_link, token_in_link):
    time_limit = 15 * 60  # maximum time in sec(such as: 15(mins) * 60 (convert them to sec)`enter code here`) that you want them to start download after the link has been generated.
    if (time.time() - int(time_in_link)) > time_limit:  #timeout, return False
        return False
    if hmac.new(secret, str(time_in_link), hashlib.sha256).hexdigest() == token_in_link:  # Check the token is available or not
        return True
    else:
        return False

最好的方法是使用 itsdangerous 包。您可以生成一个持续任意时间的 URL。此外,您可以秘密编码 URL 中的任何信息。这样做的好处是不需要将时间戳处理或存储到数据库中

生成持续 30 分钟的 URL 并在令牌中编码用户 ID

from itsdangerous import TimedJSONWebSignatureSerializer as Serializer

s = Serializer('WEBSITE_SECRET_KEY', 60*30) # 60 secs by 30 mins
token = s.dumps({'user_id': currentuser.id}).decode('utf-8') # encode user id 

使用以下生成你想要的URL

url_for('get_file', token=token)

验证 URL

@app.route('/get_file/<token>')
def get_file(token):
    s = Serializer('WEBSITE_SECRET_KEY')
    try:
        user_id = s.loads(token)['user_id']
    except:
        return None
    user = User.query.get(user_id)

    if not user:
        flash('This is an invalid or expired URL, please generate a new one!', 'warning')
        return redirect(url_for('another_route'))

    return send_from_directory('static', filename, as_attachment=True)

我在淋浴时想到的东西:

from flask import Flask
import datetime as dt
import hashlib

app = Flask(__name__)

time_lock = dt.datetime.now().strftime("%m/%d/%Y")

@app.route('/')
def home():
    security_code = hashlib.sha256(time_lock.encode())
    return f"<a href='/test/{security_code.hexdigest()}'>Click Here</a>"


@app.route('/test/<security_code>')
def test(security_code):
    master_code = hashlib.sha256(time_lock.encode())
    if security_code == master_code.digest():
        return "Access granted!"

这是在使用当前日期的哈希值作为临时 URL。显然存在安全漏洞,但它确实是一个疯狂的想法,并且可以进一步提高安全性。它与选择的答案非常相似,但不使用持久存储。这可能适用于保护链接不被添加书签?