tylerjrichards / st-paywall

A python package for creating subscription Streamlit apps
MIT License
235 stars 46 forks source link

Cookie Functions in Streamlit Application - managing user sessions and authentication using Google OAuth #53

Open KazeroG opened 6 months ago

KazeroG commented 6 months ago

Analysis of Cookie Functions in Streamlit Application

The provided code snippet includes functions for handling cookies in a Streamlit web application. These functions are part of a system managing user sessions and authentication using Google OAuth. Below is a breakdown of the key functions:

1. set_cookie_js(hash)

Purpose: To store a session identifier as a cookie in the user's browser.

How It Works:

2. get_cookie_js()

Purpose: To extract the session cookie value and use it in the app.

How It Works:

3. get_cookie_from_query_params()

Purpose: To obtain the session identifier from the URL's query parameters.

How It Works:

Overall Workflow

  1. Setting the Cookie: set_cookie_js is called with a hash to set a browser cookie on login or session creation.
  2. Retrieving the Cookie: get_cookie_js passes the cookie value back to the server via URL query parameters.
  3. Utilizing the Cookie: get_cookie_from_query_params retrieves the session value for server-side processing.

These functions are crucial for managing user sessions in a web application, ensuring secure and effective user state management.

tylerjrichards commented 5 months ago

Hey @KazeroG, if a user manually sets their cookie, will they be able to get access to someone else's subscriptions? that's the main concern here

KazeroG commented 5 months ago

Hi @tylerjrichards

The application generates a unique session hash using uuid.uuid4().hex and stores this hash along with the user's email in a mock database (user_sessions_db). This hash is then set as a cookie in the user's browser using a JavaScript snippet.When attempting to retrieve the logged-in user's email, the application first checks for a session hash within the query parameters. If present, it validates this hash against the mock database to retrieve the associated email. but the application sets the cookie without specifying the HttpOnly attribute, which means the cookie is accessible via JavaScript. This is a potential security risk because it makes the cookie vulnerable to client-side scripts, including those injected through XSS (Cross-Site Scripting) attacks.

If a user manually sets their cookie to a session hash that belongs to another user, and if that session hash is valid and present in the user_sessions_db, then yes, they could potentially access the application as if they were the other user. This is because the application's security relies on the uniqueness and secrecy of the session hash.

indeed the lack of HttpOnly flag and the reliance on client-side cookie management without additional safeguards can indeed allow a user to manually set their cookie to access someone else's session if they can obtain or guess the session hash.

the solution maybe :

response.set_cookie('session', hash, httponly=True, secure=True, samesite='Lax')

and use a secure, server-side session management system using Flask for handling the OAuth callback and session cookie setting :

from flask import Flask, request, redirect, make_response
import uuid

app = Flask(__name__)

@app.route('/oauth/callback')
def oauth_callback():
    # This endpoint would handle the OAuth callback, perform authentication, and then set an HttpOnly cookie
    session_hash = uuid.uuid4().hex  # Generate a secure session hash
    response = make_response(redirect('your_streamlit_app_url'))
    response.set_cookie('session', session_hash, httponly=True, secure=True, samesite='Strict')
    # Store session_hash and associated user info securely on the server
    return response

if __name__ == '__main__':
    app.run(ssl_context='adhoc')  # Use HTTPS

Why using using Flask ?

thats because Streamlit's architecture is primarily client-focused, and direct server-side session management, including setting HttpOnly cookies, is not natively supported. Streamlit's session state can be used to manage user sessions to a certain extent. While it doesn't replace HttpOnly cookies for security, it can hold session data during the user's interaction with the application.

This method stores session information in the browser session, but it's not as secure as HttpOnly cookies since it's more susceptible to XSS attacks.

if 'user_session' not in st.session_state:
    st.session_state['user_session'] = generate_user_session_hash()  # Securely generate and store session hash
avandvik commented 3 months ago

Any updates on whether/when cookies will be enabled so the user does not have to authenticate on refresh?