diff --git a/README.md b/README.md index 588ccf9..14a9df9 100644 --- a/README.md +++ b/README.md @@ -1,54 +1,18 @@ # Secure Coding with Python. -## Chapter 3: Weak Password Storage -### Fix -In order to prevent rainbow table attacks, cryptographers incorporated *[salt](https://en.wikipedia.org/wiki/Salt_(cryptography)* to hashing algorithms. -One of the algorithms that incorporates *salt* is [Bcrypt](https://en.wikipedia.org/wiki/Bcrypt). -Said algorithm also uses a technique known as *[key stretching](https://en.wikipedia.org/wiki/Key_stretching)*, while salt prevents precomputation attacks, key stretching helps thwart attacks that rely on hardware that can perform hashes very quickly, such as GPUs and ASICs +## Chapter 4: Broken Authentication +### Requirement +Now that we have users in the system, we need to allow them to login. -To test this concept, here is a function that hashes a password and times how long it takes to do so. We increase the iteration in 4 every time. -```python -In [1]: import bcrypt +### Development +We add a simple form to allow users to login, check for user and password to be correct and add a simple session. +If something goes wrong, we drop some error messages. -In [2]: import time +### Vulnerability +Since we are very transparent and explicit in our error messages, an attacker can take advantage of them to enumerate users on our system. +This could be done to reduce time of a brute force or credential stuffing attack. -In [3]: def hash(passwd, r): - ...: start = time.time() - ...: salt = bcrypt.gensalt(rounds=r) - ...: hashed = bcrypt.hashpw(passwd, salt) - ...: end = time.time() - ...: print(end - start) - ...: print(salt) - ...: print(hashed) - ...: - -In [4]: hash(b'supersecret', 4) -0.0013570785522460938 -b'$2b$04$wBySsg90EhLyCxFhuNC9Ze' -b'$2b$04$wBySsg90EhLyCxFhuNC9ZeDZKdauAtlEcegqM0GOyZKIgJhJ6neMW' - -In [5]: hash(b'supersecret', 8) -0.01915597915649414 -b'$2b$08$QNWHnTrxBQu8pscr5hhveu' -b'$2b$08$QNWHnTrxBQu8pscr5hhveuyNOPwtR4VhxujWE/O.yjc60DhIduWkq' - -In [6]: hash(b'supersecret', 12) -0.2138371467590332 -b'$2b$12$.Eql6xg1/uUoWr3yuYSOaO' -b'$2b$12$.Eql6xg1/uUoWr3yuYSOaOLkEZ.XoUiJOjuMHtjyWNZoW8JOOSHx.' - -In [7]: hash(b'supersecret', 16) -3.2648401260375977 -b'$2b$16$A4xDXHZPHPE5tUxdqoJD0u' -b'$2b$16$A4xDXHZPHPE5tUxdqoJD0uXleSIgNGHOOv8yQ6wQIU/rLoVwqtF4C' -``` - -Now if an attacker gets our hashed passwords, since hashed has been computed using the password and a unique *salt*, the brute-force attack will need to be performed per-hash, rendering rainbow tables useless. -Also since we can configure the iterations, as time passes by, we can increase it's count to make a brute-force attack slower each time. - -**Note**: Other algorithms that include the same concepts, and are arguably better, are scrypt and argon2. - -**Proceed to [next section](https://github.com/nxvl/secure-coding-with-python/tree/4-broken-authentication/code)** +**Proceed to [next section](https://github.com/nxvl/secure-coding-with-python/tree/4-broken-authentication/fix)** ## Index ### 1. Vulnerable Components diff --git a/marketplace/helpers.py b/marketplace/helpers.py new file mode 100644 index 0000000..4dd8a5a --- /dev/null +++ b/marketplace/helpers.py @@ -0,0 +1,21 @@ +from flask import session, redirect, url_for + +from .models import User +from . import db + + +def auth(func): + def decorated_function(*args, **kwargs): + key = session.get('key') + if not key: + return redirect(url_for('users.login')) + + user = db.session.query(User).filter_by(session_key=key).scalar() + if not user: + return redirect(url_for('users.login')) + + return func(user, *args, **kwargs) + + decorated_function.__name__ = func.__name__ + return decorated_function + diff --git a/marketplace/models.py b/marketplace/models.py index 660f9c3..d160222 100644 --- a/marketplace/models.py +++ b/marketplace/models.py @@ -8,7 +8,7 @@ class User(db.Model): __tablename__ = 'users' id = db.Column(db.Integer, primary_key=True) full_name = db.Column(db.String(100)) - email = db.Column(db.String(100)) + email = db.Column(db.String(100), unique=True) _password = db.Column('password', db.String(100)) @hybrid_property diff --git a/marketplace/templates/users/login.html b/marketplace/templates/users/login.html new file mode 100644 index 0000000..e3df28d --- /dev/null +++ b/marketplace/templates/users/login.html @@ -0,0 +1,16 @@ +{% extends 'base.html' %} + +{% block header %} +