Conflict between captcha and CSRF - python

For my login page, I have enable a captcha where user has to enter a 4 digits as displayed in the image. I'm using flask-session-captcha module for this. I followed exactly the same steps as in the instruction.
In the same login page, I also have a hidden CSRF token.
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
It's working fine when I developed and tested it in my local environment (my laptop) but it doesn't work when I tested it in the test server. It will give errors such as "The CSRF tokens do not match." or "Invalid captcha" (even though I answered correctly) or sometimes "Missing CSRF token".
I have tried so many things like using decorator #csrf_exempt for that particular login page. I also added this:
#app.after_request
def apply_caching(response):
response.headers["X-Frame-Options"] = "SAMEORIGIN"
response.headers["Cache-Control"] = "public, max-age=0, no-cache, no-store, must-revalidate"
response.headers["Pragma"] = "no-cache"
response.headers["Expires"] = "0"
return response
But nothing seems to work. It really puzzles me why it works on local but not on test server. I tried not to use csrf_token() in the login page but it gave me error of missing csrf token. Using Google ReCaptcha is not an option because the user doesn't want to use that.
I wonder if it's because the captcha token is conflicted with the csrf token?

Can you try flask_recaptcha?
Below are minimized example.
#app/__init__.py
from flask_recaptcha import ReCaptcha
recaptcha = ReCaptcha(app=app)
#app/views/your_view.py
#app.route('/a_view_for_demo/', methods=['GET', 'POST'])
def a_view_for_demo():
a_form = AForm()
if a_form.validate_on_submit() and recaptcha.verify():
# do your thing after validation.
return render_template('a_template.html', a_form=a_form)
#app_config
RECAPTCHA_ENABLED=True
RECAPTCHA_SITE_KEY="get this key from Google"
RECAPTCHA_SECRET_KEY="get this key from Google"
# a_template.html
<form .....>
{{ a_form.csrf_token }}
{{ recaptcha }}
</form>

Related

Flask form validation - CSRF token middleware

I have an html form, and I would like to insure that all submissions come from my website. I think I have seen people using a key for this (I believe this happens in Django?), and might have some ideas on how to go with that. Is there any standard way to do this in Flask?
Edit:
Now I know I'm talking about CSRF token middleware. Again, is there any standard way of doing this in Flask? How can I store the key on the server side?
In flask you can do CSRF protection using Flask-SeaSurf.There are other methods also but it is straight forward.
To start Just do pip install flask-seasurf and you are ready
import Flask
from flask_seasurf import SeaSurf
app = Flask(__name__)
csrf = SeaSurf(app)
<form method="POST">
...
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
</form>
#csrf.exempt
#app.route('/exempt_view', methods=['POST'])
def exempt_view():
'''This view is exempted from CSRF validation.'''
return 'foobar'
For more information you can visit official website
Please mark this as answer if this solves you problem.

Flask not getting cookies

I'm making backend for my website and it requires flask setting cookies. When I set the cookies, it works fine, but when I try to get them with request.cookies.get('name') it returns None. I tried just returning request.cookies and all that was there was my GA cookies, not the one that I set. Am I doing something wrong? Here's my code:
#app.route("/setcookie", methods=["GET", "POST"])
def setcookie():
resp = make_response(render_template("index.html"))
resp.set_cookie("authToken", "testestestestestestes", max_age=1)
return resp
#app.route("/getcookie", methods=["GET" ,"POST"])
def getcookie():
return request.cookies
index.html form:
<form action="/setcookie" method="POST">
<button type="submit">Set</button>
</form>
<form action="/getcookie" method="POST">
<button type="submit">Get</button>
</form>
I compared the GA cookies to the cookies that I set and the only difference was the name and value so that confuses me even more. I also don't need to do anything with them on the frontend, they only need to be read by flask. Can someone help? Thanks
You set max_age to 1 second, your cookie just gets expired, try increasing max_age value

Using GET in a Django Form

I have a question regarding Django Forms and GET
I have a form to download student scores in CSV format. The fields are name and year so I have a forms.py
StudentDownloadForm(forms.Form):
name=forms.CharField()
year = forms.CharField()
And I want to use this form in the template.html with
context={'student_form' : StudentDownloadForm(),}
<form action ="" method="GET">
{% csrf_token %}{{ student_form|crispy }}
<input type="submit" value="Query"/>
</form>
So my questions are as follows:
If I use the method="GET" then the csrf token is visible in the URL, which is a security issue
Can I then use the method="POST" instead?
Alternatively, can I remove the csrf token in the form?
According to Django documentation (Cross Site Request Forgery protection):
For all incoming requests that are not using HTTP GET, HEAD, OPTIONS
or TRACE, a CSRF cookie must be present, and the ‘csrfmiddlewaretoken’
field must be present and correct. If it isn’t, the user will get a
403 error.
And:
It deliberately ignores GET requests (and other requests that are
defined as ‘safe’ by RFC 2616). These requests ought never to have any
potentially dangerous side effects , and so a CSRF attack with a GET
request ought to be harmless. RFC 2616 defines POST, PUT and DELETE as
‘unsafe’, and all other methods are assumed to be unsafe, for maximum
protection.
So, you can omit CSRF token for GET requiests

