Implement Web logon validation using the Python Flask Framework form plug-in FLASK-WTF _python

Source: Internet
Author: User
Tags openid csrf attack

A form is the basic element that lets users interact with our web application. Flask itself does not help us with the form, but the FLASK-WTF extension lets us use the popular wtforms package in our flask applications. This package makes it easier to define forms and process submissions.

Flask-wtf
The first thing we want to do with FLASK-WTF (after installing it, GitHub project page: HTTPS://GITHUB.COM/LEPTURE/FLASK-WTF) is to define a form in the Myapp.forms package.

# ourapp/forms.py

from FLASK_WTF import Form
to wtforms import Stringfield, Passwordfield from
Wtforms.validators import datarequired, email

class Emailpasswordform (Form):
 email = stringfield (' email ', Validators=[datarequired (), Email ()])
 password = passwordfield (' Password ', validators=[datarequired ())

Prior to the FLASK-WTF 0.9 version, FLASK-WTF provided its own encapsulation for wtforms fields and validators. You may see a lot of code outside of the flask.ext.wtforms is not imported from the Wtforms Textfield,passwordfield.
After FLASK-WTF version 0.9, we should import these fields and validators directly from Wtforms.
The form we define is a user login form. We call it Emailpasswordform (), and we can reuse the same form class (form) to do other things, like registering a form. Instead of defining a long, unused form, we choose a very common form, just to show you how to define a form using FLASK-WTF. Perhaps a particularly complex form will be defined later in the formal project. For a form to contain field names, we recommend that you use a clear name and remain unique in a form. I have to say, for a long form, we might want to give a more consistent field name.

The sign-in form can do something for us. It ensures the security of our applications to prevent CSRF vulnerabilities, validate user input, and render the appropriate tags, which are the fields that we define for the form.

CSRF Protection and validation
CSRF says Cross station request forgery. CSRF attack refers to a third-party forgery (like a form submission) requesting a server to an application. A vulnerable server assumes that data from a form is from its own web site and takes the appropriate action.

As an example, a mail provider may allow you to delete your account by submitting a form. The form sends a POST request to the Account_delete endpoint on the server and deletes the login account when the form is submitted. We can create a form on our own web site that sends a POST request to the same Account_delete endpoint. Now, if we let someone click on our form's submit button (or do it through JavaScript), the login account provided by the email provider will be deleted. Of course, the email provider does not yet know that the form submission does not occur on their site.

So how do you block POST requests from other sites? Wtforms makes it possible to generate a unique token while rendering each form. The generated token is sent back to the server, and the token must accept the server's validation before the form is accepted, along with the data for the POST request. The key is that the token is related to a value stored in the user session (cookies) and will expire after a period of time (default is 30 minutes). This approach ensures that the person who submits a valid form is the person who loads the page (or at least the person who is using the same computer), and that they can only do so within 30 minutes of loading the page.

To start using FLASK-WTF protection CSRF, we need to define a view for our login page.

# ourapp/views.py from

flask import render_template, redirect, url_for from

. Import apps from
. Forms Import Em Ailpasswordform

@app. Route ('/login ', methods=["get", "POST"])
def login ():
 form = Emailpasswordform ()
 if Form.validate_on_submit ():

  # Check The password and log the user in
  # [...]

  Return Redirect (Url_for (' index ')) return
 render_template (' login.html ', form=form)

If the form has been submitted and validated, we can continue the logic of the login. If it is not submitted (for example, just a GET request), we will pass the form object to our template so that it can be rendered. Here's what the template looks like when we use CSRF protection.

{# ourapp/templates/login.html #}

{% extends ' layout.html '%}
{% Endraw%}
 
 

{% RAW%} {{Form.csrf_token}}} {% Endraw%} renders a hidden field that contains those peculiar CSRF tokens and looks for this field when wtforms validates the form. We don't have to worry about the logic of handling tokens, Wtforms will take the initiative to help us. Oh, wow!

Custom validation
In addition to the built-in form validators provided by wtforms (for example, Required (), Email (), etc.), we can create our own validators. We will write a unique () validator to show how to create our own validator, the unique () validator is used to check the database and ensure that the user-supplied values do not exist in the database. This can be used to ensure that the user name or email address is not yet in use. Without wtforms, we might want to do these things in the view, but now we can do something in the form itself.

Now let's define a simple registration form, which is virtually the same as the form that is logged in. Just add some custom validators to it later.

# ourapp/forms.py

from FLASK_WTF import Form
to wtforms import Stringfield, Passwordfield from
Wtforms.validators import datarequired, email

class Emailpasswordform (Form):
 email = stringfield (' email ', Validators=[datarequired (), Email ()])
 password = passwordfield (' Password ', validators=[datarequired ())

Now we're going to add our validators to ensure that the mailbox addresses they provide do not exist in the database. We put this validator in a new util module, util.validators.

# ourapp/util/validators.py from
wtforms.validators import ValidationError

class Unique (object):
 def _ _init__ (self, model, field, Message=u ' this element already exists. '):
  Self.model = model
  Self.field = field< C14/>def __call__ (Self, form, field):
  check = self.model.query.filter (Self.field = = field.data).
  Check:
   raise ValidationError (Self.message)

This validator assumes that we are using SQLAlchemy to define our model. Wtforms expects the validator to return some callable object (for example, a callable Class).

In the \_\_init\_\_ of Unique () we can specify which parameters to pass into the validator, in this case we'll pass in the relevant model (for example, in our example, the incoming User model) and the field to check. When the validator is invoked, it throws a validationerror if any instance of the model is defined to match the value submitted in the form. We can also add a message with a common default value that will be included in the ValidationError.

Now we can modify the Emailpasswordform and use our custom Unique validator.

# ourapp/forms.py

from FLASK_WTF import Form
to wtforms import Stringfield, Passwordfield from
Wtforms.validators import datarequired from

. Util.validators Import the Unique from
. Models Import User

class Emailpasswordform (Form):
 email = stringfield (' email ', validators=[datarequired (), email (),
  Unique (
   User,
   user.email,
   message= ' There is already ' and that email. ')]
 Password = Passwordfield (' Password ', validators=[datarequired ())

Render a form
Wtforms can also help us render HTML representations of forms. The field fields implemented by Wtforms can be rendered as HTML representations of the fields, so in order to render them, we only have to call the fields of the form in our template. This is like rendering csrf_token fields. Here is an example of a login template where we use wtforms to render our fields.

{# ourapp/templates/login.html #}

{% extends ' layout.html '%}
 
 

We can customize how the fields are rendered, passing the properties of the fields as arguments to the call.

<form action= "" method= "post" >
 {Form.email.label}}: {{form.email (placeholder= ' yourname@email.com ')}}<  c17/><br>
 {% raw%}{{Form.password.label}}: {{Form.password}}{% endraw%}
 <br>
 {% raw%}{{ Form.csrf_token}}{% endraw%}
</form>

Process OpenID Login
in real life, we find that a lot of people don't know they have some public accounts. A number of big-name websites or service providers will provide their members with a public account certification. A chestnut, if you have a Google account, you actually have a public account, similar to Yahoo, AOL, Flickr and so on.
To make it easier for our users to use their public accounts simply, we will add links to these public accounts to a list so that users do not have to enter them manually.

We're going to define some of the public service providers that are available to the user into a list, and the list will be in the configuration file (fileconfig.py):

csrf_enabled = True
secret_key = ' you-will-never-guess '
 
openid_providers = [
 {' name ': ' Google ', ' url ': ' Https://www.google.com/accounts/o8/id '},
 {' name ': ' Yahoo ', ' url ': ' https://me.yahoo.com '},
 {' name ': ' AOL ', ' URL ': ' http://openid.aol.com/<username> '},
 {' name ': ' Flickr ', ' url ': ' http://www.flickr.com/< Username> '},
 {' name ': ' Myopenid ', ' url ': ' https://www.myopenid.com '}]

The next step is to use this list in our login view function:

@app. Route ('/login ', methods = [' Get ', ' POST '])
def login ():
 form = LoginForm ()
 if Form.validate_on_submit ():
  Flash (' Login requested for openid= "' + Form.openid.data + '", remember_me= ' + str (form.remember_me.data))
  Return redirect ('/index ') return
 render_template (' login.html ',
  title = ' Sign in ',
  form = form,
  providers = app.config[' Openid_providers '])

We introduced the configuration list of the public account service provider from the app.config, then introduced it as a parameter through the Render_template function to the template.

The next thing I want you to guess is that we need to display these service links in our login template.

<!--extend Base layout--> {% extends "base.html"%} {% block content%} <script type= "Text/javascript" > F Unction Set_openid (OpenID, pr) {u = openid.search (' <username> ') if (U!=-1) {/OpenID requires username us
 ER = prompt (' Enter your ' + PR + ' username: ') OpenID = openid.substr (0, u) + user} form = document.forms[' login ']; form.elements[' OpenID '].value = OpenID} </script>  

This time the template adds something that seems a little more. Some public accounts need to provide user name, in order to solve this we use a bit of JavaScript. When the user clicks on the relevant public account link, the user name of the public account will prompt the user to enter the user name, JavaScript will be processed into the user name available public account, and finally inserted into the OpenID field text box.

Here is the screenshot shown after clicking on the Google link in the login page:

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.