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
18 changes: 18 additions & 0 deletions Python/Flask_Book_Library/project/core/special_objects.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
class ReadOnce:
def __init__(self, initial=None):
self._value = initial
self._is_set = initial is not None

@property
def value(self):
return self._value

@value.setter
def value(self, new_value):
if self._is_set:
raise AttributeError("Value is read-once and cannot be changed.")
self._value = new_value
self._is_set = True

def __str__(self):
return str("Sensitive Data: ***")
47 changes: 43 additions & 4 deletions Python/Flask_Book_Library/project/customers/models.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from project.core.special_objects import ReadOnce
from project import db, app


# Customer model
class Customer(db.Model):
class CustomerDBModel(db.Model):
__tablename__ = 'customers'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True, index=True)
Expand All @@ -19,11 +20,49 @@ def __init__(self, name, city, age, pesel, street, appNo):
self.pesel = pesel
self.street = street
self.appNo = appNo
print("Getting: " + str(self),flush=True)

def __repr__(self):
return f"Customer(ID: {self.id}, Name: {self.name}, City: {self.city}, Age: {self.age}, Pesel: {self.pesel}, Street: {self.street}, AppNo: {self.appNo})"


with app.app_context():
db.create_all()


class Customer:
def __init__(self, name, city, age, pesel, street, appNo):
self.db_model = CustomerDBModel(name, city, age, pesel, street, appNo)
self.name = name
self.city = city
self.age = age
self.pesel = ReadOnce(pesel)
self.street = ReadOnce(street)
self.appNo = ReadOnce(appNo)
print("Getting: " + str(self),flush=True)

@classmethod
def from_db(cls, customer_model):
return cls(
name=customer_model.name,
city=customer_model.city,
age=customer_model.age,
pesel=customer_model.pesel,
street=customer_model.street,
appNo=customer_model.appNo
)
def set_name(self, name):
self.name = name
self.db_model.name = name
def set_city(self, city):
self.city = city
self.db_model.city = city
def set_age(self, age):
self.age = age
self.db_model.age = age

@staticmethod
def get(id):
customer_model = CustomerDBModel.query.get(id)
if customer_model:
return Customer.from_db(customer_model)
return None
def __repr__(self):
return f"Customer(Name: {self.name}, City: {self.city}, Age: {self.age}, Pesel: {self.pesel}, Street: {self.street}, AppNo: {self.appNo})"
35 changes: 23 additions & 12 deletions Python/Flask_Book_Library/project/customers/views.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from flask import render_template, Blueprint, request, redirect, url_for, jsonify
from flask import render_template, Blueprint, request, redirect, url_for, jsonify, render_template_string
from project import db
from project.customers.models import Customer
from project.customers.models import Customer, CustomerDBModel


# Blueprint for customers
Expand All @@ -11,19 +11,30 @@
@customers.route('/', methods=['GET'])
def list_customers():
# Fetch all customers from the database
customers = Customer.query.all()
customers = CustomerDBModel.query.all()
print('Customers page accessed')
# if len(customers) == 1:
# print(customers[0].city)
# return render_template_string(customers[0].name + "city:" + customers[0].city + customers[0].street)
return render_template('customers.html', customers=customers)


# Route to fetch customers in JSON format
@customers.route('/json', methods=['GET'])
def list_customers_json():
# Fetch all customers from the database and convert to JSON
customers = Customer.query.all()
customers = CustomerDBModel.query.all()
customer_list = [{'name': customer.name, 'city': customer.city, 'age': customer.age} for customer in customers]
return jsonify(customers=customer_list)

@customers.app_template_filter("my_filter")
def my_filter(fn, args):
"DATA"
print(type(fn))
print(repr(fn))
print(type(args))
print(repr(args))
return fn(args)

# Route to create a new customer
@customers.route('/create', methods=['POST', 'GET'])
Expand All @@ -46,7 +57,7 @@ def create_customer():
)

try:
db.session.add(new_customer)
db.session.add(new_customer.db_model)
db.session.commit()
print('Customer added successfully')
return redirect(url_for('customers.list_customers'))
Expand All @@ -60,7 +71,7 @@ def create_customer():
@customers.route('/<int:customer_id>/edit-data', methods=['GET'])
def edit_customer_data(customer_id):
# Get the customer with the given ID
customer = Customer.query.get(customer_id)
customer = Customer.get(customer_id)

if customer:
# Convert customer data to a dictionary
Expand All @@ -79,7 +90,7 @@ def edit_customer_data(customer_id):
@customers.route('/<int:customer_id>/edit', methods=['POST'])
def edit_customer(customer_id):
# Get the customer with the given ID
customer = Customer.query.get(customer_id)
customer = Customer.get(customer_id)

# Check if the customer exists
if not customer:
Expand All @@ -91,9 +102,9 @@ def edit_customer(customer_id):
data = request.form

# Update customer details
customer.name = data['name']
customer.city = data['city']
customer.age = data['age']
customer.set_name(data['name'])
customer.set_city(data['city'])
customer.set_age(data['age'])

# Commit the changes to the database
db.session.commit()
Expand All @@ -109,14 +120,14 @@ def edit_customer(customer_id):
# Route to delete a customer
@customers.route('/<int:customer_id>/delete', methods=['POST'])
def delete_customer(customer_id):
customer = Customer.query.get(customer_id)
customer = Customer.get(customer_id)
if not customer:
print('Customer not found')
return jsonify({'error': 'Customer not found'}), 404

