Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion JWT/jwt-signature-apis-challenges/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ app.post('/jwt/none', (req, res) => { //None endpoint
} else if (jwt_b64_dec.header.alg == 'none') {
secret_key = '';
}
JWT.verify(jwt_token, secret_key, { algorithms: ['none', 'HS256'], complete: true, audience: 'https://127.0.0.1/jwt/none' }, (err, decoded_token) => {
JWT.verify(jwt_token, secret_key, { algorithms: ['HS256'], complete: true, audience: 'https://127.0.0.1/jwt/none' }, (err, decoded_token) => {
if (err) {
res.status(400).json(err);
} else {
Expand Down
3 changes: 3 additions & 0 deletions Python/Flask_Book_Library/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ ENV PASSWORD=1qaz@WSX
# Instalujemy zależności
RUN pip install --no-cache-dir -r requirements.txt

# Uruchomienie testów
RUN pytest -q

# Ustawiamy zmienną środowiskową, aby Flask wiedział, jak uruchomić aplikację
ENV FLASK_APP=app.py
ENV FLASK_RUN_HOST=0.0.0.0
Expand Down
Binary file added Python/Flask_Book_Library/bladbudowania.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Python/Flask_Book_Library/blokadaATAKU.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Python/Flask_Book_Library/jwtATAK.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
56 changes: 56 additions & 0 deletions Python/Flask_Book_Library/prtext.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
## Lab 3 TBO
### Zadanie 1
Ćwiczenie rozpoczęto od opracowania serii testów jednostkowych dla wybranej klasy. W tym wypadku była to klasa Book (technologia wybrana - Python). W tym celu wykorzystano bibliotekę pytest oraz dodano odpowiednie informacje do pliku Dockerfile i requirements.txt. Następnie testy wyegzekwowano z efektem widocznym na Zdj. 1.

![blad](bladbudowania.png)
Zdj. 1. Błąd zbudowania Dockerfile - wysyp na testach zgodnie z założeniem

### Zadanie 2
w kolejnym etapie próbowano przeprowadzić atak JWT, aby dostać przyznanie praw administratora dla zwykłego użytkownika. W tym celu pobrano token "Boba" przykładowego użytkownika. Zdekodowana wartość tokena Boba wyglądała następująco (odebrana po wysłaniu zapytania):

```javascript
//header
{
"alg": "HS256",
"typ": "JWT"
}
//payload
{
"account": "Bob",
"role": "User",
"iat": 1764790137,
"aud": "https://127.0.0.1/jwt/none"
}
```

Aby móc uzyskać prawa administatora należy podmienic odpowiednie elementy tokena aby pominąć szyfrowanie i ustawić rolę na admina. Zmiany wprowadzono w headerze tak jak widoczne jest to poniżej:

```javascript
// (header)
{
"alg": "none",
"typ": "JWT"
}

// (payload)
{
"account": "Administrator",
"role": "User",
"iat": 1764790137,
"aud": "https://127.0.0.1/jwt/none"
}
```
a następnie zakodować go w formacie base64 aby móc podać go w zapytaniu przez token JWT. Ostateczny token wyglądał następująco:
```python
eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJhY2NvdW50IjoiQWRtaW5pc3RyYXRvciIsInJvbGUiOiJVc2VyIiwiaWF0IjoxNzY0NzkwMTM3LCJhdWQiOiJodHRwczovLzEyNy4wLjAuMS9qd3Qvbm9uZSJ9.
```

W wyniku wysłania zapytania z powyższym tokenem otrzymano prawa administratora - przeprowadzono atak, co pokazano na Zdj. 2.
![jwtATAK](jwtATAK.png)
Zdj. 2. Przeprowadzony atak JWT

### Zadanie 3
W ostatnim ćwiczeniu przystąpiono do wprowadzenia poprawki w app.json aby uniknąć ataków JWT typu none. Zmiana polegała na usunięciu opcji "none" przy przyjmowanych formach szyfrowania komunikacji. Następnie ponownie zbudowano kontener i sprawdzono możliwość przesyłu szkodliwego JWT. Tym razem atak nie powiódł się jak widać na Zdj. 3.

![blokadaATAKU](blokadaATAKU.png)
Zdj. 3. Zablokowany atak JWT, blokada przez brak zapewnienia trybu szyfrowania
1 change: 1 addition & 0 deletions Python/Flask_Book_Library/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ SQLAlchemy==2.0.21
typing_extensions==4.8.0
Werkzeug==2.3.7
WTForms==3.0.1
pytest==8.4.2
244 changes: 244 additions & 0 deletions Python/Flask_Book_Library/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
import pytest
from project import app as flask_app, db as flask_db
from project.books.models import Book

@pytest.fixture(scope="module")
def app():
flask_app.config.update(
{
"TESTING": True,
"SQLALCHEMY_DATABASE_URI": "sqlite:///:memory:",
"SQLALCHEMY_TRACK_MODIFICATIONS": False,
}
)

with flask_app.app_context():
yield flask_app


@pytest.fixture(scope="function")
def session(app):
flask_db.create_all()
yield flask_db.session
flask_db.session.remove()
flask_db.drop_all()


def test_create_book_valid_default_status(session):
book = Book(
name="Sample Book",
author="John Doe",
year_published=2020,
book_type="Fiction",
)
session.add(book)
session.commit()

stored = Book.query.filter_by(name="Sample Book").first()
assert stored is not None
assert stored.author == "John Doe"
assert stored.year_published == 2020
assert stored.book_type == "Fiction"
assert stored.status == "available"


def test_create_book_valid_given_status(session):
book = Book(
name="Status Book",
author="John Doe",
year_published=2020,
book_type="Fiction",
status="unavailable",
)
session.add(book)
session.commit()

stored = Book.query.filter_by(name="Status Book").first()
assert stored is not None
assert stored.status == "unavailable"


def test_create_book_missing_name(session):
with pytest.raises(Exception):
book = Book(
name=None,
author="John Doe",
year_published=2020,
book_type="Fiction",
)
session.add(book)
session.commit()


def test_create_book_invalid_year(session):
with pytest.raises(Exception):
book = Book(
name="Invalid Year Book",
author="Jane Doe",
year_published="abcd",
book_type="Fiction",
)
session.add(book)
session.commit()


def test_create_book_name_too_long(session):
with pytest.raises(Exception):
book = Book(
name="a" * 65,
author="Jane Doe",
year_published=2024,
book_type="Fiction",
)
session.add(book)
session.commit()


def test_create_book_author_too_long(session):
with pytest.raises(Exception):
book = Book(
name="Sample Book",
author="a" * 65,
year_published=2024,
book_type="Fiction",
)
session.add(book)
session.commit()


def test_create_book_book_type_too_long(session):
with pytest.raises(Exception):
book = Book(
name="Sample Book",
author="Jane Doe",
year_published=2024,
book_type="a" * 21,
)
session.add(book)
session.commit()


def test_create_book_status_too_long(session):
with pytest.raises(Exception):
book = Book(
name="Sample Book",
author="Jane Doe",
year_published=2024,
book_type="Fiction",
status="a" * 21,
)
session.add(book)
session.commit()


def test_create_book_name_not_unique(session):
first = Book(
name="Sample Book",
author="John Doe",
year_published=2020,
book_type="Fiction",
)
second = Book(
name="Sample Book",
author="John Doe2",
year_published=2022,
book_type="Fiction",
)
with pytest.raises(Exception):
session.add(first)
session.add(second)
session.commit()


def test_sql_injection_like_input(session):
suspicious_name = "Book'); DROP TABLE books;--"
book = Book(
name=suspicious_name,
author="Hacker",
year_published=2023,
book_type="Fiction",
)
with pytest.raises(Exception):
session.add(book)
session.commit()


def test_javascript_injection_like_input(session):
suspicious_name = "<script>alert('Hacked!');</script>"
book = Book(
name=suspicious_name,
author="Hacker",
year_published=2023,
book_type="Fiction",
)
with pytest.raises(Exception):
session.add(book)
session.commit()


def test_extremely_long_input(session):
long_value = "a" * 10000
attrs_to_check = ["name", "author", "book_type", "status"]

for attr in attrs_to_check:
base_data = {
"name": "Normal Name",
"author": "Normal Author",
"year_published": 2023,
"book_type": "Fiction",
"status": "available",
}

base_data[attr] = long_value

book = Book(**base_data)
session.add(book)
session.commit()

stored = Book.query.filter_by(**{attr: long_value}).first()
assert stored is not None
assert getattr(stored, attr) == long_value
assert len(getattr(stored, attr)) == 10000

session.delete(stored)
session.commit()


def test_negative_year(session):
book = Book(
name="Boundary Year Book",
author="Author",
year_published=-2,
book_type="Non-Fiction",
)
with pytest.raises(Exception):
session.add(book)
session.commit()


def test_create_book_missing_author(session):
with pytest.raises(Exception):
book = Book(
name="No Author Book",
author=None,
year_published=2021,
book_type="Fiction",
)
session.add(book)
session.commit()


def test_create_book_empty_status_defaults_to_available(session):
book = Book(
name="Empty Status Book",
author="Author",
year_published=2021,
book_type="Fiction",
status=None,
)
session.add(book)
session.commit()

stored = Book.query.filter_by(name="Empty Status Book").first()
assert stored is not None
assert stored.status == "available"