From 421405cb3455a96b71aa69868bc10df410f274ec Mon Sep 17 00:00:00 2001 From: Nicolas Valcarcel Date: Sun, 11 Aug 2019 21:32:21 -0500 Subject: [PATCH 1/9] use bcrypt --- README.md | 84 +++++++++++++++++++++---------------------- marketplace/models.py | 5 +-- requirements.txt | 2 +- 3 files changed, 45 insertions(+), 46 deletions(-) diff --git a/README.md b/README.md index 3291834..6fa5b5a 100644 --- a/README.md +++ b/README.md @@ -1,50 +1,48 @@ # Secure Coding with Python. ## Chapter 3: Weak Password Storage -### Test -Every encryption algorithm can be theoretically cracked using brute-force attacks, this attack consist in trying multiple possible strings until one provides de desired hash. Said attacks are fairly expensive to perform as they take some time. - -Given that we know the algorithm used for a hash we can create a very simple dictionary brute-force attack against the hash. We will be using the [RockYou](https://github.com/brannondorsey/naive-hashcat/releases/download/data/rockyou.txt) wordlist. - -```text -> $ time python crackpass.py f75778f7425be4db0369d09af37a6c2b9a83dea0e53e7bd57412e4b060e607f7 rockyou.txt -Password is: supersecret -python crackpass.py rockyou.txt 0.32s user 0.01s system 99% cpu 0.325 total - +### 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, has also been created to have a configurable iteration count, making the algorithm slower as computation power increases over time. + +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 + +In [2]: import time + +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 that's just 1 password, if we had to crack thousands of passwords, the effort starts getting significant. That's where rainbow tables kick in. -The [wikipedia definition](https://en.wikipedia.org/wiki/Rainbow_table) describes rainbow tables as: "A rainbow table is a precomputed table for reversing cryptographic hash functions, usually for cracking password hashes." - -Let's try to mass crack: -#### 50 hashes -```text -> $ time python rainbow-crack.py rockyou-rainbow.txt hashes-50.txt -[...] -password for b'73d07a303cc50a5423ae72081cafe4e50a2fb1a0ef161d55e332e8533c5e25a0' is b"b'vane944218'" -password for b'2c2d908b313fb71b5592ae4a44dfad2dbedd1832915a97a547d58e4c09a8ee49' is b"b'Robert7681'" -python rainbow-crack.py rockyou-rainbow.txt 10.98s user 1.50s system 99% cpu 12.484 total -``` - -#### 100 hashes -```text -> $ time python rainbow-crack.py rockyou-rainbow.txt hashes-100.txt -[...] -password for b'37325783f2e3763b14f25d3a28edc90fbd08283fffa9b446d827ad60c0d19272' is b"b'raaces'" -password for b'6df380dbe975a3bb65a880360e84584fdacea1455c27aa7ffef9a4b639592259' is b"b'mattlvu'" -python crackers/rainbow-crack.py ~/Downloads/rockyou-rainbow.txt 10.83s user 1.52s system 99% cpu 12.367 total -``` - -#### 200 hashes -```text -> $ time python rainbow-crack.py rockyou-rainbow.txt hashes-200.txt -[...] -password for b'53ad0738f0356042ae89f837767078f39492fc9b29e60fe056be5cefa9e9b510' is b"b'shaiyshaiy'" -password for b'9459c1e60e359f9f646bfe92a3a1ff1167a3b6d816290d09a33cdf8a565b15c6' is b"b'kuizenga'" -python crackers/rainbow-crack.py ~/Downloads/rockyou-rainbow.txt 10.99s user 1.53s system 99% cpu 12.541 total -``` - -As can be seen with Rainbow tables the cracking time is fairly linear, it takes around 11s for almost any case, most of the time is probably spend on loading up the DB, which can be optimized, but for the sake of this example we have done on a non-ideal way. + + Now if an attacker gets our hashed passwords, since each password has it's own hash, the brute-force attack will need to be performed per-hash, since the salt chances for each one. And since we can configure the iterations, as time passes by, we can increase it to make a brute-force attack slower each time. **Proceed to [next section](https://github.com/nxvl/secure-coding-with-python/tree/3.2-weak-password-storage/fix)** diff --git a/marketplace/models.py b/marketplace/models.py index c5b515f..22871be 100644 --- a/marketplace/models.py +++ b/marketplace/models.py @@ -1,4 +1,4 @@ -from hashlib import sha256 +import bcrypt from sqlalchemy.ext.hybrid import hybrid_property @@ -17,7 +17,8 @@ def password(self): @password.setter def password(self, plaintext): - self._password = sha256(plaintext.encode('ascii')).hexdigest() + salt = bcrypt.gensalt(rounds=12) + self._password = bcrypt.hashpw(plaintext, salt) class Listing(db.Model): __tablename__ = 'listings' diff --git a/requirements.txt b/requirements.txt index b34d6d7..32e0093 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ Flask==1.0.3 psycopg2==2.8.3 Flask-Migrate==2.5.2 -Flask-SQLAlchemy==2.4.0 \ No newline at end of file +Flask-SQLAlchemy==2.4.0bcrypt==3.1.7 From 242a62f8ac684a1d761a0ca6013ead307cb8b495 Mon Sep 17 00:00:00 2001 From: Nicolas Valcarcel Date: Sun, 11 Aug 2019 21:33:08 -0500 Subject: [PATCH 2/9] update next section link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6fa5b5a..1211024 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ b'$2b$16$A4xDXHZPHPE5tUxdqoJD0uXleSIgNGHOOv8yQ6wQIU/rLoVwqtF4C' Now if an attacker gets our hashed passwords, since each password has it's own hash, the brute-force attack will need to be performed per-hash, since the salt chances for each one. And since we can configure the iterations, as time passes by, we can increase it to make a brute-force attack slower each time. -**Proceed to [next section](https://github.com/nxvl/secure-coding-with-python/tree/3.2-weak-password-storage/fix)** +**Proceed to [next section](https://github.com/nxvl/secure-coding-with-python/tree/4-weak-account-secrets/code)** ## Index ### 1. Vulnerable Components From 3df29cfc5a9e20c1d7a25f4ca36628c6fde8504f Mon Sep 17 00:00:00 2001 From: Nicolas Valcarcel Date: Sun, 11 Aug 2019 21:37:31 -0500 Subject: [PATCH 3/9] encode/decode --- marketplace/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/marketplace/models.py b/marketplace/models.py index 22871be..660f9c3 100644 --- a/marketplace/models.py +++ b/marketplace/models.py @@ -18,7 +18,7 @@ def password(self): @password.setter def password(self, plaintext): salt = bcrypt.gensalt(rounds=12) - self._password = bcrypt.hashpw(plaintext, salt) + self._password = bcrypt.hashpw(plaintext.encode(), salt).decode() class Listing(db.Model): __tablename__ = 'listings' From 8450d8695ca5e9835cbbf106c4c5741981179a84 Mon Sep 17 00:00:00 2001 From: Nicolas Valcarcel Date: Sun, 11 Aug 2019 21:38:09 -0500 Subject: [PATCH 4/9] fix requirements.txt --- requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 32e0093..3027320 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ Flask==1.0.3 psycopg2==2.8.3 Flask-Migrate==2.5.2 -Flask-SQLAlchemy==2.4.0bcrypt==3.1.7 +Flask-SQLAlchemy==2.4.0 +bcrypt==3.1.7 From 2307beafd9f3acc1f46527f3191f42f7c10adf60 Mon Sep 17 00:00:00 2001 From: Nicolas Valcarcel Date: Mon, 12 Aug 2019 12:07:39 -0500 Subject: [PATCH 5/9] Update README.md Make fix description a bit clearer --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1211024..83ff25d 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,8 @@ b'$2b$16$A4xDXHZPHPE5tUxdqoJD0u' b'$2b$16$A4xDXHZPHPE5tUxdqoJD0uXleSIgNGHOOv8yQ6wQIU/rLoVwqtF4C' ``` - Now if an attacker gets our hashed passwords, since each password has it's own hash, the brute-force attack will need to be performed per-hash, since the salt chances for each one. And since we can configure the iterations, as time passes by, we can increase it to make a brute-force attack slower each time. +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. **Proceed to [next section](https://github.com/nxvl/secure-coding-with-python/tree/4-weak-account-secrets/code)** From 203789ebfe64f85d0538ce63a299bc868c95d2a5 Mon Sep 17 00:00:00 2001 From: Nicolas Valcarcel Date: Fri, 16 Aug 2019 22:37:59 -0500 Subject: [PATCH 6/9] Add key stretching explanation and mention other algorithms. --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1211024..5d67d99 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,8 @@ ## 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, has also been created to have a configurable iteration count, making the algorithm slower as computation power increases over time. +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 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 @@ -42,7 +43,10 @@ b'$2b$16$A4xDXHZPHPE5tUxdqoJD0u' b'$2b$16$A4xDXHZPHPE5tUxdqoJD0uXleSIgNGHOOv8yQ6wQIU/rLoVwqtF4C' ``` - Now if an attacker gets our hashed passwords, since each password has it's own hash, the brute-force attack will need to be performed per-hash, since the salt chances for each one. And since we can configure the iterations, as time passes by, we can increase it to make a brute-force attack slower each time. +Now if an attacker gets our hashed passwords, since each password has it's own hash, the brute-force attack will need to be performed per-hash, since the salt chances for each one. And since we can configure the iterations, as time passes by, we can increase it 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-weak-account-secrets/code)** From b049bc9b7d8c41e97eb766f1f1a8a44964c066e5 Mon Sep 17 00:00:00 2001 From: Nicolas Valcarcel Date: Tue, 20 Aug 2019 22:21:29 -0500 Subject: [PATCH 7/9] update next section link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 647026c..cc6763e 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ Also since we can configure the iterations, as time passes by, we can increase i **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-weak-account-secrets/code)** +**Proceed to [next section](https://github.com/nxvl/secure-coding-with-python/tree/4.1-broken-authentication/code** ## Index ### 1. Vulnerable Components From b074cad56ece09e36b4cd5ac7e047d9d5d3e66d9 Mon Sep 17 00:00:00 2001 From: Nicolas Valcarcel Date: Tue, 20 Aug 2019 22:22:05 -0500 Subject: [PATCH 8/9] fix broken link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cc6763e..f4dec1f 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ Also since we can configure the iterations, as time passes by, we can increase i **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.1-broken-authentication/code** +**Proceed to [next section](https://github.com/nxvl/secure-coding-with-python/tree/4.1-broken-authentication/code)** ## Index ### 1. Vulnerable Components From 1e99e4e4d675f46724537ddfeb0532b7387e4742 Mon Sep 17 00:00:00 2001 From: Nicolas Valcarcel Date: Tue, 10 Sep 2019 21:36:28 -0500 Subject: [PATCH 9/9] fix next section --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6013364..1ac6dde 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ Also since we can configure the iterations, as time passes by, we can increase i **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.1-broken-authentication/code)** +**Proceed to [next section](https://github.com/nxvl/secure-coding-with-python/tree/4-broken-authentication/code)** ## Index ### 1. Vulnerable Components