What is the cause of the Bad Request Error when submitting form in Flask application?

After reading many similar sounding problems and the relevant Flask docs, I cannot seem to figure out what is generating the following error upon submitting a form:
400 Bad Request
The browser (or proxy) sent a request that this server could not understand.
While the form always displays properly, the bad request happens when I submit an HTML form that ties to either of these functions:
#app.route('/app/business', methods=['GET', 'POST'])
def apply_business():
if request.method == 'POST':
new_account = Business(name=request.form['name_field'], email=request.form['email_field'], account_type="business",
q1=request.form['q1_field'], q2=request.form['q2_field'], q3=request.form['q3_field'], q4=request.form['q4_field'],
q5=request.form['q5_field'], q6=request.form['q6_field'], q7=request.form['q7_field'],
account_status="pending", time=datetime.datetime.utcnow())
db.session.add(new_account)
db.session.commit()
session['name'] = request.form['name_field']
return redirect(url_for('success'))
return render_template('application.html', accounttype="business")
#app.route('/app/student', methods=['GET', 'POST'])
def apply_student():
if request.method == 'POST':
new_account = Student(name=request.form['name_field'], email=request.form['email_field'], account_type="student",
q1=request.form['q1_field'], q2=request.form['q2_field'], q3=request.form['q3_field'], q4=request.form['q4_field'],
q5=request.form['q5_field'], q6=request.form['q6_field'], q7=request.form['q7_field'], q8=request.form['q8_field'],
q9=request.form['q9_field'], q10=request.form['q10_field'],
account_status="pending", time=datetime.datetime.utcnow())
db.session.add(new_account)
db.session.commit()
session['name'] = request.form['name_field']
return redirect(url_for('success'))
return render_template('application.html', accounttype="student")
The relevant part of HTML is
<html>
<head>
<title>apply</title>
</head>
<body>
{% if accounttype=="business" %}
<form action="{{ url_for('apply_business') }}" method=post class="application_form">
{% elif accounttype=="student" %}
<form action="{{ url_for('apply_student') }}" method=post class="application_form">
{% endif %}
<p>Full Name:</p>
<input name="name_field" placeholder="First and Last">
<p>Email Address:</p>
<input name="email_field" placeholder="your#email.com">
...
The problem for most people was not calling GET or POST, but I am doing just that in both functions, and I double checked to make sure I imported everything necessary, such as from flask import request. I also queried the database and confirmed that the additions from the form weren't added.
In the Flask app, I was requesting form fields that were labeled slightly different in the HTML form. Keeping the names consistent is a must. More can be read at this question Form sending error, Flask
The solution was simple and uncovered in the comments. As addressed in this question, Form sending error, Flask, and pointed out by Sean Vieira,
...the issue is that Flask raises an HTTP error when it fails to find a
key in the args and form dictionaries. What Flask assumes by default
is that if you are asking for a particular key and it's not there then
something got left out of the request and the entire request is
invalid.
In other words, if only one form element that you request in Python cannot be found in HTML, then the POST request is not valid and the error appears, in my case without any irregularities in the traceback. For me, it was a lack of consistency with spelling: in the HTML, I labeled various form inputs
<input name="question1_field" placeholder="question one">
while in Python, when there was a POST called, I grab a nonexistent form with
request.form['question1']
whereas, to be consistent with my HTML form names, it needed to be
request.form['question1_field']

How to protect a Google App Engine app with a password?

How would you implement simple password protection on a Google App Engine application? No users authentication, just simple requirement to enter a password in order to open specific page. The other requirement is that the target page should not be displayed if its URL is entered directly.
I'm looking for a solution using Python.
If you're protecting a single page and need no session persistence.
class MainPage(webapp.RequestHandler):
def post(self):
if self.request.get('user') == 'admin' and self.request.get('pass') == 'soopersecure':
self.response.out.write('authorized');
else:
self.response.out.write("""
<form method="post">
<input type="text" name="user"/>
<input type="password" name="pass"/>
<input type="submit" value="login"/>
</form>""")
Otherwise you could hash the username + salt and hand it to user as a session ID in a cookie and store that session ID into the datastore. Much simpler to use Google accounts though.
http://code.google.com/appengine/docs/python/gettingstarted/usingusers.html
If you want to restrict access for the entire app, use URL handler with "login" setting
Check - User and Administrator Login

Categories