Session Flask WTF CSRF会话令牌丢失,找不到密钥

Session Flask WTF CSRF会话令牌丢失,找不到密钥,session,flask,token,csrf,Session,Flask,Token,Csrf,我已经研究了Flask WTF应用程序中关于“CSRF会话令牌丢失”的每一篇帖子,但到目前为止,我在任何有解决方案的帖子中都找不到解决方案,或者我丢失了它而没有看到它 在本例中,我创建了一个登录页面,并且在登录表单的POST/submit上生成了错误 在浏览器开发工具中,我可以在表单数据中看到“csrf_令牌”,但在标题中没有令牌 表单数据来自 <form method="POST" action=""> {{ form.hidden_tag() }} {{ for

我已经研究了Flask WTF应用程序中关于“CSRF会话令牌丢失”的每一篇帖子,但到目前为止,我在任何有解决方案的帖子中都找不到解决方案,或者我丢失了它而没有看到它

在本例中,我创建了一个登录页面,并且在登录表单的POST/submit上生成了错误

在浏览器开发工具中,我可以在表单数据中看到“csrf_令牌”,但在标题中没有令牌

表单数据来自

 <form method="POST" action="">
    {{ form.hidden_tag() }}
    {{ form.csrf_token() }}
返回预期的机密值:

secret_key = {bytes} b'abc123ced456'
field_name = _get_config(
    token_key, 'WTF_CSRF_FIELD_NAME', 'csrf_token',
    message='A field name is required to use CSRF.'
)
返回

field_name = {str} ‘csrf_token’
而且数据似乎正常:

data = {str} 'IjZiNWY5ZDdiNTZjMTVkM2U0Mzg3MjU1NGMxYzc3Yjg1MTMzYTlhYzEi.XC447w.cmc1INq6u8qVuq0EOL9ARcPwB6k'
但由于“字段名称”不在会话中,因此失败

if field_name not in session:
    raise ValidationError('The CSRF session token is missing.')
所以问题是为什么

我还从login form方法中得到一个检查键/值的错误

@app.route("/login", methods=['GET', 'POST'])
def login():
    test = session['secret_key']
KeyError:“机密密钥”

app.secret\u密钥如何到达会话“secret\u密钥”? 这似乎没有发生

app.py login.html 请求结果 回应 请求将军 响应头 请求头 表单数据
{{form.hidden_tag()}
应该扩展为类似

<input id="csrf_token" name="csrf_token" type="hidden" value="... long string ...">

如果您没有看到,请仔细检查您如何设置应用程序的配置部分。除了
SECRET\u KEY
,您是否设置了任何
WTF\u
选项

您可能需要删除
{{form.csrf_token()}

不涉及
X-
标题。(我快速检查了我的一个应用程序,以防忘了什么。)

查看cookie的“安全”属性是否已设置。如果这是真的,并且您正在呼叫一个不安全的网站,cookie将不会被发送。我发现这是CSRF令牌丢失问题的原因。

我昨天遇到了“CSRF令牌丢失”问题,幸运的是,我找到了我的案例的原因。我已经在Gunicorn+Nginx上使用sync workers配置部署了我的Flask应用程序,这就是问题所在。Flask无法使用Gunicorn的同步工作程序,因此转到线程解决了我的问题

gunicorn--workers 1--threads 3-b 0.0.0.0:5000 wsgi:app


来自@brian的答案对我有用。问题是我在一个带有localhost设置的测试环境中,没有HTTPS服务

请参阅更多:

将以下配置设置为False可在非生产环境(测试环境)中加载会话cookie

app.config['SESSION\u COOKIE\u SECURE']=False


是-在提交表单和帖子之前,我在代码中看到了我设置的选项,其中包括WTF_CSRF_SECRET_KEY,我特别尝试过,尽管它应该使用默认的SECRET_KEY,WTF_CSRF_ENABLED为True也如前所述,我在表单数据中看到了CSRF_标记值,但不确定是否正确唯一突出的是
CSRF.init_app(app)
发生在更改配置设置之前。这似乎很危险。(我没有在我的应用程序中显式地
init_app
django_wtf.csrf
,因为它会惰性地发生。)在什么时候你会考虑上面的代码?很难找到关于何时按什么顺序做什么的明确文档/信息…我的flask应用程序中没有一个使用
flask_wtf
显式初始化csrf。我尝试将csrf.init_应用程序(应用程序)移动到我能想到的每一个顺序,结果相同。当我删除它-我现在得到一个不同的错误“{'csrf_token':['csrf tokens not match.]}”你解决了这个问题吗?我现在也遇到过。布鲁斯-不,我放弃了。使用Flask本来可以节省我的时间却浪费在这样的事情上了你在哪里/如何部署应用程序?当我在一个免费的heroku部署上部署时,我也遇到了同样的问题,但它只是偶尔发生。我有点确信,问题在于heroku在不同的服务器/dyno上启动了应用程序容器,而不是启动它的服务器/dyno。执行此操作时,CSRF令牌与会话不匹配。同样,这只是我的猜测,但似乎与我看到的症状相符。我也有同样的问题,有人找到了解决方案?我在找到解决方案之前放弃了烧瓶-但是Brian DeRocher下面的评论可能值得尝试…如果你没有使用Gunicorn,仔细研究您的容器/服务器,因为它可能是原因。您知道它之间的关系吗?我不确定,但我认为这是因为web服务器(Gunicorn)为相应的FLASK实例创建了独立的操作系统进程。这些实例没有同步或相互通信,因为FLASK不支持这一点(我知道)。
from flask import Flask, render_template, url_for, flash, redirect,  Response, jsonify, abort, session
from flask_session import Session
from flask_wtf.csrf import CSRFProtect
from flask_cors import CORS

from flask_login import  LoginManager,UserMixin,current_user,login_required,login_user,logout_user

from forms import RegistrationForm, LoginForm, TimecardForm
from employees import employees

csrf = CSRFProtect()

app = Flask(__name__)
csrf.init_app(app)

app.config['SECRET_KEY'] = os.getenv('SECRET_KEY') or \
    'abc123ced456'

app.config['SESSION_TYPE'] = 'memcached'
app.config['WTF_CSRF_ENABLED'] = True
app.config['WTF_CSRF_SECRET_KEY'] = os.getenv('SECRET_KEY') or \
    'abc123ced456'
app.config['SESSION_COOKIE_SECURE'] =  True
app.config['REMEMBER_COOKIE_SECURE'] =  True

CORS(app)
sess = Session()
sess.init_app(app)


login_manager = LoginManager()
login_manager.init_app(app)
login_manager.session_protection = "strong"
login_manager.login_view = 'login'


@login_manager.user_loader
def load_user(userid):
    result = None
    emp_collection = employees.oEmployeeCollection()
    emp_collection.getAllEmployees(None, None)
    result = emp_collection.getEmployee(userid)

    return result

@app.route("/login", methods=['GET', 'POST'])
def login():
    form = LoginForm()

    if form.validate_on_submit():
        emp_collection = employees.oEmployeeCollection()
        emp_collection.getAllEmployees(None, None)
        current_user = emp_collection.getEmployee(form.user_init.data.upper())

        if current_user is not None:
            if current_user.password == form.password.data:
                login_user(current_user, remember=True)
                sess['current_user'] = current_user.toJSON()

                flash('You have been logged in!', 'success')

                #next = flask.request.args.get('next')
                ## is_safe_url should check if the url is safe for redirects.
                #if not is_safe_url(next):
                #    return flask.abort(400)
                #return flask.redirect(next or flask.url_for('index'))

                return redirect(url_for('home'))
            else:
                flash('Login Unsuccessful. Please check username and password', 'danger')

        else:
            flash('Login Unsuccessful. Please check username and password', 'danger')

    flash(form.errors)
    return render_template('login.html', title='Login', form=form)


@app.before_first_request
def execute_this():
    # emp_collection.getAllEmployees(None, None)
    test = None

if __name__ == '__main__':
    app.run(host='flask.local', port=5000, debug=False)
{% extends "template.html" %}
{% block content %}
    <div class="content-section">
        <form method="POST" action="">
            {{ form.hidden_tag() }}
            {{ form.csrf_token() }}

            <fieldset class="form-group">
                <legend class="border-bottom mb-4">Log In</legend>

                <div class="form-group">
                    {{ form.user_init.label(class="form-control-label")}}
                    {% if form.user_init.errors %}
                        {{ form.user_init(class="form-control form-control-lg is-invalid") }}
                        <div class="invalid-feedback">
                            {% for error in form.user_init.errors %}
                                <span>{{ error }}</span>
                            {% endfor %}
                        </div>
                    {% else %}
                        {{ form.user_init(class="form-control form-control-lg") }}
                    {% endif %}
                </div>
                <div class="form-group">
                    {{ form.password.label(class="form-control-label") }}
                    {% if form.password.errors %}
                        {{ form.password(class="form-control form-control-lg is-invalid") }}
                            <div class="invalid-feedback">
                            {% for error in form.password.errors %}
                                <span>{{ error }}</span>
                            {% endfor %}
                        </div>
                    {% else %}
                        {{ form.password(class="form-control form-control-lg") }}
                    {% endif %}
                </div>
                <div class="form-check">
                    {{ form.remember(class="form-check-input") }}
                    {{ form.remember.label(class="form-check-label") }}
                </div>
            </fieldset>
            <div class="form-group">
                {{ form.submit(class="btn btn-outline-info") }}
            </div>
            <small class="text-muted ml-2">
                <a href="#">Forgot Password?</a>
            </small>
        </form>
    </div>
    <div class="border-top pt-3">
        <small class="text-muted">
            Need An Account? <a class="ml-2" href="{{ url_for('register') }}">Sign Up Now</a>
        </small>
    </div>
{% endblock content %}
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField,      BooleanField, DateField, DecimalField
from wtforms.validators import DataRequired, Length, Email, EqualTo

class LoginForm(FlaskForm):
    user_init = StringField('User',  validators=[DataRequired()])
    password = PasswordField('Password', validators=[DataRequired()])
    remember = BooleanField('Remember Me')
    submit = SubmitField('Login')
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>400 Bad Request</title>
<h1>Bad Request</h1>
<p>The CSRF session token is missing.</p>
Content-Type →text/html
Content-Length →142
Access-Control-Allow-Origin →*
Set-Cookie →session=ad0a88f2-4048-4a3b-9934-c2cd5957e9ff; Expires=Sun, 03-Feb-2019 14:55:27 GMT; HttpOnly; Path=/
Server →Werkzeug/0.14.1 Python/3.7.1
Date →Thu, 03 Jan 2019 14:55:27 GMT
Request URL: http://localhost:5000/login
Request Method: POST
Status Code: 400 BAD REQUEST
Remote Address: 127.0.0.1:5000
Referrer Policy: no-referrer-when-downgrade
Access-Control-Allow-Origin: http://localhost:5000
Content-Length: 150
Content-Type: text/html
Date: Thu, 03 Jan 2019 14:47:18 GMT
Server: Werkzeug/0.14.1 Python/3.7.1
Set-Cookie: session=62e6139c-332b-4811-ad3a-de5c29c878aa; Expires=Sun, 03-Feb-2019 14:47:18 GMT; HttpOnly; Path=/
Vary: Origin
POST /login HTTP/1.1
Host: localhost:5000
Connection: keep-alive
Content-Length: 258
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36
Origin: http://localhost:5000
Content-Type: application/x-www-form-urlencoded
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Referer: http://localhost:5000/login
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9

Cookie: Webstorm-655f3561=d5da8892-b9fc-4680-8fe8-17baf5fd6f8d;session=62e6139c-332b-4811-ad3a-de5c29c878aa
csrf_token=ImI5ZDlkYjZmNjkxMDZlZDczZjdlY2VjMTM2NTQzOWZlMDBkYTY1ZWMi.XC4gZQ.DVyKZ07nrQN6WZn0jmoHyKrf_YI&
        csrf_token=ImI5ZDlkYjZmNjkxMDZlZDczZjdlY2VjMTM2NTQzOWZlMDBkYTY1ZWMi.XC4gZQ.DVyKZ07nrQN6WZn0jmoHyKrf_YI&user_init=ABC&password=changeme&remember=y&submit=Login
<input id="csrf_token" name="csrf_token" type="hidden" value="... long string ...">