Skip to main content

Overview

Secure authentication is the foundation of application security. This guide demonstrates best practices based on the secure implementation in the Auth Security Demo.

Password Security

Password Hashing with Werkzeug

Never store passwords in plain text. Always use strong hashing algorithms like bcrypt.
1

Install Werkzeug

Werkzeug provides secure password hashing utilities:
pip install Werkzeug==3.0.0
2

Hash passwords during registration

Use generate_password_hash() to securely hash passwords:
secure/app.py
from werkzeug.security import generate_password_hash

# During registration
password = request.form.get('password', '')
hashed_password = generate_password_hash(password)

# Store hashed_password in database
query = "INSERT INTO users (username, password, email) VALUES (%s, %s, %s)"
cursor.execute(query, (username, hashed_password, email))
3

Verify passwords during login

Use check_password_hash() to verify credentials:
secure/app.py
from werkzeug.security import check_password_hash

# During login
query = "SELECT * FROM users WHERE username = %s"
cursor.execute(query, (username,))
user = cursor.fetchone()

if user and check_password_hash(user['password'], password):
    # Login successful
    session['user_id'] = user['id']
    session['username'] = user['username']
Werkzeug’s generate_password_hash() uses pbkdf2:sha256 by default, which is secure and recommended for most applications.

Session Management

Secure Session Configuration

Proper session management prevents session hijacking and fixation attacks.
secure/app.py
import os
from flask import Flask, session
from dotenv import load_dotenv

load_dotenv()

app = Flask(__name__)
# Use a strong, random secret key from environment variables
app.secret_key = os.getenv('SECRET_KEY')

Session Storage

Store minimal user data in sessions:
  • User ID
  • Username
  • Role/permissions

Session Validation

Always validate session data:
  • Check if user_id exists
  • Verify user still has access
  • Validate role permissions

Session Creation Example

secure/app.py
if user and check_password_hash(user['password'], password):
    session['user_id'] = user['id']
    session['username'] = user['username']
    session['role'] = user['role']
    session.permanent = True  # Use permanent session cookies
    flash('Login exitoso!', 'success')
    return redirect('/dashboard')

Authentication Checks

Protecting Routes

Always verify authentication before allowing access to protected resources.
secure/app.py
@app.route('/dashboard')
def dashboard():
    # Verify user is authenticated
    if 'user_id' not in session:
        flash('Debes iniciar sesión', 'warning')
        return redirect('/login')
    
    return render_template('dashboard.html', 
                         username=session.get('username'),
                         role=session.get('role'))
For production applications, consider creating a decorator like @login_required to avoid repeating authentication checks in every route.

Login Best Practices

Generic Error Messages

Use generic messages like “Invalid credentials” instead of “User not found” or “Wrong password” to prevent username enumeration.

Rate Limiting

Implement rate limiting to prevent brute-force attacks on login endpoints.

Secure Logout

Clear all session data on logout to prevent session reuse.

Password Requirements

Enforce minimum password length and complexity requirements during registration.

Secure Logout Implementation

secure/app.py
@app.route('/logout')
def logout():
    session.clear()  # Clear all session data
    flash('Sesión cerrada correctamente', 'success')
    return redirect('/')

Password Requirements

Enforce strong password policies during registration:
secure/app.py
if len(password) < 6:
    flash('La contraseña debe tener al menos 6 caracteres', 'danger')
    return render_template('register.html')
Consider implementing more robust password validation:
  • Minimum 8-12 characters
  • Mix of uppercase and lowercase letters
  • At least one number and special character
  • Check against common password lists

Environment Variables

Store sensitive configuration in environment variables, never in code:
from dotenv import load_dotenv
import os

load_dotenv()

app.secret_key = os.getenv('SECRET_KEY')
database_url = os.getenv('DATABASE_URL')
Generate strong secret keys using: python -c "import secrets; print(secrets.token_hex(32))"

Next Steps

Input Validation

Learn about validating and sanitizing user input

Access Control

Implement proper authorization checks

Build docs developers (and LLMs) love