Implement Web login validation using Python's Flask framework form plug-in FLASK-WTF

Source: Internet
Author: User
Tags openid csrf attack
Forms are the basic elements that let 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 application. This package makes it easier to define forms and handle submissions.

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

# ourapp/forms.pyfrom FLASK_WTF Import formfrom wtforms import Stringfield, passwordfieldfrom wtforms.validators import D atarequired, Emailclass emailpasswordform (Form): email = stringfield (' email ', validators=[datarequired (), email ()]) Password = Passwordfield (' Password ', validators=[datarequired ()])

Prior to the FLASK-WTF 0.9 release, FLASK-WTF provided its own encapsulation for wtforms fields and validators. You may see a whole bunch of code out there that is not imported from the flask.ext.wtforms from the Wtforms Textfield,passwordfield.
After the FLASK-WTF 0.9 release, we should import these fields and validators directly from the 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 something else, like a registration 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. A particularly complex form may be defined later in a formal project. For a form that contains field names, we recommend that you use a clear name and remain unique within a form. Have to say, for a long form, we might want to give a field name that is more consistent with the above.

Login form can do something for us. It ensures the security of our applications to prevent CSRF vulnerabilities, validates user input and renders appropriate tags, which are the fields we define for the form.

CSRF Protection and verification
CSRF indicates cross-site request forgery. A CSRF attack is a third-party forgery (like a form submission) that requests a server to an application. A vulnerable server assumes that the data from a form is coming from its own website and taking appropriate action.

As an example, let's say that a mail provider allows 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 logged-in account when the form is submitted. We can create a form on our own website that sends a POST request to the same Account_delete endpoint. Now, if we let someone click on the submit button of our form (or do this through JavaScript), the login account provided by the email provider will be deleted. Of course, email providers don't yet know that form submissions do not occur on their sites.

So how can I block POST requests from other sites? Wtforms is made possible by generating a unique token when rendering each form. The generated token is passed back to the server, and with the data of the POST request, the token must accept authentication from the server before the form is accepted. The key is that the token is related to a value stored in the user session (cookie) and will expire after a period of time (default is 30 minutes). This way, it is guaranteed that the person who submits a valid form is the person who loaded the page (or at least the person who is using the same computer), and they can only do so within 30 minutes of loading the page.

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

# Ourapp/views.pyfrom Flask Import render_template, redirect, Url_forfrom. Import appfrom. Forms Import Emailpasswordform@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 already been submitted and verified, we can continue the logic of login. If it is not committed (for example, just a GET request), we are going to 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%}   Login Page     

{% RAW%} {{Form.csrf_token}} {% Endraw%} renders a hidden field that contains those fancy CSRF tokens and looks for this field when wtforms validates the form. We don't have to worry about the logic that involves handling tokens, wtforms will do it for us. Well, 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 demonstrate how to create our own validators by writing a unique () validator, which is used to check the database and ensure that the values provided by the user 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 about the form itself.

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

# ourapp/forms.pyfrom FLASK_WTF Import formfrom wtforms import Stringfield, passwordfieldfrom wtforms.validators import D atarequired, Emailclass emailpasswordform (Form): email = stringfield (' email ', validators=[datarequired (), email ()]) Password = Passwordfield (' Password ', validators=[datarequired ()])

Now we are going to add our validators to make sure that the mailbox addresses they provide do not exist in the database. We put this authenticator in a new util module, util.validators.

# ourapp/util/validators.pyfrom wtforms.validators Import Validationerrorclass Unique (object): Def __init__ (self, Model, field, Message=u ' this element already exists. '):  Self.model = model  Self.field = Field def __call__ (self, F ORM, field):  check = self.model.query.filter (Self.field = = Field.data). First ()  if check:   raise ValidationError (Self.message)

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

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

Now we can modify the Emailpasswordform, using our custom Unique validator.

# ourapp/forms.pyfrom FLASK_WTF Import formfrom wtforms import Stringfield, passwordfieldfrom wtforms.validators import D Atarequiredfrom. Util.validators import uniquefrom. Models import userclass Emailpasswordform (Form): email = Stringfield (' Email ', validators=[datarequired (), email (),  Unique (   User,   user.email,   message= ' there is already An (with that email. ')]) Password = Passwordfield (' Password ', validators=[datarequired ()])

Render Form
Wtforms can also help us render the form as an HTML representation. 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 invoke the form's fields in our template. This is like rendering the Csrf_token field. An example of a login template is given below, where we use wtforms to render our fields.

{# ourapp/templates/login.html #} {% extends "layout.html"%}   Login Page     

We can customize how the fields are rendered by passing in the properties of the field as arguments into the call.

Handling OpenID Login
In real life, we find that many people do not know that they have some public accounts. Some big-names websites or service providers will provide their members with a public account certification. For 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, we will add links to these public accounts to a list so that users do not have to enter them manually.

We are going to define some of the public account providers that are available to the user in a list, and the list is in the config file (fileconfig.py):

csrf_enabled = Truesecret_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 /
 
  
   
  '}, {' name ': ' Flickr ', ' url ': ' http://www.flickr.com/
  
   
    
   '}, {' name ': ' Myopenid ', ' url ': ' Htt Ps://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 provider from App. Config and then introduced it as a parameter to the template using the Render_template function.

Next thing to do I think you can guess, we need to show these providers in the login template.

 
  {% extends "base.html"%} {% block content%}

Sign In

{% Endblock%}

This time the template adds something that seems a bit more. Some public accounts need to provide user names, in order to solve this we used a bit of JavaScript. When the user clicks on the relevant public account link, the public account that requires the user name prompts the user for the user name, and JavaScript processes the user name into a usable public account, and then inserts it into the OpenID field's text box.

The following is shown on the login page after clicking on the Google Link:

  • 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.