try:
# Delete the customer from the database
db.session.delete(customer)
db.session.delete(customer.db_model)
db.session.commit()
print('Customer deleted successfully')
return redirect(url_for('customers.list_customers'))
Expand Down
8 changes: 4 additions & 4 deletions Python/Flask_Book_Library/project/loans/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from project.loans.models import Loan
from project.loans.forms import CreateLoan
from project.books.models import Book
from project.customers.models import Customer
from project.customers.models import Customer, CustomerDBModel


# Blueprint for loans
Expand All @@ -26,7 +26,7 @@ def list_books_json():
def list_customers_json():

# Fetch all customers from the database
customers = Customer.query.all()
customers = CustomerDBModel.query.all()
# Create a list of customer names
customer_list = [{'name': customer.name} for customer in customers]
# Return customer data in JSON format
Expand All @@ -49,7 +49,7 @@ def create_loan():
form = CreateLoan()

if request.method == 'POST':

# Process form submission
customer_name = form.customer_name.data
book_name = form.book_name.data
Expand Down Expand Up @@ -145,7 +145,7 @@ def delete_loan(loan_id):
author=loan.original_author,
year_published=loan.original_year_published,
book_type=loan.original_book_type,
status='available'
status='available'
)

# Add the book to the database
Expand Down
60 changes: 60 additions & 0 deletions Python/Flask_Book_Library/spraw.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Zad1

dane wrażliwe są wyświetlane tylko w przypadku wykonania operacji tworzenia nowego użytkownika. Logi pokazują wtedy dane wrażliwe jak pesel, miejsce zamieszkania.

log:
`Getting: Customer(ID: None, Name: imie, City: city, Age: 22, Pesel: 20001110, Street: street, AppNo: 40)`

### Poprawki:
Problem został naprawiony przez zastosowania wzorca projektowego read-once dla pól PESEL, street, appno.
Aby było to możliwe, została stworzona nowa klasa dla oddzielenia bazy danych (klasa CustomerDBModel) or klasy używanej przez backend (klasa Customer). Klasa Customer posiada pola typu read-once, które dla metody str() zwracają "Sensitive Data: ***".

log:
`Getting: Customer(Name: a, City: a, Age: 11, Pesel: Sensitive Data: ***, Street: Sensitive Data: ***, AppNo: Sensitive Data: ***)`

# Zad2

Git leaks znalazło 3 sekrety

### 1
Zcommitowany klucz prywatny RSA w pliku deployment.key,
dla commita:
`bc17b7ddc46f46fff175aed55d68e11bb48166cc`
link:
`https://github.com/Mixeway-Academy/task2/blob/bc17b7ddc46f46fff175aed55d68e11bb48166cc/deployment.key#L1`
### 2
Zcommitowany klucz prywatny RSA w pliku deployment2.key
dla commita:
`de9d7b8cb63bd7ae741ec5c9e23891b71709bc28`
link:
`https://github.com/Mixeway-Academy/task2/blob/de9d7b8cb63bd7ae741ec5c9e23891b71709bc28/deployment2.key#L1`

### 3
Zcommitowany klucz prywatny RSA w pliku awscredentials.json dla commita:
`bc17b7ddc46f46fff175aed55d68e11bb48166cc`
link:
`https://github.com/Mixeway-Academy/task2/blob/de9d7b8cb63bd7ae741ec5c9e23891b71709bc28/deployment2.key#L1`

# Zad 3

Przeanalizowałem exploit:

| jinja2 | 3.1.2 | <3.1.5 | 76378 |
+==============================================================================+
| An oversight in how the Jinja sandboxed environment detects calls to |
| str.format allows an attacker who controls the content of a template to |
| execute arbitrary Python code. To exploit the vulnerability, an attacker |
| needs to control the content of a template. Whether that is the case depends |
| on the type of application using Jinja. This vulnerability impacts users of |
| applications which execute untrusted templates. Jinja's sandbox does catch |
| calls to str.format and ensures they don't escape the sandbox. However, it's |
| possible to store a reference to a malicious string's format method, then |
| pass that to a filter that calls it. No such filters are built-in to Jinja, |
| but could be present through custom filters in an application. After the |
| fix, such indirect calls are also handled by the sandbox. |
+==============================================================================+

Oznacza on, że w tej wersji, jeżeli atakujący miał dostęp do niezabezpieczoego template (na przykład przy używaniu render_template_string) możliwe było wykonanie zdefiniowanego obiektu format dla obiektu string (przy użyciu metody set), następne, jeżeli deweloperzy aplikacji zdefiniowali własny filtr, (przy użyciu @blueprint.app_template_filter("name")), możliwe było wywołanie tego filtru z podaniem obiektu format jako argumentu. Jako że metoda filtr znajdywała się poza sandoxem Jinja, możliwe było dostanie się do atrybutów globalnych, i
zdalne wykonanie kodu.

W tej aplikacji, wykonanie tego exploitu nie jest możliwe, gdyż używa ona tylko metody render_template, która blokuje możliwość interpretowania otrzymanych danych jako template syntax.