diff --git a/README.md b/README.md index a914103..5d6a350 100644 --- a/README.md +++ b/README.md @@ -1,69 +1,37 @@ # Secure Coding with Python. ## Chapter 2: SQL Injection -### 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. - -First we install `pytest`: -```bash -> pip install pytest -``` -or -```bash -> pip install -r requirements.txt -``` +### Fix +Given that we have seen that the way this injection works is by breaking out of the `'`'s, we can use PostgreSQL +escaping `E'\''`. For that we change our SQL query and replace every occurrence of `'` with `\'`: -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') + sql = "INSERT INTO listings (title, description) VALUES (E'%s', E'%s')" % ( + title.replace("'", "\\'"), description.replace("'", "\\'") + ) ``` -After running `pytest --tb=short` we get: +With that our test now pass: + ```text -============================= test session starts ============================== -platform linux -- Python 3.5.3, pytest-5.0.1, py-1.8.0, pluggy-0.12.0 +(venv) > $ pytest --tb=short +================================================================================================== test session starts =================================================================================================== +platform linux -- Python 3.5.3, pytest-5.0.0, py-1.8.0, pluggy-0.12.0 rootdir: {...} collected 1 item +tests/test_listings.py . [100%] +================================================================================================ 1 passed in 0.95 seconds ================================================================================================ +``` -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 =========================== +But this is not sufficient, if we modify our payload to be `injection\', (select version()))-- -` our query will end up being: +```sql +INSERT INTO listings (title, description) VALUES (E'injection\\', (select version()))-- -', E'\'') ``` -**Proceed to [next section](https://github.com/nxvl/secure-coding-with-python/tree/2.1-sql-injection/fix)** +and attacker will still be able to exploit our app. + +**Proceed to [next section](https://github.com/nxvl/secure-coding-with-python/tree/2.2-sql-injection/test)** ## Index ### 1. Vulnerable Components diff --git a/marketplace/listings.py b/marketplace/listings.py index 3f9dbcb..65ce3ac 100644 --- a/marketplace/listings.py +++ b/marketplace/listings.py @@ -24,7 +24,9 @@ def register(): db = get_db() cur = db.cursor() - sql = "INSERT INTO listings (title, description) VALUES ('%s', '%s')" % (title, description) + sql = "INSERT INTO listings (title, description) VALUES (E'%s', E'%s')" % ( + title.replace("'", "\\'"), description.replace("'", "\\'") + ) print(sql, file=sys.stdout) cur.execute(sql) db.commit()