TIL Python Basics Day 68 - Authentication with Flask

이다연·2021년 3월 1일
0

Udemy Python Course

목록 보기
64/64

Figure out how to register, login and logout users with email and password. So they can access their own private profile pages.

Send from directory

The send_from_directory function is the recommended secure way to allow a user to download a file from our application.

Docs
Helpful Article

@app.route("/get-image/<image_name>")
def get_image(image_name):

    try:
        return send_from_directory(app.config["CLIENT_IMAGES"],
        filename=image_name, as_attachment=True)
    except FileNotFoundError:
        abort(404)

-app.config["CLIENT_IMAGES"] - The path to the directory containing the images we're allowing our users to download
-filename=image_name - The image_name variable passed in from the URL
-as_attachment=True - Allows the client to download the file as an attachment
-send_from_directory is then returned

@app.route('/download')
def download():
    return send_from_directory('static', 
    			filename='files/cheat_sheet.pdf')

Authentication

Why Authenticate?: to associate data with individual users.

  1. Access to private contents like messages
  2. Restrict Acess depending on user's status: paid membership

* Level 1 Encryption: store password into DB.
* Level 2 Authentication: Use of encryption
Scrambling it to be unable to decode data without anigma machine/ key

Caesar Cipher: early form of encryption

A way of scrambling message
Key to unscramble the msg

* Level 3 Hashing:

Password  + Key -> Ciphertext
              Method

Hash Function

Not using Key
We don’t decrypt the password

Password turned into Hash
Hash doesn’t go backward into original password.

377/1 = 13*29
13*29 

Calculated very fast, but doesn’t go backward

Store hash: When try to login again, send Hash to the DB, DB compare it to stored Hash.
If those two matches, they are same.
In that way, only users know their password.

How to Hack Password 101

'Have been pwned?'

Same password -> Same Hash

Generating hashes with GPU

Hash Table Cracks

Pre-built hash table: Search by the hash value of common password

Longer the password, harder to crack: Time to crack exponentially increase.
That's why include all Uppercase, Lowercase, Numbers, Symbols over 10 characters.

Dictionary Attack

don’t use dictionary words

Salting Passwords

* Level 4 : Hashing & Salting

In addition to the password + random set of character (SALT) increases number of character.

Salt it stored in the DB along with Hash
Combine with PW+Salt
We only store Salt and Hash

MD5 is less secure

Bcrypt Hashes

Industry standard password-hashing function

Salt Rounds

As computers get faster, almost doubles the computing power.
You can increse the rounds exponentially. It gets securer.

Round 1
PW + SALT => Hash1
     (Random set of characters)
     
Round 2
Hash1 + SALT => Hash2

Round 3
Hash2 + SALT => Hash3
...

DB
User/ Salt/ Hash x 10(rounds)

*level 5: Cookies and Sessions
Cookie
-Amazon stores the cookies, session contains id number to be used to fetch all data. If we delete the cookie, we won't be able to see the item in the cart.
-At Amazon, Adding an item in a shopping cart means "Browser POST request to server; I added item to my cart." In response, server creates the cookies containing the data and send it to browser, telling save the coockie in. When browser(user)comeback to the website, browser make GET request, sending along the cookie to server. Server responses by sending html, css, js and cookie.

There are lots of different type of cookies.
Session: a period of time when a browser interacts with a server. When logged in, cookie is created and expires after a certain period of time.


1. Hashing Passwords using Werkzeug

generate_password_hash()
        hash_and_salted_password = generate_password_hash(
            request.form.get('password'),
            method='pbkdf2:sha256',
            salt_length=8
        )
        new_user = User(
            email=request.form.get('email'),
            name=request.form.get('name'),
            password=hash_and_salted_password,
        )

Security Helpers
Docs

2. Authenticating Users with Flask-Login

Flask-Login package provides user session management for Flask. It handles the common tasks of logging in, logging out, and remembering your users’ sessions over extended periods of time.

Youtube tutorial by pretty print

1. configure Flask app to use Flask_Login

login_manager = LoginManager()
login_manager.init_app(app)

2. create a user_loader function.

@login_manager.user_loader
def load_user(user_id):
    """to associate the coockie with user object"""
    return User.query.get(int(user_id))

-user_loader: How Flask_Login creates the connection btw cookie and data in DB.

-flask login uses user_loader to find whose session is currently active. Flask login will create a cookie when the used is logged in and inside the cookie, will be the 'user id'.
Anytime user performs a request on your app it sends the cookie along with that. Flask looks for the user id and usees user_loader to actually find the user in DB.

3. Implement the UserMixin in User class.

from flask_login import UserMixin

class User(UserMixin, db.Model):

-Without UserMinxin, Flask_Login cannot read or modify User class.

-UserMixin adds some attritubes to User class to help Flask_login actually use your User Class.

4. check_password_hash function.

Check stored password hash against entered password hashed.
#(pw from DB, user typed pw-plain text)

#1 check only password
password = request.form.get('password')
if check_password_hash(user.password, password):
            login_user(user)
    
    
    
#2 check both email in DB and password
email = request.form.get('email')
password = request.form.get('password')
user = User.query.filter_by(email=email).first()
if not user and not check_password_hash(user.password, password): 
            return redirect(url_for("login"))	
      login_user(user)      

5. Find the user by the email

When try to register but user email already on DB, redirect to login

When try to login, find the user by email

email = request.form.get('email')

user = User.query.filter_by(email=email).first()

6. login_user(user) function to authenticate them.

function creates the cookcie . session tells flask that user is logged in

7. Login_required

-login_required secure the page, only logged in user can view the page.
-current_user object represents user object.

e.g. Secure the /secrets and /download route

@app.route('/secrets')
@login_required
def secrets():
    return render_template("secrets.html", name=current_user.name)

secrets.html

  <h1 class="title">Welcome, {{ name }}  </h1>

8. Log out

Users aren't logged in shouldn't do logout, so login_required

@app.route('/logout')
@login_required 
def logout():
    logout_user()
    return redirect(url_for('index.html'))

Flask Flash messages

Give user a feedback
e.g. Was there an issue with login in? Are they typing in the wrong password or does their email not exist?

-They are messages that get sent to the template to be rendered just once. And they disappear when the page is reloaded.

When tried to register with existing email at register page, redirects to login and show flash saying Email already esits.(so try loggingin)

main.py

if user:
        flash("Email address already exists. ")
        return redirect(url_for('login'))

login.html

    <h1>Login</h1>
    {% with messages = get_flashed_messages() %}
      {% if messages %}
        {% for message in messages %}
         <p>{{ message }}</p>  
        {% endfor %}
      {% endif %}
    {% endwith %}

Passing Authentication Status to Templates

nav bar -> base.html

 {% if not current_user.is_authenticated %}
     <li class="nav-item">
        <a class="nav-link" href="{{ url_for('home') }}">Home</a>
      </li>
        {% if not current_user.is_authenticated %}
      <li class="nav-item">
        <a class="nav-link" href="{{ url_for('login') }}">Login</a>
      </li>
        <li class="nav-item">
        <a class="nav-link" href="{{ url_for('register') }}">Register</a>
      </li>
        {% endif %}
<li class="nav-item">
        <a class="nav-link" href="{{ url_for('logout') }}">Log Out</a>
      </li>

base.html changes after logging in

profile
Dayeon Lee | Django & Python Web Developer

0개의 댓글