Skip to main content
This application has no session management whatsoever, demonstrating one of the most critical security flaws possible in a web application.

Overview

Session management is the process of securely handling multiple requests to a web application from the same user. Proper session management is critical for maintaining user authentication state and protecting user data. The Normo Unsecure PWA demonstrates what happens when an application has zero session management - every page load is treated as a new, unauthenticated request.

Critical Session Management Flaws

No Session Tracking

The application does not implement any session tracking mechanism:
main.py
@app.route("/", methods=["POST"])
def home():
    if request.method == "POST":
        username = request.form["username"]
        password = request.form["password"]
        isLoggedIn = dbHandler.retrieveUsers(username, password)
        if isLoggedIn:
            dbHandler.listFeedback()
            # User is "authenticated" but NO SESSION IS CREATED
            return render_template("/success.html", value=username, state=isLoggedIn)
        else:
            return render_template("/index.html")
Notice that after successful authentication, the application simply renders a template. There’s no session cookie, no token, no way to remember the user on subsequent requests.

No Logout Functionality

There is no logout endpoint in main.py. Users cannot explicitly end their session because there is no session to end:
main.py
# No logout route exists!
# Expected but missing:
# @app.route("/logout")
# def logout():
#     session.clear()
#     return redirect("/")

No Authentication Required for Protected Pages

The /success.html endpoint accepts GET requests without any authentication:
main.py
@app.route("/success.html", methods=["POST", "GET", "PUT", "PATCH", "DELETE"])
def addFeedback():
    if request.method == "POST":
        feedback = request.form["feedback"]
        dbHandler.insertFeedback(feedback)
        dbHandler.listFeedback()
        return render_template("/success.html", state=True, value="Back")
    else:
        # Anyone can GET this page without logging in!
        dbHandler.listFeedback()
        return render_template("/success.html", state=True, value="Back")

Common Session Management Vulnerabilities

Missing Session Mechanism

Problem: Application doesn’t create or maintain sessions after authentication.Impact:
  • Users can’t stay logged in
  • Every request requires re-authentication
  • No way to track user state across requests
  • Protected resources are not actually protected
Found in: main.py:46-67

Real-World Attack Scenarios

1

Direct Access to Protected Pages

Because there’s no session management, an attacker can directly navigate to “protected” pages:
# No authentication required!
curl http://localhost:5000/success.html
The application will render the page and display all feedback without checking if the user is authenticated.
2

State Manipulation

Even if a user “logs in”, clicking any link or refreshing the page loses their authentication state:
1. User logs in successfully
2. User clicks a link or refreshes
3. User is no longer "logged in" (no session to maintain state)
4. User must log in again
3

No Access Control

Without sessions, there’s no way to implement proper access control:
# Can't check if user is authenticated
# Can't check user roles or permissions
# Can't track user activity
# Can't implement per-user features

Implementing Secure Session Management

Option 1: Flask-Session (Server-Side)

Flask-Session provides secure, server-side session management:
pip install Flask-Session

Option 2: Flask-Login (User Session Management)

Flask-Login provides user session management with more features:
pip install Flask-Login

Session Security Best Practices

1. Session Configuration Checklist

  • Generate strong session IDs - Use cryptographically secure random values
  • Set HttpOnly flag - Prevents JavaScript from accessing cookies
  • Set Secure flag - Ensures cookies only sent over HTTPS
  • Set SameSite attribute - Protects against CSRF attacks
  • Implement session timeout - Auto-logout after inactivity
  • Regenerate session ID - After login to prevent fixation
  • Clear sessions on logout - Properly invalidate sessions

2. Session Timeout Implementation

from datetime import datetime, timedelta

@app.before_request
def check_session_timeout():
    if 'logged_in' in session:
        last_activity = session.get('last_activity')
        if last_activity:
            last_activity = datetime.fromisoformat(last_activity)
            if datetime.now() - last_activity > timedelta(minutes=30):
                session.clear()
                return redirect(url_for('home', msg='Session expired'))
        
        session['last_activity'] = datetime.now().isoformat()

3. Concurrent Session Management

import uuid

@app.route("/", methods=["POST"])
def home():
    username = request.form["username"]
    password = request.form["password"]
    
    if dbHandler.retrieveUsers(username, password):
        # Generate unique session identifier
        session_id = str(uuid.uuid4())
        
        # Store in database to track concurrent sessions
        dbHandler.create_session(username, session_id)
        
        session['session_id'] = session_id
        session['username'] = username
        
        return redirect('/success.html')

4. Session Monitoring and Logging

import logging

@app.before_request
def log_session_activity():
    if 'logged_in' in session:
        logging.info(f"User {session.get('username')} accessed {request.path} from {request.remote_addr}")

@app.route("/logout")
def logout():
    username = session.get('username', 'Unknown')
    logging.info(f"User {username} logged out from {request.remote_addr}")
    session.clear()
    return redirect('/')

Testing Session Management

1

Test Session Creation

Verify that sessions are created upon login:
import requests

session = requests.Session()
response = session.post('http://localhost:5000/', 
    data={'username': 'testuser', 'password': 'password'})

# Check if session cookie is set
print(session.cookies)
2

Test Session Persistence

Verify that authenticated state persists across requests:
# After login, try accessing protected page
response = session.get('http://localhost:5000/success.html')
assert response.status_code == 200
3

Test Session Invalidation

Verify that logout properly invalidates sessions:
# Logout
session.get('http://localhost:5000/logout')

# Try accessing protected page
response = session.get('http://localhost:5000/success.html')
assert response.status_code == 302  # Redirected to login
4

Test Session Timeout

Verify that sessions expire after inactivity:
import time

# Login
session.post('http://localhost:5000/', data={'username': 'test', 'password': 'test'})

# Wait for timeout period
time.sleep(1800)  # 30 minutes

# Try accessing protected page
response = session.get('http://localhost:5000/success.html')
assert 'Session expired' in response.text

References

Build docs developers (and LLMs) love