Flask-WTF SelectField with CSRF protection enabled - python

I'm having a problem when submitting a form containing a dynamically populated SelectField. For some reason when Flask tries to validate the CSRF token it always fails when the SelectField is in the form. When I remove the SelectField from the form, it validates the CSRF token successfully.
Has anyone come across this behavior?
EDIT
Form:
class AddToReportForm(Form):
selectReportField = SelectField(u'Reports',choices=[('test1','test')])
def __init__(self, *args, **kwargs):
"""
Initiates a new user form object
:param args: Python default
:param kwargs: Python default
"""
Form.__init__(self, *args, **kwargs)
def validate(self,id_list):
rv = Form.validate(self)
if not rv:
print False
#Check for the CSRF Token, if it's not there abort.
return False
print True
return True
Jinja2:
<form method=post name="test">
{{ form.hidden_tag()}}
{{ form.selectReportField }}
Add to report
</form>
Rendering:
form = AddToReportForm()
return render_template('random',title='add reports',form=form

Where are you setting SECRET_KEY? It must be available either in the Form class:
class AddToReportForm(Form):
selectReportField = SelectField(u'Reports',choices=[('test1','test')])
SECRET_KEY = "myverylongsecretkey"
def __init__(self, *args, **kwargs):
"""
Initiates a new user form object
:param args: Python default
:param kwargs: Python default
"""
Form.__init__(self, *args, **kwargs)
def validate(self,id_list):
rv = Form.validate(self)
if not rv:
print False
#Check for the CSRF Token, if it's not there abort.
return False
return True
or in the application bootstrap:
app = Flask(__name__)
app.secret_key = 'myverylongsecretkey'
or in the constructor:
form = AddToReportForm(secret_key='myverylongsecretkey')
return render_template('random',title='add reports',form=form)

I still can't see any connection between SelectField and CSRF. The validate method is little suspicious and the extra argument would trip the following testcase, but as it stands this seems to work just fine:
from flask import Flask, render_template_string
from flaskext.wtf import Form, SelectField
app = Flask(__name__)
app.debug = True
app.secret_key = 's3cr3t'
class AddToReportForm(Form):
selectReportField = SelectField(u'Reports', choices=[('test1', 'test')])
#app.route('/test', methods=['GET', 'POST'])
def test():
form = AddToReportForm()
if form.validate_on_submit():
print 'OK'
return render_template_string('''\
<form method=post name="test">
{{ form.hidden_tag()}}
{{ form.selectReportField }}
<input type="submit">
</form>
''', form=form)
app.run(host='0.0.0.0')

Recommended use:
app.secret_key = 'key here' # key user defined

Related

Flask-WTF: variable choices for SelectField in FieldList of FormField

I have a setup with a FieldList(FormField(SubForm)) where SubForm includes a SelectField whose choices are determined at runtime (in the view function). Currently I have the following setup, which basically derives a new class for SubForm with the choices each time it is called:
from flask import Flask, render_template_string, url_for
from wtforms import SelectField, StringField, FieldList, FormField
from flask_wtf import FlaskForm as Form
app = Flask(__name__)
app.secret_key = 'secret_key'
class BaseForm(Form):
#classmethod
def append_field(cls, name, field):
setattr(cls, name, field)
return cls
class SubForm(BaseForm):
title = StringField('SubForm')
class RootForm(BaseForm):
entries = FieldList(FormField(SubForm))
#app.route('/test/', methods=['GET', 'POST'])
def form_viewer():
form = RootForm(title='Title')
subformclass = SubForm
subformclass = subformclass.append_field('options',
SelectField('Options', choices=[('%i' %i,'option%i' %i) for i in range(1,5)]))
if form.validate_on_submit():
while form.entries:
subform = form.entries.pop_entry()
print(subform.options.data)
else:
print(form.errors)
for entry in range(1,3):
subform = subformclass()
subform.title = 'SubTitle%i' %entry
form.entries.append_entry(subform)
html_template = '''<html>
<form action="{{ url_for('form_viewer') }}" method=post>
{{ form.hidden_tag() }}
{{ form.entries() }}
<input type=submit value=Submit>
</form>
</html>'''
return render_template_string(html_template, form=form)
My problem is now, that if the app restarts (because things happen) between a GET and POST request, then it raises AttributeError: 'UnboundField' object has no attribute 'data' while trying to access the submitted form data.
How can this be fixed? Or is there maybe another, better way to have a variable number of SelectFields with variable choices?
edit:
I think the problem was, that after FieldList was created, I changed SubForm. So now I am creating FieldList in a similar way
like SelectField:
formclass = RootForm.append_field('entries', FieldList(FormField(subformclass)))
But I am still wondering whether this is the way I am supposed to do it? It feels like, there might be a more elegant solution.

Implementing a login page with Flask

Does Flask have any built-in support for user login/logout functionality? I've found this add-on project, but it only seems to provide the pieces to build your own login system. It doesn't seem to be a complete system.
I'm new to Flask, and I'm coming from Django where this is all built-in, so I'm finding it a little baffling that this basic functionality is missing.
Using some incomplete examples I've found, I'm trying to implement an index page that redirects to a login page for anonymous users, and after a successful login, redirects page to the index page. This is what I currently have implemented as a "hello world" login/logout example:
#!/usr/bin/env python
import flask
from flask import Flask, Response, render_template
from flask.ext.login import LoginManager, UserMixin, login_required, login_user, logout_user
app = Flask(__name__)
login_manager = LoginManager()
login_manager.init_app(app)
class User(UserMixin):
# proxy for a database of users
user_database = {
"JohnDoe": ("JohnDoe", "John"),
"JaneDoe": ("JaneDoe", "Jane"),
}
def __init__(self, username, password):
self.id = username
self.password = password
#property
def name(self):
return self.id
#classmethod
def get(cls, id):
ret = cls.user_database.get(id)
if ret is not None:
return cls(*ret)
#login_manager.user_loader
def load_user(user_id):
return User.get(user_id)
#login_manager.request_loader
def load_user(request):
token = request.headers.get('Authorization')
if token is None:
token = request.args.get('token')
if token is not None:
username,password = token.split(":") # naive token
user_entry = User.get(username)
if (user_entry is not None):
user = User(user_entry[0],user_entry[1])
if (user.password == password):
return user
return None
#app.route("/",methods=["GET"])
#login_manager.request_loader
def index():
if load_user(flask.request):
return render_template('index.html')
else:
return flask.redirect(flask.url_for('login'))
#return Response(response="Hello World!",status=200)
#app.route('/login', methods=['GET', 'POST'])
def login():
if flask.request.method == 'GET':
return '''
<form action='login' method='POST'>
<input type='text' name='email' id='email' placeholder='email'></input>
<input type='password' name='pw' id='pw' placeholder='password'></input>
<input type='submit' name='submit'></input>
</form>
'''
email = flask.request.form['email']
user = User.get(email)
if user and flask.request.form['pw'] == user.password:
login_user(user)
return flask.redirect(flask.url_for('index'))
return 'Bad login'
#app.route("/logout")
#login_required
def logout():
logout_user()
return flask.redirect(flask.url_for('index'))
if __name__ == '__main__':
app.config["SECRET_KEY"] = "ITSASECRET"
app.run(port=5000, debug=True)
However, it doesn't work, because even though it seems to login successfully, when it redirects to the index page, it can't lookup the user from the session and redirects back to the login page. What am I doing wrong?
Flask-Login is a very basic login manager that is built upon with a few other user management frameworks. I have been using Flask-User in production for over 1.5 years with about 30K users and have not had any problems with it (which uses flask-login under the hood). The maintainer is active and has responded to my issues in a timely manner.
It handles user login, registration, lost password, and even email confirmations if so desired. It comes with some pre-built forms if you don't want to mess with that but is easily customizable if you do.
Sounds like maybe when login_user gets called the login is not being persisted. I would make sure however you're doing your sessions is actually storing the login.

Methods to protect a bottle.py webapplication against XSS and CSRF?

I'm writing a webapplication using bottle.py, Beaker for sessions and a custom AAA written module, as many I'm worried about security and the best method to protect against some targeted attack like the one I've mentioned.
As an example I have the following code:
#route('/manage/adddomain',method='POST')
def adddomain():
#This checks if user has enough power to create a domain
aaa.require(50,'/forbidden')
user = aaa.getusername() # This is retrieved from a server side session
domainname = request.forms.get('domain')
description = request.forms.get('description')
# Additional checks are performed in the sql module
# to protect against forged requests with valid login
return sql.createdomain(user,domainname,description)
What additional checks would you perform to secure your webapplication?
Blender pretty much covered what you need, but I would like to add another method. Instead of checking on each POST, you can add a wrapper like:
def wrap_requires_csrf(*methods):
def wrapper(fn):
#wraps(fn)
def wrapped(*args, **kwargs):
if request.method in methods:
if request.method == 'POST':
csrf = request.form.get('csrf')
elif request.method == 'GET':
csrf = request.args.get('csrf')
if not csrf or csrf != session.get('csrf'):
abort(400)
session['csrf'] = generate_csrf_token()
return fn(*args, **kwargs)
return wrapped
return wrapper
#app.route('/some/page', methods=['GET','POST'])
#wrap_requires_csrf('POST')
def some_page():
...
Then, in your template, you would provide the hidden field with
<input name="csrf" type="hidden" name="{{session.csrf}}" />
You need to include a CSRF token in every important form field and sanitize all output with your template engine.
Here's a Flask snippet that you can adapt to your Bottle app:
A common technique against CSRF attacks is to add a random string
to the session, and check that string against a hidden field in the
POST.
#app.before_request
def csrf_protect():
if request.method == "POST":
token = session.pop('_csrf_token', None)
if not token or token != request.form.get('_csrf_token'):
abort(403)
def generate_csrf_token():
if '_csrf_token' not in session:
session['_csrf_token'] = some_random_string()
return session['_csrf_token']
app.jinja_env.globals['csrf_token'] = generate_csrf_token
And then in your template:
<form method=post action="">
<input name=_csrf_token type=hidden value="{{ csrf_token() }}">
As for sanitizing, that's dependent upon your template engine. Jinja2 has the e or escape filter:
<h2>No results for {{ search_query|escape }}</h2>
I adapted #Blender's solution to bottle, and here it is:
from string import ascii_letters, digits
from random import choice
from bottle import Bottle, request, Jinja2Template, abort
app = Bottle()
#app.hook('before_request')
def csrf_protect():
if request.method == 'POST':
sess = request.environ['beaker.session']
req_token = request.forms.get('csrf_token')
# if no token is in session or it doesn't match the request one, abort
if 'csrf_token' not in sess or sess['csrf_token'] != req_token:
abort(403)
def str_random(length):
'''Generate a random string using range [a-zA-Z0-9].'''
chars = ascii_letters + digits
return ''.join([choice(chars) for i in range(length)])
def gen_token():
'''Put a generated token in session if none exist and return it.'''
sess = request.environ['beaker.session']
if 'csrf_token' not in sess:
sess['csrf_token'] = str_random(32)
return sess['csrf_token']
# allow access of the token generator using csrf_token
Jinja2Template.defaults['csrf_token'] = gen_token
Bottle does not come with sessions by default, so I'm using bottle-beaker.
The input which needs to be included in every form:
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">

method="delete" from html form in FLASK

I have an HTML form:
{% set delete_urls = url_for('store_add') ~ store_id ~ "?__METHOD_OVERRIDE__=DELETE" %}
<form action="{{delete_urls}}" name="delete" method="post" id="{{form_id}}" style="display:none">
and in the views:
class StoreAdd(MethodView):
#login_required
def delete(self,store_id):
store_selected = request.args['store_id']
qstr = "DELETE FROM store WHERE store_id=%d AND cust_id=%d"%(store_id,self.cust_id)
h = pgexec(qstr,False,True)
h.process()
flash("deleted the store:%d"%(store_selected))
return redirect(url_for('store_add'))
store_add = StoreAdd.as_view('store_add')
app.add_url_rule('/storeadd/',
defaults={'store_id': 0},
view_func=store_add,
methods=["GET","PUT"])
app.add_url_rule('/storeadd/',
view_func=store_add,
methods=["POST"])
app.add_url_rule('/storeadd/<int:store_id>',
view_func=store_add,
methods=['DELETE','PUT','GET'])
Of course implemented the routing:
from werkzeug import url_decode
from flask import flash
class MethodRewriteMiddleware(object):
def __init__(self, app):
self.app = app
def __call__(self, environ, start_response):
if 'METHOD_OVERRIDE' in environ.get('QUERY_STRING', ''):
args = url_decode(environ['QUERY_STRING'])
method = args.get('__METHOD_OVERRIDE__')
if method in ['GET', 'POST', 'PUT', 'DELETE']:
method = method.encode('ascii', 'replace')
environ['REQUEST_METHOD'] = method
return self.app(environ, start_response)
But still on submission of the delete form it cannot access the delete method?
What is going wrong?
Edit:
The problem with delete is as follows.
When I submit the form it seems it tries to "POST" to url:
/storeadd/13?__METHOD_OVERRIDE__=DELETE
But the POST url rule says it can only be: /storeadd. Thus it gives 405 ERROR Page.
Thus the override which should happen never happens.
Have you applied this middleware on your flask application?
app.wsgi_app = MethodRewriteMiddleware(app.wsgi_app)

Unable to use flask.g to access variables in other functions

I'm trying to use flask.g to store variables that can be accessed in other functions, but I don't seem to be doing something correctly. The application generates the following error when I try to access g.name: AttributeError: '_RequestGlobals' object has no attribute 'name'.
The documentation for flask.g says:
Just store on this whatever you want. For example a database
connection or the user that is currently logged in.
Here's a complete, minimal example that illustrates the error that I receive when trying to access the variable outside of the function it was created in. Any help would be greatly appreciated.
#!/usr/bin/env python
from flask import Flask, render_template_string, request, redirect, url_for, g
from wtforms import Form, TextField
application = app = Flask('wsgi')
#app.route('/', methods=['GET', 'POST'])
def index():
form = LoginForm(request.form)
if request.method == 'POST' and form.validate():
name = form.name.data
g.name = name
# Need to create an instance of a class and access that in another route
#g.api = CustomApi(name)
return redirect(url_for('get_posts'))
else:
return render_template_string(template_form, form=form)
#app.route('/posts', methods=['GET'])
def get_posts():
# Need to access the instance of CustomApi here
#api = g.api
name = g.name
return render_template_string(name_template, name=name)
class LoginForm(Form):
name = TextField('Name')
template_form = """
{% block content %}
<h1>Enter your name</h1>
<form method="POST" action="/">
<div>{{ form.name.label }} {{ form.name() }}</div><br>
<button type="submit" class="btn">Submit</button>
</form>
{% endblock %}
"""
name_template = """
{% block content %}
<div>"Hello {{ name }}"</div><br>
{% endblock %}
"""
if __name__ == '__main__':
app.run(debug=True)
The g object is a request-based object and does not persist between requests, i.e. g is recreated between your request to index and your request to get_posts.
Application Globals in Flask:
Flask provides you with a special object that ensures it is only valid for the active request and that will return different values for each request. In a nutshell: it does the right thing, like it does for request and session.
For persistent storage of tiny data between requests use sessions instead. You may (but should not) get away with storing the data in the app object directly for global (all sessions) application state, similar to what config does, if you find a really good reason to do so.
For more complex data use databases.
If you need to track authentication information, I'd suggest one of the Flask plugins like Flask-Login or Flask-Principal.
For example, we use Flask-Principal. It raises the identity-loaded signal when somebody authenticates (or it detects an authentication cookie). We then map their logged-in identity with a user in our database. Something like this:
# not actual code
#identity_loaded.connect_via(app)
def on_identity_loaded(sender, identity):
user = Person.query.filter(Person.username==identity.person.username).one()
g.user = user
and then we can use g.user in any controller or template. (We're actually ripping a lot of this out, it was a easy, lazy hack that's caused more trouble than it's worth.)
If you don't want to use a module, there's a built-in signal you can hook into at the start of every request:
http://flask.pocoo.org/docs/tutorial/dbcon/
# This runs before every request
#app.before_request
def before_request():
g.user = your_magic_user_function()
and g.user would then be magically available everywhere.
I hope that helps!
Just use sessions in flask. In your case, you just want to save the user/name in your request and the easiest way is to use sessions.
from flask import session
app.secret_key = 'some key for session'
Then, your functions could be changed as below:
#app.route('/', methods=['GET', 'POST'])
def index():
form = LoginForm(request.form)
if request.method == 'POST' and form.validate():
session['name'] = form.name.data
return redirect(url_for('get_posts'))
else:
return render_template_string(template_form, form=form)
#app.route('/posts', methods=['GET'])
def get_posts():
if 'name' in session:
name = session['name']
else:
name = "Unknown"
return render_template_string(name_template, name=name)
I will like to shed more light on the use of g global in storing data. g only store data with a request and when redirecting to another route, the g global is set back to null i.e it reset back to nothing. This means whatever set to g in one request can't be access in another request. Use sessions to store data that will be accessed across request.
One benefit of using g global is when connecting to a database to fetct a user. For example, may be the admin from the database. The admin can be store in the g global using the below method.
from flask import Flask, g
app = Flask(__name__)
#app.before_request
def text():
g.a = User.query.filter_by(email='admin#gmail.com')
#app.route("/getTrue", methods=['GET', 'POST'])
def getTrue():
form = UserForm()
if form.validate_on_submit():
if g.a == form.email.data:
return "Admin is logged in"
else:
return "User is logged in"
return render_template('login.html', form=form)
In the example above, the g can be use to save data which will be use in another request. I hope this help. Thanks

Categories