A final version of the report.
This commit is contained in:
parent
59692b7c05
commit
c5a062d073
Binary file not shown.
@ -70,9 +70,12 @@
|
||||
|
||||
\usepackage{amsmath, amssymb, amsfonts}
|
||||
\usepackage{graphicx}
|
||||
\usepackage{caption}
|
||||
\usepackage{subcaption}
|
||||
\usepackage{float}
|
||||
|
||||
\captionsetup[figure]{name=Εικόνα}
|
||||
|
||||
% Requires: -shell-escape compile argument
|
||||
\usepackage{minted}
|
||||
\usepackage{xcolor} %
|
||||
@ -84,10 +87,11 @@
|
||||
baselinestretch=1.1,
|
||||
tabsize=2,
|
||||
numbersep=8pt,
|
||||
startinline,
|
||||
gobble=0
|
||||
}
|
||||
|
||||
\setminted[σ;λ]{
|
||||
\setminted[sql]{
|
||||
fontsize=\small,
|
||||
breaklines,
|
||||
autogobble,
|
||||
@ -126,8 +130,8 @@
|
||||
Τα παραδοτέα της εργασίας αποτελούνται από:
|
||||
\begin{itemize}
|
||||
\item Την παρούσα αναφορά.
|
||||
\item Τον κατάλογο
|
||||
\item Το \href{\repo}{σύνδεσμο} με το αποθετήριο που περιέχει όλο το project με τον κώδικα της εφαρμογής, τον κώδικα της αναφοράς, τις διορθώσεις και τα παραδοτέα.
|
||||
\item Τον κατάλογο \textit{passman-dev/} με τον κώδικα της εφαρμογής μετά τις αλλαγές.
|
||||
\item Το \href{\repo}{σύνδεσμο} με το αποθετήριο που περιέχει όλο το project με τον κώδικα της εφαρμογής, τον κώδικα της αναφοράς, τα branch των διορθώσεων και τα παραδοτέα.
|
||||
\end{itemize}
|
||||
|
||||
\section{Περιγραφή της εφαρμογής}
|
||||
@ -160,17 +164,18 @@
|
||||
|
||||
\section{Περιβάλλον δοκιμών και μεταφορά σε Docker}
|
||||
|
||||
Πριν την ανάλυση των κενών ασφάλειας κρίθηκε απαραίτητο να δημιουργηθεί ένα ελεγχόμενο και αναπαραγώγιμο περιβάλλον δοκιμών.
|
||||
Αντί της χρήσης του προτεινόμενου περιβάλλοντος XAMPP, επιλέχθηκε η μεταφορά (porting) της εφαρμογής σε περιβάλλον Docker, το οποίο θεωρείται πιο σύγχρονη και ευέλικτη λύση.
|
||||
Πριν την ανάλυση των κενών ασφάλειας κρίθηκε απαραίτητο να δημιουργηθεί ένα \textbf{ελεγχόμενο και αναπαραγώγιμο} περιβάλλον δοκιμών.
|
||||
Αντί της χρήσης του προτεινόμενου περιβάλλοντος XAMPP, επιλέχθηκε η μεταφορά (porting) της εφαρμογής σε περιβάλλον \textbf{Docker}, το οποίο θεωρείται πιο σύγχρονη και ευέλικτη λύση.
|
||||
|
||||
Η επιλογή του Docker επιτρέπει την ακριβή καθοδήγηση των εκδόσεων λογισμικού που χρησιμοποιούνται, την εύκολη αναπαραγωγή του περιβάλλοντος σε διαφορετικά συστήματα και τον σαφή διαχωρισμό των επιμέρους υπηρεσιών.
|
||||
Συγκεκριμένα, η εφαρμογή διαχωρίστηκε σε δύο containers:
|
||||
έναν web server (Apache με PHP) και έναν database server (MariaDB).
|
||||
Συγκεκριμένα, η εφαρμογή διαχωρίστηκε αρχικά σε δύο containers: έναν web server (Apache με PHP) και έναν database server (MariaDB).
|
||||
Τον MariaDB τον χρησιμοποιούμε από το επίσημο αποθετήριο, αλλά τον web, τον χτίζουμε με δικό μας Dockerfile.
|
||||
Στο τέλος της διαδικασίας προστέθηκε ένας ακόμα (caddy) που αναλαμβάνει το reverse proxy για το https.
|
||||
|
||||
Κατά τη διαδικασία μεταφοράς απαιτήθηκαν ορισμένες στοχευμένες αλλαγές:
|
||||
\begin{itemize}
|
||||
\item \textbf{Διαχείριση HTTP headers και sessions}:
|
||||
Σε αντίθεση με το XAMPP, το περιβάλλον Docker χρησιμοποιεί αυστηρότερες ρυθμίσεις όσον αφορά την αποστολή HTTP headers.
|
||||
Σε αντίθεση με το XAMPP, το περιβάλλον του Apache-php στο Docker χρησιμοποιεί αυστηρότερες ρυθμίσεις όσον αφορά την αποστολή HTTP headers.
|
||||
Αυτό αποκάλυψε προβλήματα στη ροή του κώδικα, όπου HTML output προηγούνταν κλήσεων όπως \textit{session\_start()} και \textit{header()}.
|
||||
Η λύση ήταν η αναδιάταξη του κώδικα, ώστε όλη η λογική ελέγχου και ανακατεύθυνσης να εκτελείται πριν από οποιοδήποτε HTML output.
|
||||
|
||||
@ -199,7 +204,8 @@ docker compose up -d --build
|
||||
Τα δεδομένα της βάσης αποθηκεύονται σε Docker named volume, εξασφαλίζοντας τη διατήρησή τους μεταξύ επανεκκινήσεων των containers.
|
||||
|
||||
Μετά την επιτυχή εκκίνηση, η εφαρμογή είναι προσβάσιμη μέσω web browser στη διεύθυνση:\\
|
||||
\texttt{http://localhost/passman}
|
||||
\texttt{http://localhost/passman}.\\
|
||||
Για περισσότερες πληροφορίες μπορείτε να επισκεφτείτε το \href{\repo}{αποθετήριο} της εφαρμογής.
|
||||
|
||||
Για λόγους δοκιμών και ανάλυσης, είναι επίσης δυνατή η απευθείας πρόσβαση στη βάση δεδομένων μέσω τερματικού, χρησιμοποιώντας την εντολή:
|
||||
\begin{minted}[fontsize=\small]{bash}
|
||||
@ -212,20 +218,17 @@ docker compose exec db mariadb -uroot -prootpass
|
||||
\label{sec:sqli}
|
||||
|
||||
Το πρώτο κενό ασφάλειας που εξετάζεται αφορά την ευπάθεια σε επιθέσεις SQL Injection.
|
||||
Η ευπάθεια αυτή προκύπτει όταν δεδομένα που παρέχονται από τον χρήστη ενσωματώνονται απευθείας σε SQL εντολές, χωρίς κατάλληλη επικύρωση ή χρήση μηχανισμών παραμετροποίησης (prepared statements).
|
||||
Η ευπάθεια αυτή προκύπτει όταν δεδομένα που παρέχονται από τον χρήστη \textbf{ενσωματώνονται απευθείας} σε SQL εντολές, χωρίς κατάλληλη επικύρωση ή χρήση μηχανισμών παραμετροποίησης (prepared statements).
|
||||
Αποτέλεσμα είναι ο επιτιθέμενος να μπορεί να αλλοιώσει τη λογική του SQL query, οδηγώντας σε μη εξουσιοδοτημένη πρόσβαση ή/και διαρροή δεδομένων.
|
||||
|
||||
Στην εφαρμογή, η ευπάθεια εντοπίζεται σε πολλά σημεία και παρότι θα τα λύσουμε όλα εδώ παραθέτουμε ως παράδειγμα την ευπάθεια στο \textit{login.php}, καθώς αυτό είναι ίσως οτ πιο σοβαρό.
|
||||
Στην εφαρμογή, η ευπάθεια εντοπίζεται σε πολλά σημεία και παρότι θα τα λύσουμε όλα, εδώ παραθέτουμε ως παράδειγμα την ευπάθεια στο \textit{login.php}, καθώς αυτό είναι ίσως οτ πιο σοβαρό.
|
||||
Η είσοδος του χρήστη (username/password), όπως φαίνεται στο απόσπασμα παρακάτω, εισάγεται απευθείας στο query αυθεντικοποίησης μέσω string concatenation.
|
||||
\begin{minted}{php}
|
||||
$sql_query =
|
||||
"SELECT * FROM login_users WHERE username='{$username}' AND password='{$password}';";
|
||||
// The SQL query is constructed by directly concatenating user-controlled input.
|
||||
// This enables SQL injection, because the attacker can break out of the quoted string
|
||||
// and inject additional SQL conditions (e.g., OR 1=1), bypassing authentication.
|
||||
\end{minted}
|
||||
\label{lst:sqli-login-query}
|
||||
Στο συγκεκριμένο σημείο, το περιεχόμενο των μεταβλητών \textit{\$username} και \textit{\$password} δεν υφίσταται κανέναν έλεγχο ή escaping.
|
||||
Στο συγκεκριμένο σημείο, το περιεχόμενο των μεταβλητών \textit{\$username} και \textit{\$password} \textbf{δεν υφίσταται κανέναν έλεγχο ή escaping}.
|
||||
Επομένως, ένας επιτιθέμενος μπορεί να εισάγει ειδικούς χαρακτήρες (π.χ. \texttt{'}) και SQL τελεστές (π.χ. \texttt{OR}) ώστε να παρακάμψει τη συνθήκη αυθεντικοποίησης.
|
||||
|
||||
\begin{figure}[!ht]
|
||||
@ -271,18 +274,17 @@ $sql_query =
|
||||
\label{fig:sqli_db_users}
|
||||
\end{figure}
|
||||
|
||||
Η συγκεκριμένη ευπάθεια θεωρείται ιδιαίτερα σοβαρή, καθώς υπονομεύει πλήρως τον μηχανισμό αυθεντικοποίησης και επιτρέπει μη εξουσιοδοτημένη πρόσβαση σε ευαίσθητα δεδομένα.
|
||||
Στα επόμενα τμήματα παρουσιάζεται η αντιμετώπισή της με χρήση prepared statements, ώστε τα δεδομένα του χρήστη να μην μπορούν να επηρεάσουν τη σύνταξη της SQL εντολής.
|
||||
Η συγκεκριμένη ευπάθεια θεωρείται \textbf{ιδιαίτερα σοβαρή}, καθώς υπονομεύει πλήρως τον μηχανισμό αυθεντικοποίησης και \textbf{επιτρέπει μη εξουσιοδοτημένη πρόσβαση σε ευαίσθητα δεδομένα}.
|
||||
|
||||
\subsection{Αντιμετώπιση SQL Injection}
|
||||
\label{sec:sqli_fix}
|
||||
|
||||
Η αντιμετώπιση του SQL Injection βασίζεται στη θεμελιώδη αρχή του διαχωρισμού \textit{δεδομένων} από \textit{εντολές}.
|
||||
Το πρόβλημα στην αρχική υλοποίηση προέκυπτε επειδή το SQL query κατασκευαζόταν μέσω απλής συνένωσης (string concatenation) με δεδομένα που παρείχε ο χρήστης.
|
||||
Το πρόβλημα στην αρχική υλοποίηση προέκυπτε επειδή το SQL query κατασκευαζόταν μέσω \textbf{απλής συνένωσης (string concatenation)} με δεδομένα που παρείχε ο χρήστης.
|
||||
Έτσι, ειδικοί χαρακτήρες και τελεστές SQL μπορούσαν να εισαχθούν ως μέρος της συμβολοσειράς, με αποτέλεσμα να αλλοιώνεται η σύνταξη και η λογική της SQL εντολής.
|
||||
|
||||
Η καθιερωμένη πρακτική αντιμετώπισης, η οποία προτείνεται τόσο από τις επίσημες οδηγίες ασφαλούς κωδικοποίησης (secure coding guidelines) όσο και από τον οργανισμό OWASP (Open Worldwide Application Security Project), είναι η χρήση \textit{prepared statements} με παραμέτρους (parameterized queries).
|
||||
Με τη χρήση prepared statements, ο SQL server λαμβάνει πρώτα το statement (template) και στη συνέχεια τις παραμέτρους ως δεδομένα, χωρίς να επιτρέπεται η ερμηνεία τους ως τμήμα της SQL σύνταξης.
|
||||
Η καθιερωμένη πρακτική αντιμετώπισης, η οποία προτείνεται τόσο από τις επίσημες οδηγίες ασφαλούς κωδικοποίησης (secure coding guidelines) όσο και από τον οργανισμό OWASP (Open Worldwide Application Security Project), είναι η χρήση \textbf{prepared statements} με παραμέτρους (parameterized queries).
|
||||
Με τη χρήση prepared statements, ο SQL server λαμβάνει πρώτα το statement (template) και στη συνέχεια τις \textbf{παραμέτρους ως δεδομένα}, χωρίς να επιτρέπεται η ερμηνεία τους ως τμήμα της SQL σύνταξης.
|
||||
Ως αποτέλεσμα, οποιαδήποτε κακόβουλη είσοδος αντιμετωπίζεται ως απλό κείμενο (data) και όχι ως εκτελέσιμο SQL.
|
||||
|
||||
\subsection{Υλοποίηση της διόρθωσης}
|
||||
@ -350,55 +352,46 @@ $sql_query =
|
||||
|
||||
\subsection{Περαιτέρω σημεία ευπάθειας σε SQL Injection}
|
||||
|
||||
Πέρα από το αρχικό κενό ασφάλειας στον μηχανισμό αυθεντικοποίησης, εντοπίστηκαν επιπλέον σημεία στην εφαρμογή όπου SQL εντολές κατασκευάζονταν δυναμικά μέσω συνένωσης συμβολοσειρών με δεδομένα προερχόμενα από τον χρήστη ή από session μεταβλητές.
|
||||
Πέρα από το αρχικό κενό ασφάλειας στον μηχανισμό αυθεντικοποίησης, εντοπίστηκαν \textbf{επιπλέον σημεία} στην εφαρμογή όπου SQL εντολές κατασκευάζονταν δυναμικά μέσω συνένωσης συμβολοσειρών με δεδομένα προερχόμενα από τον χρήστη ή από session μεταβλητές.
|
||||
Τα σημεία αυτά δημιουργούν πρόσθετες επιφάνειες επίθεσης και καθιστούν την εφαρμογή ευάλωτη σε άμεσες ή έμμεσες (second-order) επιθέσεις SQL Injection.
|
||||
|
||||
\subsubsection{Εγγραφή νέων χρηστών (\textit{register.php})}
|
||||
Στο αρχείο \textit{register.php}, η εγγραφή νέου χρήστη υλοποιούνταν με απευθείας ενσωμάτωση των πεδίων \textit{username} και \textit{password} σε εντολή \texttt{INSERT}.
|
||||
Το Απόσπασμα~\ref{lst:sqli-register} παρουσιάζει τον προβληματικό κώδικα.
|
||||
|
||||
Το Απόσπασμα παρακάτω παρουσιάζει τον προβληματικό κώδικα:
|
||||
\begin{minted}{php}
|
||||
// Vulnerable SQL construction: user-controlled input is concatenated directly.
|
||||
$sql_query = "INSERT INTO login_users (username,password)
|
||||
VALUES ('{$new_username}','{$new_password}');";
|
||||
VALUES ('{$new_username}','{$new_password}');";
|
||||
$result = $conn->query($sql_query);
|
||||
\end{minted}
|
||||
\label{lst:sqli-register}
|
||||
|
||||
Η πρακτική αυτή επιτρέπει την αλλοίωση της SQL σύνταξης μέσω κατάλληλης εισόδου, καθιστώντας δυνατή την εκτέλεση αυθαίρετων SQL εντολών.
|
||||
Η διόρθωση πραγματοποιήθηκε με χρήση prepared statements και δεσμευμένων παραμέτρων.
|
||||
|
||||
\subsubsection{Αποθήκευση σημειώσεων (\textit{notes.php})}
|
||||
Στο \textit{notes.php}, η αποθήκευση νέων σημειώσεων πραγματοποιούνταν με δυναμική κατασκευή εντολής \texttt{INSERT}, η οποία περιλάμβανε τόσο το περιεχόμενο της σημείωσης όσο και το \textit{username} του συνδεδεμένου χρήστη.
|
||||
Ο προβληματικός κώδικας φαίνεται στο Απόσπασμα~\ref{lst:sqli-notes}.
|
||||
|
||||
Ο προβληματικός κώδικας φαίνεται στο Απόσπασμα εδώ:
|
||||
\begin{minted}{php}
|
||||
// Vulnerable SQL construction: note content and session data are injected into SQL.
|
||||
$sql_query = "INSERT INTO notes (login_user_id, note)
|
||||
VALUES ((SELECT id FROM login_users
|
||||
WHERE username='{$username}'), '{$new_note}')";
|
||||
VALUES ((SELECT id FROM login_users WHERE username='{$username}'), '{$new_note}')";
|
||||
$result = $conn->query($sql_query);
|
||||
\end{minted}
|
||||
\label{lst:sqli-notes}
|
||||
|
||||
Παρότι το \textit{username} προέρχεται από session μεταβλητή, δεν μπορεί να θεωρηθεί έμπιστο, καθώς μπορεί να αλλοιωθεί σε σενάρια XSS ή session hijacking.
|
||||
Η διόρθωση βασίστηκε στη χρήση prepared statements τόσο για το υποερώτημα όσο και για το περιεχόμενο της σημείωσης.
|
||||
|
||||
\subsubsection{Διαχείριση διαπιστευτηρίων ιστοσελίδων (\textit{dashboard.php})}
|
||||
Στο αρχείο \textit{dashboard.php} εντοπίστηκαν πολλαπλά σημεία SQL Injection που αφορούν την εισαγωγή, διαγραφή και προβολή αποθηκευμένων διαπιστευτηρίων.
|
||||
Ενδεικτικά, το Απόσπασμα~\ref{lst:sqli-dashboard-insert} δείχνει τον τρόπο εισαγωγής νέας εγγραφής.
|
||||
|
||||
Ενδεικτικά, το Απόσπασμα παρακάτω δείχνει τον τρόπο εισαγωγής νέας εγγραφής:
|
||||
\begin{minted}{php}
|
||||
// Vulnerable SQL construction: multiple user-controlled fields concatenated.
|
||||
$sql_query = "INSERT INTO websites (login_user_id,web_url,web_username,web_password)
|
||||
VALUES ((SELECT id FROM login_users
|
||||
WHERE username='{$username}'),
|
||||
'{$new_website}','{$new_username}','{$new_password}');";
|
||||
VALUES ((SELECT id FROM login_users WHERE username='{$username}'),
|
||||
'{$new_website}','{$new_username}','{$new_password}');";
|
||||
$result = $conn->query($sql_query);
|
||||
\end{minted}
|
||||
\label{lst:sqli-dashboard-insert}
|
||||
|
||||
Αντίστοιχα, η διαγραφή εγγραφών βασιζόταν σε δυναμικά κατασκευασμένη εντολή \texttt{DELETE}, όπως φαίνεται στο Απόσπασμα~\ref{lst:sqli-dashboard-delete}.
|
||||
Αντίστοιχα, η διαγραφή εγγραφών βασιζόταν σε δυναμικά κατασκευασμένη εντολή \texttt{DELETE}, όπως φαίνεται στο επόμενο Απόσπασμα.
|
||||
|
||||
\begin{minted}{php}
|
||||
// Vulnerable DELETE statement: identifier injected directly into SQL.
|
||||
@ -411,14 +404,13 @@ $sql_query =
|
||||
\begin{minted}{php}
|
||||
// Vulnerable SELECT statement: session data treated as trusted input.
|
||||
$sql_query = "SELECT * FROM websites
|
||||
INNER JOIN login_users
|
||||
ON websites.login_user_id=login_users.id
|
||||
WHERE login_users.username='{$username}';";
|
||||
INNER JOIN login_users ON websites.login_user_id=login_users.id
|
||||
WHERE login_users.username='{$username}';";
|
||||
$result = $conn->query($sql_query);
|
||||
\end{minted}
|
||||
\label{lst:sqli-dashboard-select}
|
||||
|
||||
Η συνολική αντιμετώπιση των παραπάνω σημείων βασίστηκε στην ίδια αρχή με το αρχικό SQL Injection, ότι καμία τιμή που επηρεάζει τη σύνταξη SQL εντολής δεν ενσωματώνεται πλέον απευθείας στο query.
|
||||
Η συνολική αντιμετώπιση των παραπάνω σημείων βασίστηκε στην ίδια αρχή με το αρχικό SQL Injection, ότι \textbf{καμία τιμή που επηρεάζει τη σύνταξη SQL εντολής δεν ενσωματώνεται πλέον απευθείας στο query}.
|
||||
Όλα τα παραπάνω αντικαταστάθηκαν με prepared statements και δεσμευμένες παραμέτρους, εξαλείφοντας τόσο άμεσα όσο και έμμεσα σενάρια SQL Injection.
|
||||
Ο αναγνώστης μπορεί \href{\fixsqlitag}{εδώ} να βρει το branch με όλες τις αλλαγές.
|
||||
|
||||
@ -436,10 +428,9 @@ $sql_query =
|
||||
Η ευπάθεια προκύπτει όταν περιεχόμενο που εισάγει ο χρήστης αποθηκεύεται στη βάση δεδομένων και στη συνέχεια προβάλλεται -- πιθανόν σε άλλους χρήστες -- χωρίς κατάλληλη κωδικοποίηση εξόδου (output encoding).
|
||||
Ως αποτέλεσμα, κακόβουλος κώδικας JavaScript μπορεί να εκτελεστεί στον browser του θύματος με τα δικαιώματα του αντίστοιχου session.
|
||||
|
||||
\subsection{Root cause στον κώδικα}
|
||||
\subsection{XSS στο \textit{notes.php}}
|
||||
Στο \textit{notes.php}, οι αποθηκευμένες σημειώσεις προβάλλονται απευθείας μέσα σε HTML με χρήση \texttt{echo}, χωρίς escaping.
|
||||
Ενδεικτικά, το Απόσπασμα~\ref{lst:xss-notes-echo} δείχνει το προβληματικό σημείο: το \texttt{\$row["note"]} (δεδομένο από τη βάση) θεωρείται ως έμπιστο και εισάγεται αυτούσιο στο DOM.
|
||||
|
||||
Ενδεικτικά, το Απόσπασμα παρακάτω δείχνει το προβληματικό σημείο: το \texttt{\$row["note"]} (δεδομένο από τη βάση) θεωρείται ως έμπιστο και εισάγεται αυτούσιο στο DOM.
|
||||
\begin{minted}{php}
|
||||
while ($row = $result -> fetch_assoc()) {
|
||||
echo "<div class='note'>";
|
||||
@ -521,7 +512,7 @@ $sql_query =
|
||||
\subsubsection{Session hijacking demonstration μέσω \textit{usecookie.php}}
|
||||
\label{sec:xss_hijack}
|
||||
|
||||
Ο αντίκτυπος της ευπάθειας είναι ιδιαίτερα σοβαρός, καθώς το session cookie μπορεί να χρησιμοποιηθεί για πλαστοπροσωπία (session hijacking).
|
||||
Ο αντίκτυπος της ευπάθειας είναι ιδιαίτερα σοβαρός, καθώς το \textbf{session cookie μπορεί να χρησιμοποιηθεί για πλαστοπροσωπία} (session hijacking).
|
||||
Στην Εικόνα~\ref{fig:xss_usecookie} φαίνεται η χρήση του \textit{usecookie.php} με παράμετρο το κλεμμένο \texttt{PHPSESSID}.
|
||||
Στη συνέχεια, η πρόσβαση σε προστατευμένη σελίδα της εφαρμογής (π.χ. \textit{dashboard.php}) είναι εφικτή ως ο χρήστης-θύμα, όπως φαίνεται στην Εικόνα~\ref{fig:xss_dashboard_after_hijack}.
|
||||
|
||||
@ -543,15 +534,13 @@ $sql_query =
|
||||
\end{figure}
|
||||
|
||||
Με βάση τα παραπάνω, επιβεβαιώνεται ότι η εφαρμογή επιτρέπει αποθήκευση και εκτέλεση κακόβουλου JS κώδικα μέσω του μηχανισμού σημειώσεων, με αποτέλεσμα τόσο την εξαγωγή cookies όσο και την πρακτική επίδειξη πλαστοπροσωπίας.
|
||||
Στα επόμενα τμήματα παρουσιάζεται η αντιμετώπιση της ευπάθειας με \textit{context-aware output encoding}, ώστε το περιεχόμενο της βάσης να αντιμετωπίζεται ως μη-έμπιστο και να προβάλλεται με ασφάλεια.
|
||||
|
||||
|
||||
\subsection{Αντιμετώπιση Stored XSS στο \textit{notes.php}}
|
||||
\label{sec:xss_fix}
|
||||
|
||||
Η αντιμετώπιση του Stored XSS βασίζεται στην αρχή ότι \textit{οποιοδήποτε περιεχόμενο προέρχεται από τον χρήστη ή/και τη βάση δεδομένων πρέπει να θεωρείται μη-έμπιστο}.
|
||||
Η αντιμετώπιση του Stored XSS βασίζεται στην αρχή ότι \textbf{οποιοδήποτε περιεχόμενο προέρχεται από τον χρήστη ή/και τη βάση δεδομένων πρέπει να θεωρείται μη-έμπιστο}.
|
||||
Στην αρχική υλοποίηση, το περιεχόμενο των σημειώσεων εμφανιζόταν απευθείας στο HTML, με αποτέλεσμα ο browser να το ερμηνεύει ως markup και να εκτελεί τυχόν ενσωματωμένο JavaScript.
|
||||
Η καθιερωμένη πρακτική για την αποτροπή XSS σε \textit{HTML body context} είναι το \textit{context-aware output encoding}, δηλαδή η κωδικοποίηση ειδικών χαρακτήρων (π.χ. \texttt{<}, \texttt{>}, \texttt{"}, \texttt{'}) πριν από την προβολή τους.
|
||||
Η καθιερωμένη πρακτική για την αποτροπή XSS σε \textit{HTML body context} είναι το \textbf{context-aware output encoding}, δηλαδή η κωδικοποίηση ειδικών χαρακτήρων (π.χ. \texttt{<}, \texttt{>}, \texttt{"}, \texttt{'}) πριν από την προβολή τους.
|
||||
Με αυτόν τον τρόπο, ακόμη και αν στη βάση υπάρχουν αποθηκευμένα payloads, αυτά προβάλλονται ως απλό κείμενο και δεν είναι δυνατόν να εκτελεστούν.
|
||||
|
||||
\subsubsection{Υλοποίηση της διόρθωσης}
|
||||
@ -588,11 +577,11 @@ $sql_query =
|
||||
\label{fig:xss_fix_weblogs}
|
||||
\end{figure}
|
||||
|
||||
Συνεπώς, η εφαρμογή \textit{context-aware output encoding} στο σημείο προβολής εξαλείφει την εκτέλεση αποθηκευμένων XSS payloads στο συγκεκριμένο context, διατηρώντας αμετάβλητη τη λειτουργικότητα της εφαρμογής.
|
||||
Συνεπώς, η εφαρμογή \textit{context-aware output encoding} στο σημείο προβολής \textbf{εξαλείφει την εκτέλεση αποθηκευμένων XSS payloads} στο συγκεκριμένο context, διατηρώντας αμετάβλητη τη λειτουργικότητα της εφαρμογής.
|
||||
|
||||
\subsection{Επέκταση της αντιμετώπισης XSS στα επιπλέον σημεία}
|
||||
|
||||
Η ευπάθεια Stored XSS που παρουσιάστηκε μέσω της λειτουργίας σημειώσεων δεν αποτελεί μεμονωμένο περιστατικό, αλλά ενδεικτικό ενός γενικότερου προγραμματιστικού μοτίβου στην εφαρμογή.
|
||||
Η ευπάθεια Stored XSS που παρουσιάστηκε μέσω της λειτουργίας σημειώσεων \textbf{δεν αποτελεί μεμονωμένο περιστατικό}, αλλά ενδεικτικό ενός γενικότερου προγραμματιστικού μοτίβου στην εφαρμογή.
|
||||
Δεδομένα προερχόμενα από χρήστες ή από τη βάση δεδομένων προβάλλονταν απευθείας στο HTML χωρίς κατάλληλη κωδικοποίηση εξόδου.
|
||||
Για τον λόγο αυτό πραγματοποιήθηκε εκτενέστερη ανάλυση και εντοπίστηκαν επιπλέον σημεία με δυνητική ή άμεση ευπάθεια σε XSS.
|
||||
|
||||
@ -607,7 +596,7 @@ $sql_query =
|
||||
\end{minted}
|
||||
|
||||
Η πρακτική αυτή επιτρέπει την αποθήκευση και εκτέλεση κακόβουλου HTML ή JavaScript κώδικα (\textit{Stored XSS}), με εκτέλεση του payload κάθε φορά που προβάλλεται η σελίδα.
|
||||
Η αντιμετώπιση πραγματοποιήθηκε με εφαρμογή \textit{context-aware output encoding} σε όλα τα αντίστοιχα πεδία.
|
||||
|
||||
|
||||
\subsubsection{Εμφάνιση ονόματος χρήστη από session (\textit{dashboard.php})}
|
||||
Στην ίδια σελίδα, το όνομα του συνδεδεμένου χρήστη εμφανιζόταν στο header της σελίδας.
|
||||
@ -619,7 +608,7 @@ $sql_query =
|
||||
|
||||
|
||||
Παρότι τα session δεδομένα συχνά θεωρούνται έμπιστα, στην πράξη μπορούν να αλλοιωθούν σε σενάρια XSS, session hijacking ή cookie tampering.
|
||||
Η συγκεκριμένη περίπτωση αποτελεί χαρακτηριστικό παράδειγμα \textit{second-order XSS}.
|
||||
Η συγκεκριμένη περίπτωση αποτελεί χαρακτηριστικό παράδειγμα \textbf{second-order XSS}.
|
||||
Η αντιμετώπιση βασίστηκε στην κωδικοποίηση της τιμής πριν την εμφάνιση της.
|
||||
|
||||
\subsubsection{Reflected XSS σε μηνύματα σφάλματος (\textit{login.php}, \textit{register.php})}
|
||||
@ -631,8 +620,8 @@ $sql_query =
|
||||
\end{minted}
|
||||
|
||||
|
||||
Στην παρούσα υλοποίηση, οι μεταβλητές αυτές λαμβάνουν μόνο στατικές συμβολοσειρές και δεν ενσωματώνουν δεδομένα εισόδου χρήστη.
|
||||
Ως εκ τούτου, δεν είναι πρακτικά εκμεταλλεύσιμες για Reflected XSS στην τρέχουσα μορφή της εφαρμογής.
|
||||
Στην παρούσα υλοποίηση, οι μεταβλητές αυτές λαμβάνουν μόνο \textbf{στατικές συμβολοσειρές} και δεν ενσωματώνουν δεδομένα εισόδου χρήστη.
|
||||
Ως εκ τούτου, δεν είναι πρακτικά εκμεταλλεύσιμες για \textit{reflected XSS} στην τρέχουσα μορφή της εφαρμογής.
|
||||
Για τον λόγο αυτό, δεν πραγματοποιήθηκε περαιτέρω εκμετάλλευση, αλλά το σημείο καταγράφεται ως θεωρητικό σενάριο που θα απαιτούσε αντιμετώπιση σε μελλοντική επέκταση της εφαρμογής.
|
||||
Ο αναγνώστης μπορεί \href{\fixxsstag}{εδώ} να βρει το branch με όλες τις αλλαγές.
|
||||
|
||||
@ -644,34 +633,31 @@ $sql_query =
|
||||
\section{Αποθήκευση κωδικών αυθεντικοποίησης σε απλό κείμενο}
|
||||
\label{subsec:plaintext-auth}
|
||||
|
||||
Η εφαρμογή, στην αρχική της υλοποίηση, διαχειριζόταν τους κωδικούς αυθεντικοποίησης των χρηστών (login passwords) ως απλό κείμενο (plaintext).
|
||||
Η εφαρμογή, στην αρχική της υλοποίηση, διαχειριζόταν τους \textbf{κωδικούς} αυθεντικοποίησης των χρηστών (login passwords) ως \textbf{απλό κείμενο (plaintext)}.
|
||||
Το πρόβλημα αυτό εμφανίζεται σε περισσότερα του ενός σημεία της εφαρμογής:
|
||||
|
||||
\begin{enumerate}
|
||||
\item \textbf{Πίνακας βάσης δεδομένων \texttt{login\_users}:}
|
||||
Οι κωδικοί πρόσβασης αποθηκεύονταν αυτούσιοι στη βάση δεδομένων, γεγονός που μπορεί να επιβεβαιωθεί με απευθείας ερώτημα SQL.
|
||||
Η αποθήκευση του κωδικού σε απλό κείμενο είναι άμεσα ορατή και στο περιεχόμενο της βάσης δεδομένων, όπως φαίνεται στην Εικόνα~\ref{fig:pltxt_vuln_logins}.
|
||||
\begin{figure}[!ht]
|
||||
\centering
|
||||
\includegraphics[width=0.7\textwidth]{img/pltxt-VulnLogins.png}
|
||||
\caption{Κωδικοί χρηστών σε απλό κείμενο.}
|
||||
\label{fig:pltxt_vuln_logins}
|
||||
\end{figure}
|
||||
|
||||
\item \textbf{Διαδικασία εγγραφής χρήστη (\texttt{register.php}):}
|
||||
Ο κωδικός που εισάγεται από τον χρήστη αποθηκευόταν στη βάση χωρίς καμία μορφή μετασχηματισμού (hashing).
|
||||
\begin{minted}{php}
|
||||
$sql_query = "INSERT INTO login_users (username,password) VALUES
|
||||
('{$new_username}','{$new_password}');";
|
||||
$result = $conn->query($sql_query);
|
||||
\end{minted}
|
||||
|
||||
\item \textbf{Διαδικασία σύνδεσης χρήστη (\texttt{login.php}):}
|
||||
Η αυθεντικοποίηση πραγματοποιούνταν με άμεση σύγκριση του κωδικού που εισάγει ο χρήστης με τον αποθηκευμένο κωδικό, μέσω της SQL εντολής \texttt{WHERE username = ? AND password = ?}.
|
||||
|
||||
\item \textbf{Αποθήκευση κωδικών τρίτων ιστοσελίδων (\texttt{dashboard.php}):}
|
||||
Οι κωδικοί πρόσβασης ιστοσελίδων αποθηκεύονται και εμφανίζονται επίσης ως απλό κείμενο. Το συγκεκριμένο ζήτημα αναλύεται ξεχωριστά σε επόμενη ενότητα, καθώς απαιτεί διαφορετική προσέγγιση (κρυπτογράφηση αντί για hashing).
|
||||
\end{enumerate}
|
||||
|
||||
\subsection{Επιπτώσεις ασφάλειας και σενάρια εκμετάλλευσης}
|
||||
|
||||
Η αποθήκευση κωδικών αυθεντικοποίησης σε απλό κείμενο αυξάνει δραματικά τον αντίκτυπο οποιασδήποτε παραβίασης της βάσης δεδομένων.
|
||||
Σε περίπτωση που ένας επιτιθέμενος αποκτήσει πρόσβαση ανάγνωσης στη βάση (π.χ. μέσω SQL Injection, διαρροής backup ή εσφαλμένων δικαιωμάτων), μπορεί να ανακτήσει άμεσα όλους τους κωδικούς χρηστών χωρίς καμία επιπλέον προσπάθεια.
|
||||
|
||||
Επιπλέον, επειδή η αυθεντικοποίηση βασιζόταν σε σύγκριση plaintext τιμών εντός της SQL εντολής, η διαρροή του πίνακα \texttt{login\_users} οδηγεί άμεσα σε πλήρη παραβίαση όλων των λογαριασμών, χωρίς να απαιτείται σπάσιμο (cracking) κωδικών ή hashes.
|
||||
|
||||
\subsection{Ευάλωτη υλοποίηση}
|
||||
Στην αρχική υλοποίηση, η σύγκριση του κωδικού πραγματοποιούνταν απευθείας μέσα στη SQL εντολή αυθεντικοποίησης.
|
||||
|
||||
\begin{minted}{php}
|
||||
Η αυθεντικοποίηση πραγματοποιούνταν μέσω της SQL εντολής \texttt{WHERE username = ? AND password = ?}, με άμεση σύγκριση του κωδικού που εισάγει ο χρήστης με τον αποθηκευμένο κωδικό.
|
||||
\begin{minted}{php}
|
||||
$sql_query = "SELECT * FROM login_users WHERE username='{$username}'
|
||||
AND password='{$password}';";
|
||||
$result = $conn->query($sql_query);
|
||||
@ -681,31 +667,49 @@ $sql_query =
|
||||
$_SESSION['loggedin'] = true;
|
||||
// ...
|
||||
}
|
||||
\end{minted}
|
||||
\end{minted}
|
||||
|
||||
Αντίστοιχα, κατά την εγγραφή νέου χρήστη, ο κωδικός αποθηκευόταν αυτούσιος στη βάση δεδομένων.
|
||||
\begin{minted}{php}
|
||||
$sql_query = "INSERT INTO login_users (username,password) VALUES
|
||||
('{$new_username}','{$new_password}');";
|
||||
$result = $conn->query($sql_query);
|
||||
\end{minted}
|
||||
\item \textbf{Αποθήκευση κωδικών τρίτων ιστοσελίδων (\texttt{dashboard.php}):}
|
||||
Οι κωδικοί πρόσβασης ιστοσελίδων αποθηκεύονται και εμφανίζονται επίσης ως απλό κείμενο.
|
||||
Η λύση του συγκεκριμένου ζητήματος απαιτεί τη δημιουργία επιπλέον κώδικα για την κωδικοποίηση και αποκωδικοποίηση των δεδομένων, αλλά και αρκετές αλλαγές σε επίπεδο αρχητεκτονικής.
|
||||
Για παράδειγμα πρέπει να αποφασιστεί που θα αποθηκεύονται τα κλειδιά κωδικοποίησης, πως θα γίνει η αναδιάρθρωση του κώδικα για να αρχικοποιούνται τα κλειδιά κλπ.
|
||||
Για το λόγο αυτό \textbf{η υλοποίησή κρίθηκε εκτός ορίων για τη συγκεκριμένη εργασία}.
|
||||
|
||||
Η αποθήκευση του κωδικού σε απλό κείμενο είναι άμεσα ορατή και στο περιεχόμενο
|
||||
της βάσης δεδομένων, όπως φαίνεται στο αντίστοιχο στιγμιότυπο οθόνης.
|
||||
\begin{figure}[!ht]
|
||||
\centering
|
||||
\includegraphics[width=0.7\textwidth]{img/pltxt-VulnLogins.png}
|
||||
\caption{Κωδικοί χρηστών σε απλό κείμενο.}
|
||||
\label{fig:pltxt_vuln_logins}
|
||||
\end{figure}
|
||||
Αν όμως θα υλοποιούσαμε τέτοια αλλαγή, η λύση θα έπρεπε να προσθέσει ένα νέο αρχείο, π.χ. \textit{crypto.php}, το οποίο θα περιείχε βοηθητικές συναρτήσεις συμμετρικής κρυπτογράφησης με authenticated encryption (π.χ. AES-256-GCM).
|
||||
Ενδεικτικά:
|
||||
\begin{enumerate}
|
||||
\item \textbf{\texttt{pm\_get\_key()}}: ανάκτηση του μυστικού κλειδιού από environment variable (π.χ. \texttt{APP\_ENC\_KEY}) και παραγωγή σταθερού 32-byte key (π.χ. με \texttt{hash('sha256', ... , true)}), ώστε το κλειδί να μην βρίσκεται ποτέ hard-coded στο repository.
|
||||
|
||||
\item \textbf{\texttt{pm\_encrypt(\$plaintext)}}: κρυπτογράφηση του password πριν την αποθήκευση, με τυχαίο IV (\texttt{random\_bytes}) και παραγωγή authentication tag, και επιστροφή ενός base64 blob (π.χ. \texttt{base64(iv || tag || cipher)}) κατάλληλου για αποθήκευση σε \texttt{VARCHAR}.
|
||||
|
||||
\item \textbf{\texttt{pm\_decrypt(\$blob)}}: αποκρυπτογράφηση του αποθηκευμένου blob κατά την προβολή, με έλεγχο εγκυρότητας (base64 + μήκος + tag), και (προαιρετικά) fallback σε plaintext για παλαιές εγγραφές ώστε να διευκολυνθεί η μετάβαση (migration).
|
||||
\end{enumerate}
|
||||
|
||||
Στη συνέχεια, θα έπρεπε να γίνουν στοχευμένες αλλαγές στο \textit{dashboard.php}:
|
||||
\begin{enumerate}
|
||||
\item Στο σημείο εισαγωγής νέας εγγραφής, να γίνεται \texttt{require\_once 'crypto.php'} και να αντικαθίσταται η αποθήκευση του \texttt{\$new\_password} με \texttt{\$enc\_password = pm\_encrypt(\$new\_password)} πριν το \texttt{INSERT} (ώστε στη βάση να καταλήγει μόνο ciphertext), και
|
||||
|
||||
\item Στο σημείο προβολής της λίστας, να γίνεται \texttt{\$plain\_pass = pm\_decrypt(\$row['web\_password'])} πριν το \texttt{htmlspecialchars} και την εκτύπωση στο HTML (ώστε να εμφανίζεται στον χρήστη το πραγματικό password, αλλά να παραμένει κρυπτογραφημένο στην αποθήκευση).
|
||||
|
||||
\item Τέλος, θα απαιτούνταν μία διαδικασία migration (π.χ. προσωρινό script) για να κρυπτογραφηθούν οι ήδη αποθηκευμένοι plaintext κωδικοί στο \texttt{websites} table, καθώς και ρύθμιση του container/compose ώστε να ορίζεται το \texttt{APP\_ENC\_KEY} ως secret μέσω environment variables.
|
||||
\end{enumerate}
|
||||
|
||||
\end{enumerate}
|
||||
|
||||
\subsection{Επιπτώσεις ασφάλειας και σενάρια εκμετάλλευσης}
|
||||
|
||||
Η αποθήκευση κωδικών αυθεντικοποίησης σε απλό κείμενο αυξάνει δραματικά τον αντίκτυπο οποιασδήποτε παραβίασης της βάσης δεδομένων.
|
||||
Σε περίπτωση που ένας επιτιθέμενος αποκτήσει πρόσβαση ανάγνωσης στη βάση (π.χ. μέσω SQL Injection, διαρροής backup ή εσφαλμένων δικαιωμάτων), μπορεί να ανακτήσει άμεσα όλους τους κωδικούς χρηστών χωρίς καμία επιπλέον προσπάθεια.
|
||||
|
||||
Επιπλέον, επειδή η αυθεντικοποίηση βασιζόταν σε σύγκριση plaintext τιμών εντός της SQL εντολής, η διαρροή του πίνακα \texttt{login\_users} οδηγεί άμεσα σε πλήρη παραβίαση όλων των λογαριασμών, χωρίς να απαιτείται σπάσιμο (cracking) κωδικών ή hashes.
|
||||
|
||||
\subsection{Προσέγγιση αντιμετώπισης}
|
||||
Για την ασφαλή διαχείριση κωδικών αυθεντικοποίησης, οι κωδικοί δεν πρέπει ποτέ να αποθηκεύονται σε απλό κείμενο.
|
||||
Για την ασφαλή διαχείριση κωδικών αυθεντικοποίησης, \textbf{οι κωδικοί δεν πρέπει ποτέ να αποθηκεύονται σε απλό κείμενο}.
|
||||
Η καθιερωμένη πρακτική είναι:
|
||||
|
||||
\begin{itemize}
|
||||
\item χρήση συναρτήσεων hashing με ενσωματωμένο salt κατά την εγγραφή,
|
||||
\item επαλήθευση του κωδικού κατά τη σύνδεση μέσω σύγκρισης hash και όχι μέσω SQL.
|
||||
\item χρήση συναρτήσεων \textbf{hashing} με ενσωματωμένο salt κατά την εγγραφή,
|
||||
\item \textbf{επαλήθευση} του κωδικού κατά τη σύνδεση μέσω σύγκρισης hash και \textbf{όχι μέσω SQL}.
|
||||
\end{itemize}
|
||||
|
||||
Στην παρούσα εργασία χρησιμοποιούνται οι συναρτήσεις \texttt{password\_hash()} και \texttt{password\_verify()} της PHP, οι οποίες θεωρούνται βέλτιστη πρακτική για την αποθήκευση κωδικών.
|
||||
@ -713,14 +717,26 @@ $sql_query =
|
||||
|
||||
\subsection{Διορθωμένη υλοποίηση}
|
||||
|
||||
Στη διορθωμένη υλοποίηση οι κωδικοί πλέον δεν εισάγονται στη βάση αυτούσιοι, αλλά περνούν πρώτα από:
|
||||
Στη διορθωμένη υλοποίηση οι κωδικοί πλέον δεν εισάγονται στη βάση απευθείας από τη μεταβλητή του UI \textit{new\_password}, αλλά \textbf{περνά πρώτα από hashing}:
|
||||
\begin{minted}{php}
|
||||
$sql_query = "INSERT INTO login_users (username, password) VALUES (?, ?)";
|
||||
$stmt = $conn->prepare($sql_query);
|
||||
// ...
|
||||
$password_hash = password_hash($new_password, PASSWORD_DEFAULT);
|
||||
$stmt->bind_param("ss", $new_username, $password_hash);
|
||||
$result = $stmt->execute();
|
||||
\end{minted}
|
||||
|
||||
Ομοίως κατά το login, η επαλήθευση γίνεται με την:
|
||||
Ομοίως κατά το login (\textit{login.php}), η επαλήθευση στον κωδικό γίνεται με την \textit{password\_verify}.
|
||||
\begin{minted}{php}
|
||||
password_verify($password, $stored_hash);
|
||||
$stmt = $conn->prepare("SELECT id, password FROM login_users WHERE username = ?");
|
||||
$stmt->bind_param("s", $username)->execute();
|
||||
$result = $stmt->get_result();
|
||||
if ($result && $result->num_rows === 1) {
|
||||
if (password_verify($password, $result->fetch_assoc()["password"])) {
|
||||
// succesful password verification
|
||||
}
|
||||
}
|
||||
\end{minted}
|
||||
|
||||
\subsection{Απαιτούμενες αλλαγές περιβάλλοντος και βάσης δεδομένων}
|
||||
@ -728,17 +744,17 @@ $sql_query =
|
||||
Για τον προ-εγκατεστημένο χρήστη δοκιμών (\texttt{u1/p1}), η διαδικασία πραγματοποιήθηκε χειροκίνητα στο περιβάλλον Docker, ως εξής:
|
||||
|
||||
Αρχικά δημιουργήσαμε ένα hashed κωδικό για τον υπάρχον χρήστη:
|
||||
\begin{verbatim}
|
||||
\begin{minted}{bash}
|
||||
$ docker compose exec web bash
|
||||
root@ee33aeda3931:/var/www/html# php -r \
|
||||
'echo password_hash("p1", PASSWORD_DEFAULT), PHP_EOL;'
|
||||
$2y$10$L18u5/PyVkDgsce/DsUOQu0sKhTzh854Euhog3cVb1W4YAfgRzY8W
|
||||
root@ee33aeda3931:/var/www/html#
|
||||
exit
|
||||
\end{verbatim}
|
||||
\end{minted}
|
||||
|
||||
Έπειτα αλλάξαμε των κωδικό χειροκίνητα στη βάση:
|
||||
\begin{verbatim}
|
||||
\begin{minted}{sql}
|
||||
MariaDB [pwd_mgr]> SELECT * FROM login_users;
|
||||
+----+----------+----------+
|
||||
| id | username | password |
|
||||
@ -756,22 +772,25 @@ $sql_query =
|
||||
+----+----------+--------------------------------------------------------------+
|
||||
| 1 | u1 | $2y$10$L18u5/PyVkDgsce/DsUOQu0sKhTzh854Euhog3cVb1W4YAfgRzY8W |
|
||||
+----+----------+--------------------------------------------------------------+
|
||||
\end{verbatim}
|
||||
\end{minted}
|
||||
|
||||
Η παραπάνω διαδικασία επιβεβαιώνει την επιτυχή αντικατάσταση του plaintext κωδικού με ασφαλές hash, διασφαλίζοντας τη συμβατότητα της βάσης δεδομένων με τη νέα υλοποίηση αυθεντικοποίησης.
|
||||
|
||||
Τέλος ενημερώσαμε τον ίδιο κωδικό και στο αρχείο αρχικοποίησης της βάσης στον container.
|
||||
Τέλος ενημερώσαμε τον ίδιο κωδικό και στο αρχείο αρχικοποίησης (\textit{01-create-pwd\_mgr-db-withData.sql}) της βάσης στον container.
|
||||
\begin{minted}{sql}
|
||||
-- php -r 'echo password_hash("p1", PASSWORD_DEFAULT), PHP_EOL;'
|
||||
INSERT INTO `login_users` (`id`, `username`, `password`) VALUES
|
||||
(1, 'u1', '$2y$10$L18u5/PyVkDgsce/DsUOQu0sKhTzh854Euhog3cVb1W4YAfgRzY8W');
|
||||
\end{minted}
|
||||
|
||||
\begin{figure}[H]
|
||||
Στην Εικόνα~\ref{fig:pltxt_successful_login} φαίνεται η επιτυχής σύνδεση του υπάρχον χρήστη μετά την αλλαγή του κωδικού στην βάση.
|
||||
\begin{figure}[!ht]
|
||||
\centering
|
||||
\includegraphics[width=0.45\textwidth]{img/pltxt-SuccessfulLogin.png}
|
||||
\caption{Επιτυχής σύνδεση χρήστη μετά την αλλαγή κωδικού.}
|
||||
\label{fig:pltxt_successful_login}
|
||||
\end{figure}
|
||||
|
||||
Στην Εικόνα~\ref{fig:pltxt_successful_login} φαίνεται η επιτυχής σύνδεση του υπάρχον χρήστη μετά την αλλαγή του κωδικού στην βάση.
|
||||
|
||||
|
||||
|
||||
% =====================================================================
|
||||
% DB admin credentials (root) used by the web app + mitigation
|
||||
@ -780,11 +799,8 @@ $sql_query =
|
||||
\section{Χρήση διαπιστευτηρίων διαχειριστή (root)}
|
||||
\label{subsec:db-root-user}
|
||||
|
||||
Ένα επιπλέον σημαντικό πρόβλημα ασφάλειας της εφαρμογής είναι ότι η σύνδεση προς τη βάση δεδομένων
|
||||
πραγματοποιείται με διαπιστευτήρια διαχειριστή (\textit{administrator credentials}, χρήστης \texttt{root}).
|
||||
Η πρακτική αυτή παραβιάζει τη θεμελιώδη αρχή του \textit{least privilege} (ελάχιστα απαραίτητα προνόμια),
|
||||
διότι οποιοσδήποτε επιτιθέμενος αποκτήσει δυνατότητα εκτέλεσης SQL εντολών (π.χ. μέσω SQL injection ή
|
||||
μέσω πρόσβασης στη βάση) δεν περιορίζεται από δικαιώματα και μπορεί να πραγματοποιήσει καταστροφικές
|
||||
Ένα επιπλέον σημαντικό πρόβλημα ασφάλειας της εφαρμογής είναι ότι η \textbf{σύνδεση} προς τη βάση δεδομένων πραγματοποιείται με \textbf{διαπιστευτήρια διαχειριστή} (\textit{administrator credentials}, χρήστης \texttt{root}).
|
||||
Η πρακτική αυτή παραβιάζει τη θεμελιώδη αρχή του \textit{least privilege} (ελάχιστα απαραίτητα προνόμια), διότι οποιοσδήποτε επιτιθέμενος αποκτήσει δυνατότητα εκτέλεσης SQL εντολών (π.χ. μέσω SQL injection ή μέσω πρόσβασης στη βάση) δεν περιορίζεται από δικαιώματα και μπορεί να πραγματοποιήσει καταστροφικές
|
||||
ενέργειες.
|
||||
|
||||
Το πρόβλημα εντοπίζεται στα ακόλουθα σημεία.
|
||||
@ -792,26 +808,26 @@ $sql_query =
|
||||
\item config.php (defaults σε root).
|
||||
Στο αρχείο \textit{config.php}, οι παράμετροι της βάσης διαβάζονται από environment variables,
|
||||
αλλά αν δεν υπάρχουν τιμές, γίνεται fallback σε \texttt{root/rootpass}.
|
||||
\begin{minted}{php}
|
||||
$DB_HOST = getenv('DB_HOST') ?: 'db';
|
||||
$DB_USER = getenv('DB_USER') ?: 'root';
|
||||
$DB_PASS = getenv('DB_PASS') ?: 'rootpass';
|
||||
$DB_NAME = getenv('DB_NAME') ?: 'pwd_mgr';
|
||||
\end{minted}
|
||||
\begin{minted}{php}
|
||||
$DB_HOST = getenv('DB_HOST') ?: 'db';
|
||||
$DB_USER = getenv('DB_USER') ?: 'root';
|
||||
$DB_PASS = getenv('DB_PASS') ?: 'rootpass';
|
||||
$DB_NAME = getenv('DB_NAME') ?: 'pwd_mgr';
|
||||
\end{minted}
|
||||
\label{lst:db-root-config}
|
||||
|
||||
\item docker-compose.yml (η web υπηρεσία τρέχει ως root στη DB).
|
||||
Στο \textit{docker-compose.yml} τα environment variables της web υπηρεσίας ορίζονται ρητά ως \texttt{root/rootpass},
|
||||
οπότε ακόμη και αν αλλάζαμε μόνο το \textit{config.php}, η εφαρμογή θα συνέχιζε να συνδέεται ως root.
|
||||
\begin{minted}{yaml}
|
||||
services:
|
||||
web:
|
||||
environment:
|
||||
DB_HOST: db
|
||||
DB_USER: root
|
||||
DB_PASS: rootpass
|
||||
DB_NAME: pwd_mgr
|
||||
\end{minted}
|
||||
\begin{minted}{yaml}
|
||||
services:
|
||||
web:
|
||||
environment:
|
||||
DB_HOST: db
|
||||
DB_USER: root
|
||||
DB_PASS: rootpass
|
||||
DB_NAME: pwd_mgr
|
||||
\end{minted}
|
||||
\label{lst:compose-web-root}
|
||||
|
||||
\item SQL init (δεν υπάρχει dedicated χρήστης εφαρμογής).
|
||||
@ -827,8 +843,7 @@ $sql_query =
|
||||
\item είναι πιθανή \textbf{μόνιμη παραβίαση} (π.χ. αλλαγές σε grants/χρήστες), ανάλογα με τη ρύθμιση του DB server.
|
||||
\end{itemize}
|
||||
|
||||
Με άλλα λόγια, ακόμη και αν διορθωθούν SQLi σε επίπεδο κώδικα, η χρήση root αυξάνει σημαντικά το \textit{impact}
|
||||
(impact amplification) οποιασδήποτε επιτυχούς επίθεσης.
|
||||
Με άλλα λόγια, ακόμη και αν διορθωθούν SQLi σε επίπεδο κώδικα, η χρήση root αυξάνει σημαντικά τον αντίκτυπο (impact amplification) οποιασδήποτε επιτυχούς επίθεσης.
|
||||
|
||||
\subsection{Αντιμετώπιση: least privilege χρήστης βάσης δεδομένων}
|
||||
|
||||
@ -836,7 +851,7 @@ $sql_query =
|
||||
στον οποίο εκχωρούνται μόνο τα απολύτως απαραίτητα δικαιώματα στο schema \texttt{pwd\_mgr}.
|
||||
|
||||
\begin{itemize}
|
||||
\item Αλλαγή στο SQL init: δημιουργία χρήστη και GRANT.
|
||||
\item Αλλαγή στο \textbf{SQL init}: δημιουργία χρήστη και GRANT.
|
||||
Στο αρχείο \textit{01-create-pwd\_mgr-db-withData.sql} που φορτώνεται από \texttt{/docker-entrypoint-initdb.d}) προστέθηκαν οι παρακάτω εντολές:
|
||||
\begin{minted}{sql}
|
||||
-- Create a dedicated DB user for the web application (least privilege).
|
||||
@ -847,9 +862,9 @@ $sql_query =
|
||||
\end{minted}
|
||||
\label{lst:db-create-app-user}
|
||||
Με αυτόν τον τρόπο, η εφαρμογή μπορεί να λειτουργήσει κανονικά,
|
||||
ενώ παράλληλα δεν επιτρέπονται επικίνδυνα δικαιώματα όπως \texttt{DROP}, \texttt{ALTER}, \texttt{CREATE USER}, \texttt{GRANT OPTION}.
|
||||
ενώ παράλληλα \textbf{δεν επιτρέπονται επικίνδυνα δικαιώματα} όπως \texttt{DROP}, \texttt{ALTER}, \texttt{CREATE USER}, \texttt{GRANT OPTION}.
|
||||
|
||||
\item Αλλαγή στο docker-compose.yml: web συνδέεται ως passman\_app.
|
||||
\item Αλλαγή στο docker-compose.yml: web \textbf{συνδέεται ως passman\_app}.
|
||||
Στη συνέχεια, στο \textit{docker-compose.yml} αλλάχθηκαν τα credentials της web υπηρεσίας ώστε να χρησιμοποιείται ο νέος χρήστης:
|
||||
\begin{minted}{yaml}
|
||||
services:
|
||||
@ -882,9 +897,8 @@ $sql_query =
|
||||
\label{lst:db-config-fixed}
|
||||
\end{itemize}
|
||||
|
||||
Με τις παραπάνω αλλαγές, η εφαρμογή λειτουργεί με περιορισμένα δικαιώματα στη βάση δεδομένων,
|
||||
μειώνοντας σημαντικά τον αντίκτυπο (impact reduction) πιθανών επιθέσεων.
|
||||
Η πρακτική αυτή αποτελεί βασικό μέτρο \textit{defense-in-depth} και ευθυγραμμίζεται με θεμελιώδεις αρχές secure design.
|
||||
Με τις παραπάνω αλλαγές, \textbf{η εφαρμογή λειτουργεί με περιορισμένα δικαιώματα στη βάση δεδομένων}, μειώνοντας σημαντικά τον αντίκτυπο (impact reduction) πιθανών επιθέσεων.
|
||||
Η πρακτική αυτή αποτελεί βασικό μέτρο \textbf{defense-in-depth} και ευθυγραμμίζεται με θεμελιώδεις αρχές secure design.
|
||||
|
||||
Ένα παράδειγμα φαίνεται και στο στιγμιότυπο παρακάτω, όπου η βάση απορρίπτει τη διαγραφή πίνακα, στον χρήστη passman\_app:
|
||||
\begin{minted}{sql}
|
||||
@ -912,13 +926,13 @@ MariaDB [(none)]>
|
||||
\label{subsec:http-https}
|
||||
|
||||
Η εφαρμογή αρχικά λειτουργεί αποκλειστικά μέσω του μη ασφαλούς πρωτοκόλλου HTTP.
|
||||
Όπως αναφέρεται και στην εκφώνηση, η χρήση HTTP επιτρέπει σε έναν επιτιθέμενο που παρακολουθεί την κίνηση του δικτύου (\textit{network observer / man-in-the-middle}) να υποκλέψει τις πληροφορίες που εμφανίζονται στον χρήστη και τα δεδομένα που αυτός αποστέλλει (π.χ. username/password στα forms).
|
||||
Όπως αναφέρεται και στην εκφώνηση, η χρήση HTTP επιτρέπει σε έναν επιτιθέμενο που παρακολουθεί την κίνηση του δικτύου (\textit{network observer / man-in-the-middle}) \textbf{να υποκλέψει τις πληροφορίες} που εμφανίζονται στον χρήστη και τα δεδομένα που αυτός αποστέλλει (π.χ. username/password στα forms).
|
||||
|
||||
Σε πραγματικό περιβάλλον, αυτό σημαίνει ότι:
|
||||
\begin{itemize}
|
||||
\item τα credentials μπορούν να διαρρεύσουν σε plaintext,
|
||||
\item το session cookie μπορεί να υποκλαπεί,
|
||||
\item το περιεχόμενο σελίδων (π.χ. αποθηκευμένοι κωδικοί ιστοσελίδων) μπορεί να αναγνωστεί από τρίτους.
|
||||
\item τα \textbf{credentials} μπορούν να διαρρεύσουν σε \textbf{plaintext},
|
||||
\item το \textbf{session cookie} μπορεί να \textbf{υποκλαπεί},
|
||||
\item το περιεχόμενο σελίδων (π.χ. αποθηκευμένοι κωδικοί ιστοσελίδων) μπορεί να \textbf{αναγνωστεί από τρίτους}.
|
||||
\end{itemize}
|
||||
|
||||
\subsection{Πού εμφανίζεται στην υλοποίηση}
|
||||
@ -936,6 +950,7 @@ MariaDB [(none)]>
|
||||
Επιπλέον, στο \textit{index.html} υπάρχουν hard-coded σύνδεσμοι προς \texttt{http://localhost/...}, οι οποίοι επιβάλλουν τη χρήση HTTP από τον browser.
|
||||
\begin{minted}{html}
|
||||
<a href="http://localhost/passman/login.php">Login Page</a>
|
||||
// ...
|
||||
<a href="http://localhost/passman/dashboard.php">Dashboard</a>
|
||||
<a href="http://localhost/passman/notes.php">Notes</a>
|
||||
\end{minted}
|
||||
@ -946,13 +961,12 @@ MariaDB [(none)]>
|
||||
\label{subsec:https-fix}
|
||||
|
||||
Για να επιτευχθεί ασφαλής μεταφορά δεδομένων (\textit{transport security}), απαιτείται η χρήση HTTPS.
|
||||
Σε περιβάλλον Docker, ένας πρακτικός και καθαρός τρόπος είναι να προστεθεί ένα reverse proxy
|
||||
(π.χ. Caddy / NGINX) μπροστά από τον Apache web server.
|
||||
Σε περιβάλλον Docker, ένας πρακτικός και καθαρός τρόπος είναι να προστεθεί ένα reverse proxy (π.χ. Caddy / NGINX) μπροστά από τον Apache web server.
|
||||
Το reverse proxy αναλαμβάνει:
|
||||
\begin{itemize}
|
||||
\item TLS termination (διαπραγμάτευση κρυπτογράφησης),
|
||||
\item αυτόματη ανακατεύθυνση από HTTP σε HTTPS,
|
||||
\item προώθηση (proxying) της κίνησης προς το εσωτερικό container της εφαρμογής.
|
||||
\item \textbf{TLS termination} (διαπραγμάτευση κρυπτογράφησης),
|
||||
\item αυτόματη \textbf{ανακατεύθυνση} από HTTP σε HTTPS,
|
||||
\item \textbf{προώθηση} (proxying) \textbf{της κίνησης} προς το εσωτερικό container της εφαρμογής.
|
||||
\end{itemize}
|
||||
|
||||
Με αυτή την αρχιτεκτονική, το container \texttt{web} \textbf{δεν εκτίθεται πλέον} απευθείας προς τα έξω.
|
||||
@ -982,11 +996,11 @@ MariaDB [(none)]>
|
||||
\label{lst:https-compose-after}
|
||||
|
||||
\subsection{Ρύθμιση reverse proxy -- Caddyfile}
|
||||
Για χρήση σε τοπικό περιβάλλον (\texttt{localhost}), επιλέχθηκε self-signed / internal TLS (\texttt{tls internal}).
|
||||
Για χρήση σε τοπικό περιβάλλον (\texttt{localhost}), \textbf{επιλέχθηκε self-signed} (\texttt{tls internal}).
|
||||
Αυτό είναι κατάλληλο για demo/testing, καθώς δεν απαιτεί domain name ή δημόσιο πιστοποιητικό.
|
||||
Το \textit{Caddyfile} είναι:
|
||||
|
||||
\begin{minted}{text}
|
||||
\begin{minted}{nginx}
|
||||
# HTTP site: redirect everything to HTTPS
|
||||
http://localhost {
|
||||
redir https://{host}{uri} permanent
|
||||
@ -1008,7 +1022,7 @@ https://localhost {
|
||||
\label{lst:caddyfile}
|
||||
|
||||
\paragraph{Σημείωση για τοπικά πιστοποιητικά (browser warning).}
|
||||
Η χρήση \texttt{tls internal} δημιουργεί self-signed πιστοποιητικό, το οποίο ενδέχεται να προκαλέσει warning στον browser.
|
||||
Η χρήση \texttt{tls internal} δημιουργεί self-signed πιστοποιητικό, το οποίο ενδέχεται να προκαλέσει \textbf{warning στον browser}.
|
||||
Το γεγονός αυτό είναι αναμενόμενο σε τοπικό demo περιβάλλον και δεν αναιρεί τη λειτουργία κρυπτογράφησης.
|
||||
Σε παραγωγικό περιβάλλον θα χρησιμοποιούνταν έγκυρο πιστοποιητικό (π.χ. μέσω Let's Encrypt) και domain name.
|
||||
|
||||
@ -1023,7 +1037,8 @@ https://localhost {
|
||||
\label{lst:https-index-after}
|
||||
|
||||
\subsection{Επαλήθευση}
|
||||
Όπως φαίνεται και στην Εικόνα~\ref{fig:https-sample}, μετά τις αλλαγές, η εφαρμογή είναι διαθέσιμη μέσω: \texttt{https://localhost/passman},.
|
||||
Όπως φαίνεται και στην Εικόνα~\ref{fig:https-sample}, μετά τις αλλαγές, η εφαρμογή είναι διαθέσιμη μέσω:\\
|
||||
\texttt{https://localhost/passman}.
|
||||
\begin{figure}[!ht]
|
||||
\centering
|
||||
\includegraphics[width=0.55\textwidth]{img/https-Sample.png}
|
||||
@ -1033,13 +1048,13 @@ https://localhost {
|
||||
|
||||
Επιπλέον, η πρόσβαση σε \texttt{http://localhost/passman} οδηγεί σε αυτόματη ανακατεύθυνση προς HTTPS,
|
||||
επιβεβαιώνοντας ότι η μεταφορά δεδομένων προστατεύεται σε επίπεδο δικτύου.
|
||||
\begin{verbatim}
|
||||
$ curl -I http://localhost/passman
|
||||
HTTP/1.1 301 Moved Permanently
|
||||
Location: https://localhost/passman
|
||||
Server: Caddy
|
||||
Date: Sun, 11 Jan 2026 20:01:49 GMT
|
||||
\end{verbatim}
|
||||
\begin{minted}{bash}
|
||||
$ curl -I http://localhost/passman
|
||||
HTTP/1.1 301 Moved Permanently
|
||||
Location: https://localhost/passman
|
||||
Server: Caddy
|
||||
Date: Sun, 11 Jan 2026 20:01:49 GMT
|
||||
\end{minted}
|
||||
|
||||
Με την εισαγωγή reverse proxy και την ενεργοποίηση HTTPS, μειώνεται σημαντικά ο κίνδυνος υποκλοπής δεδομένων (\textit{eavesdropping}) και επιτυγχάνεται προστασία στο επίπεδο μεταφοράς (transport layer), ενισχύοντας τη συνολική ασφάλεια της εφαρμογής.
|
||||
|
||||
@ -1047,18 +1062,18 @@ https://localhost {
|
||||
|
||||
\section{Συμπεράσματα}
|
||||
|
||||
Στην παρούσα εργασία μελετήθηκε μια απλή διαδικτυακή εφαρμογή διαχείρισης κωδικών πρόσβασης, με στόχο την ανάδειξη και αντιμετώπιση κρίσιμων κενών ασφάλειας που προκύπτουν από μη ασφαλείς προγραμματιστικές πρακτικές.
|
||||
Στην παρούσα εργασία μελετήθηκε μια απλή διαδικτυακή εφαρμογή διαχείρισης κωδικών πρόσβασης, με στόχο την \textbf{ανάδειξη και αντιμετώπιση κρίσιμων κενών ασφάλειας} που προκύπτουν από μη ασφαλείς προγραμματιστικές πρακτικές.
|
||||
Η προσέγγιση που ακολουθήθηκε ήταν πειραματική και βασισμένη σε πραγματικά σενάρια επίθεσης.
|
||||
Αρχικά εντοπίστηκαν ευπάθειες όπως SQL Injection, Stored Cross-Site Scripting, αποθήκευση κωδικών σε απλό κείμενο, χρήση διαπιστευτηρίων διαχειριστή για τη βάση δεδομένων και απουσία κρυπτογράφησης στο επίπεδο μεταφοράς (HTTP).
|
||||
Για κάθε περίπτωση παρουσιάστηκε ο τρόπος εκμετάλλευσης, τεκμηριώθηκαν οι επιπτώσεις και στη συνέχεια εφαρμόστηκαν στοχευμένα διορθωτικά μέτρα, με ελάχιστη αλλά ουσιαστική παρέμβαση στον κώδικα και στη συνολική αρχιτεκτονική.
|
||||
Για κάθε περίπτωση \textit{παρουσιάστηκε ο τρόπος εκμετάλλευσης, τεκμηριώθηκαν οι επιπτώσεις και στη συνέχεια εφαρμόστηκαν στοχευμένα διορθωτικά μέτρα}, με \textbf{ελάχιστη αλλά ουσιαστική παρέμβαση στον κώδικα} και στη συνολική αρχιτεκτονική.
|
||||
|
||||
Οι διορθώσεις υλοποιήθηκαν με βάση θεμελιώδεις αρχές ασφαλούς σχεδίασης, όπως ο διαχωρισμός δεδομένων και εντολών (prepared statements), η αντιμετώπιση κάθε εισόδου ως μη έμπιστης (output encoding), η ασφαλής αποθήκευση κωδικών μέσω hashing, η αρχή του least privilege στη διαχείριση της βάσης δεδομένων και η προστασία της επικοινωνίας μέσω HTTPS.
|
||||
Ιδιαίτερη έμφαση δόθηκε όχι μόνο στη «διόρθωση» μεμονωμένων σφαλμάτων, αλλά και στη μείωση του συνολικού αντίκτυπου πιθανών επιθέσεων (impact reduction), υιοθετώντας μια προσέγγιση defense-in-depth.
|
||||
Μέσα από τη διαδικασία αυτή κατέστη σαφές ότι πολλές σοβαρές ευπάθειες δεν οφείλονται σε πολύπλοκα σφάλματα, αλλά σε απλές παραλείψεις και λανθασμένες παραδοχές κατά την ανάπτυξη της εφαρμογής.
|
||||
Οι διορθώσεις υλοποιήθηκαν με βάση θεμελιώδεις αρχές ασφαλούς σχεδίασης, όπως ο \textbf{διαχωρισμός δεδομένων και εντολών} (prepared statements), η \textbf{αντιμετώπιση κάθε εισόδου ως μη έμπιστης} (output encoding), η \textbf{ασφαλής αποθήκευση κωδικών μέσω hashing}, η αρχή του \textbf{least privilege} στη διαχείριση της βάσης δεδομένων και η προστασία της επικοινωνίας μέσω \textbf{HTTPS}.
|
||||
Ιδιαίτερη έμφαση δόθηκε όχι μόνο στη «διόρθωση» μεμονωμένων σφαλμάτων, αλλά και στη μείωση του συνολικού αντίκτυπου πιθανών επιθέσεων (impact reduction), υιοθετώντας μια προσέγγιση \textbf{defense-in-depth}.
|
||||
Μέσα από τη διαδικασία αυτή κατέστη σαφές ότι \textit{πολλές σοβαρές ευπάθειες δεν οφείλονται σε πολύπλοκα σφάλματα, αλλά σε απλές παραλείψεις και λανθασμένες παραδοχές} κατά την ανάπτυξη της εφαρμογής.
|
||||
|
||||
Συνολικά, η εργασία ανέδειξε τη σημασία της ασφάλειας ως αναπόσπαστο μέρος του σχεδιασμού λογισμικού και όχι ως μεταγενέστερη προσθήκη.
|
||||
Η κατανόηση των μηχανισμών επίθεσης και η εφαρμογή βασικών πρακτικών secure coding επιτρέπουν τη δραστική βελτίωση της ασφάλειας ακόμη και σε απλές εφαρμογές.
|
||||
Η εμπειρία που αποκομίστηκε υπογραμμίζει ότι η συστηματική σκέψη, η σωστή αρχιτεκτονική και η επίγνωση των κινδύνων είναι καθοριστικοί παράγοντες για την ανάπτυξη αξιόπιστων και ασφαλών πληροφοριακών συστημάτων.
|
||||
Η εμπειρία που αποκομίστηκε υπογραμμίζει ότι η \textbf{συστηματική σκέψη}, η \textbf{σωστή αρχιτεκτονική} και η \textbf{επίγνωση των κινδύνων} είναι καθοριστικοί παράγοντες για την ανάπτυξη αξιόπιστων και ασφαλών πληροφοριακών συστημάτων.
|
||||
|
||||
|
||||
\end{document}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user