TL;DR
Instead of a password, users answer randomly selected security questions to log in. This guide explains how to set it up using readily available tools and techniques. It’s more secure than simple passwords but requires careful question selection and implementation.
How to Implement Question-Based Authentication
- Choose a Suitable System: You’ll need a way to store questions, answers (securely hashed!), and manage user accounts. Options include:
- Existing Identity Provider (IdP): Many IdPs (like Okta, Auth0) offer custom authentication flows that can be adapted for question-based methods. This is the easiest route if you already use one.
- Custom Application: If building from scratch, choose a secure programming language and framework (Python with Django/Flask, Node.js with Express).
- Question Selection & Storage:
- Number of Questions: Aim for at least 10-20 questions per user to make brute-forcing harder.
- Question Diversity: Mix factual, personal, and slightly obscure questions. Avoid easily Googleable answers (e.g., “What’s your mother’s maiden name?”).
- Database Design: Store questions separately from answers. A simple table structure might look like this:
CREATE TABLE users ( id INT PRIMARY KEY, username VARCHAR(255) UNIQUE NOT NULL, hashed_answer1 VARCHAR(255) NOT NULL, hashed_answer2 VARCHAR(255) NOT NULL, ... ); CREATE TABLE questions ( question_id INT PRIMARY KEY, user_id INT NOT NULL, question_text TEXT NOT NULL, FOREIGN KEY (user_id) REFERENCES users(id) );
- Secure Hashing of Answers: Never store answers in plain text! Use a strong hashing algorithm like bcrypt or Argon2.
- Python Example (bcrypt):
import bcrypt pwd = 'mysecretanswer'.encode('utf-8') hashed_pwd = bcrypt.hashpw(pwd, bcrypt.gensalt()) print(hashed_pwd) # Store this in your database, not the original answer!
- Python Example (bcrypt):
- Authentication Flow:
- Random Question Selection: When a user logs in, randomly choose 3-5 questions from their stored question set.
# Example Python (using random module) import random question_ids = [1, 2, 3, 4, 5] # User's question IDs selected_questions = random.sample(question_ids, 3) # Choose 3 questions - Present Questions to User: Display the selected questions in a login form.
- Answer Verification: Compare the user’s input answers (after hashing them with the same algorithm used for storage) against the stored hashes. All answers must match for successful authentication.
import bcrypt user_provided_answer = 'mysecretanswer'.encode('utf-8') hashed_user_answer = bcrypt.hashpw(user_provided_answer, bcrypt.gensalt()) if hashed_user_answer == stored_hashed_answer: # Authentication successful! else: # Authentication failed.
- Random Question Selection: When a user logs in, randomly choose 3-5 questions from their stored question set.
- Account Recovery: Implement a secure account recovery process. This could involve email verification, secondary phone number confirmation, or pre-defined security phrases.
- Rate Limiting & Brute Force Protection: Limit the number of failed login attempts per user/IP address to prevent attackers from guessing answers.
- Regular Security Audits: Periodically review your implementation for vulnerabilities and update questions as needed.
Important Considerations
- Question Quality is Key: Poorly chosen questions are easily compromised.
- Answer Entropy: Encourage users to provide complex, non-obvious answers.
- Multi-Factor Authentication (MFA): Combine question-based authentication with another factor (e.g., a one-time code sent via SMS) for increased security.

