From f3b828ffe8d9fe36dec430d4579d9dd3b9f636b3 Mon Sep 17 00:00:00 2001 From: Nicolas Valcarcel Date: Sat, 6 Jul 2019 12:02:42 -0500 Subject: [PATCH 1/4] add a unit test for create listing and a simple SQLi fuzzer --- README.md | 51 +++++++++++++++++++++++++++++++++++++ tests/__init__.py | 0 tests/conftest.py | 29 +++++++++++++++++++++ tests/helpers/__init__.py | 0 tests/helpers/sqlifuzzer.py | 19 ++++++++++++++ tests/test_listings.py | 6 +++++ 6 files changed, 105 insertions(+) create mode 100644 tests/__init__.py create mode 100644 tests/conftest.py create mode 100644 tests/helpers/__init__.py create mode 100644 tests/helpers/sqlifuzzer.py create mode 100644 tests/test_listings.py diff --git a/README.md b/README.md index 70dd123..a1e1c1f 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,57 @@ INSERT INTO listings (title, description) VALUES ('injection', (select version() ``` As it can be seen, the inserted title will be `injection` and the description will be the result of the `select version()` command, or any other command we wish to insert there, including dropping the DB. +### Testing +Testing for SQL injections is a tedious job, it's mostly done by hand or using special scanners, like web scanners or SAST/DAST tools. For this chapter we will be writing a very simple fuzzer function and create unit tests that use them in order to test for injections. + +The fuzzer helper looks like this: +```python +import pytest + +from psycopg2.errors import SyntaxError + +def sqli_fuzzer(client, url, params): + fail = False + injections = ["'"] + for injection in injections: + for param in params: + data = {k: 'foo' for k in params} + data[param] = injection + try: + client.post(url, data=data) + except SyntaxError: + print('You seems to have an SQLi in %s for param %s' % (url, param)) + fail = True + + if fail: + pytest.fail('Seems you are vulnerable to SQLi attacks') +``` + +After running `pytest --tb=short` we get: +```text +============================= test session starts ============================== +platform linux -- Python 3.5.3, pytest-5.0.1, py-1.8.0, pluggy-0.12.0 +rootdir: {...} +collected 1 item + +tests/test_listings.py F [100%] + +=================================== FAILURES =================================== +_________________________________ test_create __________________________________ +tests/test_listings.py:6: in test_create + sqli_fuzzer(client, '/listings/create', ['title', 'description']) +tests/helpers/sqlifuzzer.py:19: in sqli_fuzzer + pytest.fail('Seems you are vulnerable to SQLi attacks') +E Failed: Seems you are vulnerable to SQLi attacks +----------------------------- Captured stdout call ----------------------------- +INSERT INTO listings (title, description) VALUES (''', 'foo') +You seems to have an SQLi in /listings/create for param title +INSERT INTO listings (title, description) VALUES ('foo', ''') +You seems to have an SQLi in /listings/create for param description +=========================== 1 failed in 0.32 seconds =========================== + +``` + ## Description Welcome to the Secure coding with python course. In this repository you will find a series of branches for each step of the development of a sample marketplace application. In such a development, we will be making security mistakes and introducing vulnerabilities, we will add tests for them and finally fixing them. diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..391301a --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,29 @@ +import os +import sys + +import pytest +from marketplace import create_app +from marketplace.db import get_db, init_db + +sys.path.append(os.path.join(os.path.dirname(__file__), 'helpers')) + +@pytest.fixture +def app(): + app = create_app({ + 'TESTING': True, + 'DATABASE': 'marketplace_test', + }) + + with app.app_context(): + init_db() + + yield app + +@pytest.fixture +def client(app): + return app.test_client() + + +@pytest.fixture +def runner(app): + return app.test_cli_runner() diff --git a/tests/helpers/__init__.py b/tests/helpers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/helpers/sqlifuzzer.py b/tests/helpers/sqlifuzzer.py new file mode 100644 index 0000000..7d8629a --- /dev/null +++ b/tests/helpers/sqlifuzzer.py @@ -0,0 +1,19 @@ +import pytest + +from psycopg2.errors import SyntaxError + +def sqli_fuzzer(client, url, params): + fail = False + injections = ["'"] + for injection in injections: + for param in params: + data = {k: 'foo' for k in params} + data[param] = injection + try: + client.post(url, data=data) + except SyntaxError: + print('You seems to have an SQLi in %s for param %s' % (url, param)) + fail = True + + if fail: + pytest.fail('Seems you are vulnerable to SQLi attacks') diff --git a/tests/test_listings.py b/tests/test_listings.py new file mode 100644 index 0000000..ac5b912 --- /dev/null +++ b/tests/test_listings.py @@ -0,0 +1,6 @@ +from .helpers.sqlifuzzer import sqli_fuzzer + +def test_create(client, app): + assert client.get('/listings/create').status_code == 200 + + sqli_fuzzer(client, '/listings/create', ['title', 'description']) From 959c0dfbd53f1f5321192d9fba82628d8ffc6c44 Mon Sep 17 00:00:00 2001 From: Nicolas Valcarcel Date: Sat, 6 Jul 2019 12:05:48 -0500 Subject: [PATCH 2/4] update link to next section --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a1e1c1f..296f362 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ The branches will have the following naming scheme for easier navigation: {Chapt For this course we will be using Python3, Flask and PostgreSQL. -**Proceed to [next section](https://github.com/nxvl/secure-coding-with-python/tree/2.1-sql-injection/test)** +**Proceed to [next section](https://github.com/nxvl/secure-coding-with-python/tree/2.1-sql-injection/fix)** ## Index ### 1. Vulnerable Components @@ -137,4 +137,4 @@ For this course we will be using Python3, Flask and PostgreSQL. ### 10. Sensitive Data Exposure * [10-sensitive-data-exposure/code](https://github.com/nxvl/secure-coding-with-python/tree/10-sensitive-data-exposure/code) * [10-sensitive-data-exposure/test](https://github.com/nxvl/secure-coding-with-python/tree/10-sensitive-data-exposure/test) -* [10-sensitive-data-exposure/fix](https://github.com/nxvl/secure-coding-with-python/tree/10-sensitive-data-exposure/fix) \ No newline at end of file +* [10-sensitive-data-exposure/fix](https://github.com/nxvl/secure-coding-with-python/tree/10-sensitive-data-exposure/fix) From b080adcae905e0d5b880866695dc9d920410dab5 Mon Sep 17 00:00:00 2001 From: Nicolas Valcarcel Date: Sat, 3 Aug 2019 15:29:55 -0500 Subject: [PATCH 3/4] remove last step text --- README.md | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/README.md b/README.md index 5c784ea..09a75ee 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,6 @@ # Secure Coding with Python. ## Chapter 2: SQL Injection -### Requirement -Since we are creating a marketplace application, we first decide to allow the upload of Listings, just text. We will worry about users later, since we want to focus on getting the DB and Models setup without needed to worry about authentication and session management at this point. - -### Development -Since the application will need some more configuration we change the `marketplace/__init__.py` to make use of the `create_app` factory function. We add the DB connection functions into `marketplace/db.py` and add the factory function. We also add the DB schema in `schema.sql` and add a flask command to init the DB, which we run with the `python -m flask init-db` command. - -### Vulnerability -Since we are generating the SQL to insert the new listing in a very unsecure way, we can insert SQL commands that will be run in the DB. For example if we insert `'` as title or description we will get `psycopg2.errors.SyntaxError: INSERT has more target columns than expressions LINE 1: INSERT INTO listings (title, description) VALUES (''', ''') ^` instead of a success. - -We can for example get the postgresql version or any other SQL function result, to check that out, insert `injection', (select version()))-- -` as the title. When we do so, the SQL that's going to be executed will be the following: -```sql -INSERT INTO listings (title, description) VALUES ('injection', (select version()))-- -', 'ignored description') -``` -As it can be seen, the inserted title will be `injection` and the description will be the result of the `select version()` command, or any other command we wish to insert there, including dropping the DB. - ### Testing Testing for SQL injections is a tedious job, it's mostly done by hand or using special scanners, like web scanners or SAST/DAST tools. For this chapter we will be writing a very simple fuzzer function and create unit tests that use them in order to test for injections. From 5bcc035ab6d44b41674452c9b92a945ae109fded Mon Sep 17 00:00:00 2001 From: Nicolas Valcarcel Date: Tue, 20 Aug 2019 22:11:48 -0500 Subject: [PATCH 4/4] add instructions for pytest installation --- README.md | 9 +++++++++ requirements.txt | 1 + 2 files changed, 10 insertions(+) diff --git a/README.md b/README.md index 74d4f08..8420156 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,15 @@ Testing for SQL injections is a tedious job, it's mostly done by hand or using s SAST/DAST tools. For this chapter we will be writing a very simple fuzzer function and create unit tests that use them in order to test for injections. +First we install `pytest`: +```bash +> pip install pytest +``` +or +```bash +> pip install -r requirements.txt +``` + The fuzzer helper looks like this: ```python import pytest diff --git a/requirements.txt b/requirements.txt index 95a4a80..88fd963 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ Flask==1.0.3 safety==1.8.5 psycopg2==2.8.3 +pytest==5.1.0 \ No newline at end of file