Flask: Password Hashing with Flask-Bcrypt
Password hashing is a critical security practice in Flask web applications to protect user credentials. Using Flask-Bcrypt, Flask integrates seamlessly with Flask Authentication, Flask User Registration, Flask User Login and Logout, Flask Querying the Database, and Flask Relationships in Models to securely store and verify passwords. This tutorial explores Flask password hashing with Flask-Bcrypt, covering secure password storage, verification, and integration with authentication workflows, with practical applications in web development.
01. Why Use Flask-Bcrypt for Password Hashing?
Storing plain-text passwords is a major security risk, as data breaches can expose user credentials. Flask-Bcrypt uses the bcrypt algorithm to generate secure, salted password hashes, ensuring that even if a database is compromised, passwords remain protected. Combined with Flask’s lightweight framework and NumPy Array Operations for efficient database interactions, Flask-Bcrypt provides a robust solution for secure user authentication in applications like blogs, e-commerce platforms, or dashboards.
Example: Basic Password Hashing
from flask import Flask, request
from flask_sqlalchemy import SQLAlchemy
from flask_bcrypt import Bcrypt
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///hash.db'
app.config['SECRET_KEY'] = 'your-secret-key'
db = SQLAlchemy(app)
bcrypt = Bcrypt(app)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
password_hash = db.Column(db.String(120), nullable=False)
@app.route('/register', methods=['POST'])
def register():
username = request.form['username']
password = request.form['password']
hashed_password = bcrypt.generate_password_hash(password).decode('utf-8')
user = User(username=username, password_hash=hashed_password)
db.session.add(user)
db.session.commit()
return "User registered with hashed password"
with app.app_context():
db.create_all()
Output:
User registered with hashed password (on valid POST request)
Explanation:
bcrypt.generate_password_hash
- Creates a secure, salted hash of the password.decode('utf-8')
- Converts the hash to a string for database storage.
02. Key Password Hashing Techniques
Flask-Bcrypt provides essential methods for secure password hashing and verification. Below is a summary of key techniques and their applications in web applications:
Technique | Description | Use Case |
---|---|---|
Password Hashing | Generate secure password hashes | Store passwords during registration |
Password Verification | Check passwords against hashes | Authenticate users during login |
Integration with Models | Store hashes in database models | Secure user data |
Validation | Ensure strong passwords | Enforce password policies |
Error Handling | Manage hashing/verification errors | Prevent security vulnerabilities |
2.1 Password Hashing
Example: Hashing Passwords During Registration
from flask import Flask, request
from flask_sqlalchemy import SQLAlchemy
from flask_bcrypt import Bcrypt
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///register.db'
app.config['SECRET_KEY'] = 'your-secret-key'
db = SQLAlchemy(app)
bcrypt = Bcrypt(app)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
password_hash = db.Column(db.String(120), nullable=False)
@app.route('/register', methods=['POST'])
def register():
username = request.form['username']
password = request.form['password']
if User.query.filter_by(username=username).first():
return "Username already exists"
hashed_password = bcrypt.generate_password_hash(password).decode('utf-8')
user = User(username=username, password_hash=hashed_password)
db.session.add(user)
db.session.commit()
return "Registration successful"
with app.app_context():
db.create_all()
Output:
Registration successful (on valid POST request with unique username)
Explanation:
generate_password_hash
- Produces a bcrypt hash with a random salt.- Checks for duplicate usernames before hashing.
2.2 Password Verification
Example: Verifying Passwords During Login
from flask import Flask, request
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager, UserMixin, login_user
from flask_bcrypt import Bcrypt
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///login.db'
app.config['SECRET_KEY'] = 'your-secret-key'
db = SQLAlchemy(app)
bcrypt = Bcrypt(app)
login_manager = LoginManager(app)
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
password_hash = db.Column(db.String(120), nullable=False)
@login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
@app.route('/login', methods=['POST'])
def login():
username = request.form['username']
password = request.form['password']
user = User.query.filter_by(username=username).first()
if user and bcrypt.check_password_hash(user.password_hash, password):
login_user(user)
return "Login successful"
return "Invalid credentials"
with app.app_context():
db.create_all()
hashed_password = bcrypt.generate_password_hash('securepass').decode('utf-8')
db.session.add(User(username='Alice', password_hash=hashed_password))
db.session.commit()
Output:
Login successful (on valid POST request with username='Alice' and password='securepass')
Explanation:
check_password_hash
- Compares the provided password with the stored hash.- Integrates with Flask User Login and Logout for secure authentication.
2.3 Integration with Models
Example: Storing Hashes in Models
from flask import Flask, request
from flask_sqlalchemy import SQLAlchemy
from flask_bcrypt import Bcrypt
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///models.db'
app.config['SECRET_KEY'] = 'your-secret-key'
db = SQLAlchemy(app)
bcrypt = Bcrypt(app)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
password_hash = db.Column(db.String(120), nullable=False)
posts = db.relationship('Post', backref='author', lazy=True)
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(100), nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
@app.route('/register', methods=['POST'])
def register():
username = request.form['username']
email = request.form['email']
password = request.form['password']
if User.query.filter_by(username=username).first() or User.query.filter_by(email=email).first():
return "Username or email already exists"
hashed_password = bcrypt.generate_password_hash(password).decode('utf-8')
user = User(username=username, email=email, password_hash=hashed_password)
db.session.add(user)
db.session.commit()
return "Registered"
with app.app_context():
db.create_all()
Output:
Registered (on valid POST request with unique username and email)
Explanation:
password_hash
- Stores the bcrypt hash in theUser
model.- Integrates with Flask Relationships in Models to link users to posts.
2.4 Password Validation
Example: Enforcing Strong Passwords
from flask import Flask, request
from flask_sqlalchemy import SQLAlchemy
from flask_bcrypt import Bcrypt
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///validate.db'
app.config['SECRET_KEY'] = 'your-secret-key'
db = SQLAlchemy(app)
bcrypt = Bcrypt(app)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
password_hash = db.Column(db.String(120), nullable=False)
@app.route('/register', methods=['POST'])
def register():
username = request.form['username']
password = request.form['password']
if not password or len(password) < 8:
return "Password must be at least 8 characters"
if User.query.filter_by(username=username).first():
return "Username already exists"
hashed_password = bcrypt.generate_password_hash(password).decode('utf-8')
user = User(username=username, password_hash=hashed_password)
db.session.add(user)
db.session.commit()
return "Registration successful"
with app.app_context():
db.create_all()
Output:
Registration successful (on valid POST request with password >= 8 characters)
Explanation:
- Enforces a minimum password length to ensure stronger passwords.
- Combines with Flask User Registration for secure onboarding.
2.5 Incorrect Password Handling
Example: Storing Plain-Text Passwords
from flask import Flask, request
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///insecure.db'
db = SQLAlchemy(app)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
password = db.Column(db.String(120), nullable=False) # Plain text
@app.route('/register', methods=['POST'])
def register():
username = request.form['username']
password = request.form['password']
user = User(username=username, password=password) # Insecure
db.session.add(user)
db.session.commit()
return "Registered"
with app.app_context():
db.create_all()
Output:
Registered (but with insecure plain-text password)
Explanation:
- Storing plain-text passwords exposes credentials in a breach.
- Solution: Use
Flask-Bcrypt
for secure hashing.
03. Effective Usage
3.1 Recommended Practices
- Always hash passwords with
Flask-Bcrypt
and verify them securely.
Example: Comprehensive Password Hashing
from flask import Flask, request, redirect, url_for
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager, UserMixin, login_user
from flask_bcrypt import Bcrypt
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///complete.db'
app.config['SECRET_KEY'] = 'secure-secret-key'
db = SQLAlchemy(app)
bcrypt = Bcrypt(app)
login_manager = LoginManager(app)
login_manager.login_view = 'login'
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
password_hash = db.Column(db.String(120), nullable=False)
@login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
@app.route('/register', methods=['POST'])
def register():
username = request.form['username']
email = request.form['email']
password = request.form['password']
if not all([username, email, password]):
return "All fields required"
if len(password) < 8:
return "Password must be at least 8 characters"
if User.query.filter_by(username=username).first():
return "Username taken"
if User.query.filter_by(email=email).first():
return "Email taken"
hashed_password = bcrypt.generate_password_hash(password).decode('utf-8')
user = User(username=username, email=email, password_hash=hashed_password)
db.session.add(user)
db.session.commit()
return "Registered"
@app.route('/login', methods=['POST'])
def login():
username = request.form['username']
password = request.form['password']
user = User.query.filter_by(username=username).first()
if user and bcrypt.check_password_hash(user.password_hash, password):
login_user(user)
return redirect(url_for('dashboard'))
return "Invalid credentials"
@app.route('/dashboard')
def dashboard():
return "User dashboard"
with app.app_context():
db.create_all()
Output:
Registered (on valid POST request with unique username, email, and password >= 8 characters)
User dashboard (on valid login)
- Validates inputs and enforces strong passwords.
- Integrates hashing and verification with registration and login workflows.
- Uses a secure secret key for session management.
3.2 Practices to Avoid
- Avoid storing plain-text passwords or using weak hashing algorithms.
Example: Insecure Password Storage
from flask import Flask, request
from flask_sqlalchemy import SQLAlchemy
import hashlib # Weak hashing
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///weak.db'
db = SQLAlchemy(app)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
password_hash = db.Column(db.String(120), nullable=False)
@app.route('/register', methods=['POST'])
def register():
username = request.form['username']
password = request.form['password']
# Insecure: Using MD5 without salt
hashed_password = hashlib.md5(password.encode()).hexdigest()
user = User(username=username, password_hash=hashed_password)
db.session.add(user)
db.session.commit()
return "Registered"
with app.app_context():
db.create_all()
Output:
Registered (but with insecure MD5 hashing)
- MD5 is outdated and vulnerable to attacks.
- Solution: Use
Flask-Bcrypt
for secure, salted hashing.
04. Common Use Cases in Web Development
4.1 Secure Blog Authentication
Hash and verify passwords for secure blog user authentication.
Example: Blog Password Hashing
from flask import Flask, request
from flask_sqlalchemy import SQLAlchemy
from flask_bcrypt import Bcrypt
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///blog.db'
app.config['SECRET_KEY'] = 'your-secret-key'
db = SQLAlchemy(app)
bcrypt = Bcrypt(app)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
password_hash = db.Column(db.String(120), nullable=False)
posts = db.relationship('Post', backref='author', lazy=True)
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(100), nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
@app.route('/register', methods=['POST'])
def register():
username = request.form['username']
password = request.form['password']
if not password or len(password) < 8:
return "Password must be at least 8 characters"
if User.query.filter_by(username=username).first():
return "Username taken"
hashed_password = bcrypt.generate_password_hash(password).decode('utf-8')
user = User(username=username, password_hash=hashed_password)
db.session.add(user)
db.session.commit()
return "Registered"
with app.app_context():
db.create_all()
Output:
Registered (on valid POST request)
Explanation:
- Secures blog user credentials with bcrypt hashing.
- Integrates with Flask Relationships in Models for post creation.
4.2 E-commerce User Security
Protect e-commerce user accounts with secure password hashing.
Example: E-commerce Password Hashing
from flask import Flask, request
from flask_sqlalchemy import SQLAlchemy
from flask_bcrypt import Bcrypt
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///ecommerce.db'
app.config['SECRET_KEY'] = 'your-secret-key'
db = SQLAlchemy(app)
bcrypt = Bcrypt(app)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
password_hash = db.Column(db.String(120), nullable=False)
orders = db.relationship('Order', backref='customer', lazy=True)
class Order(db.Model):
id = db.Column(db.Integer, primary_key=True)
item = db.Column(db.String(100), nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
@app.route('/register', methods=['POST'])
def register():
username = request.form['username']
email = request.form['email']
password = request.form['password']
if not all([username, email, password]):
return "All fields required"
if len(password) < 8:
return "Password must be at least 8 characters"
if User.query.filter_by(username=username).first():
return "Username taken"
if User.query.filter_by(email=email).first():
return "Email taken"
hashed_password = bcrypt.generate_password_hash(password).decode('utf-8')
user = User(username=username, email=email, password_hash=hashed_password)
db.session.add(user)
db.session.commit()
return "Registered"
with app.app_context():
db.create_all()
Output:
Registered (on valid POST request)
Explanation:
- Secures e-commerce user accounts with bcrypt hashing.
- Links users to orders, supporting secure account management.
Conclusion
Flask password hashing with Flask-Bcrypt, integrated with Flask Authentication, Flask User Registration, and NumPy Array Operations, ensures secure storage and verification of user credentials. Key takeaways:
- Use
Flask-Bcrypt
for secure password hashing and verification. - Integrate with registration and login workflows for complete security.
- Apply in blogs, e-commerce, or other secure applications.
- Avoid plain-text passwords or weak hashing algorithms like MD5.
With these techniques, you can build Flask applications that protect user credentials and maintain trust in your web platform!
Comments
Post a Comment