% % !TEX TS-program = xelatex % !TEX encoding = UTF-8 Unicode % !TEX spellcheck = el-GR % % Information Systems Security assignment report % % Requires compilation with XeLaTeX % % authors: % Χρήστος Χουτουρίδης ΑΕΜ 8997 % cchoutou@ece.auth.gr % Options: % % 1) mainlang= % Default: english % Set the default language of the document which affects hyphenations, % localization (section, dates, etc...) % % example: \documentclass[mainlang=greek]{AUThReport} % % 2) % Add hyphenation and typesetting support for other languages % Currently supports: english, greek, german, frenc % % example: \documentclass[english, greek]{AUThReport} % % 3) short: Requests a shorter title for the document % Default: no short % % example: \documentclass[short]{AUThReport} % \documentclass[a4paper, 11pt, mainlang=greek, english]{AUThReport/AUThReport} \CurrentDate{\today} % Greek report document setup suggestions %--------------------------------- % Document configuration \AuthorName{Χρήστος Χουτουρίδης} \AuthorAEM{8997} \AuthorMail{cchoutou@ece.auth.gr} %\CoAuthorName{CoAuthor Name} %\CoAuthorAEM{AEM} %\CoAuthorMail{CoAuthor Mail} % \WorkGroup{Ομάδα Χ} \DocTitle{Εργασία Εξαμήνου} \DocSubTitle{Ανάδειξη προβλημάτων ασφάλειας σε δικτυακή εφαρμογή αποθήκευσης κωδικών πρόσβασης (password manager) και αντιμετώπισή τους} \Department{Τμήμα ΗΜΜΥ. Τομέας Ηλεκτρονικής} \ClassName{Ασφάλεια Υπολογιστικών Συστημάτων} \InstructorName{Γεώργιος Πάγκαλος} \InstructorMail{pangalos@ece.auth.gr} \CoInstructorName{Αθανάσιος Σιαχούδης} \CoInstructorMail{asiach@ece.auth.gr} % Local package requirements %--------------------------------- %\usepackage{tabularx} %\usepackage{array} %\usepackage{commath} \usepackage{amsmath, amssymb, amsfonts} \usepackage{graphicx} \usepackage{caption} \usepackage{subcaption} \usepackage{float} \captionsetup[figure]{name=Εικόνα} % Requires: -shell-escape compile argument \usepackage{minted} \usepackage{xcolor} % \setminted[php]{ fontsize=\small, breaklines, autogobble, baselinestretch=1.1, tabsize=2, numbersep=8pt, startinline, gobble=0 } \setminted[sql]{ fontsize=\small, breaklines, autogobble, baselinestretch=1.1, tabsize=2, numbersep=8pt, gobble=0 } \newcommand{\repo}{https://git.hoo2.net/hoo2/InformationSystemsSecurity} \newcommand{\fixsqlitag}{https://git.hoo2.net/hoo2/InformationSystemsSecurity/releases/tag/fix-sqli} \newcommand{\fixxsstag}{https://git.hoo2.net/hoo2/InformationSystemsSecurity/releases/tag/fix-xss} \begin{document} % Request a title page or header \InsertTitle \section{Εισαγωγή} Η ασφάλεια των πληροφοριακών συστημάτων αποτελεί κρίσιμο παράγοντα στον σχεδιασμό και τη λειτουργία σύγχρονων δικτυακών εφαρμογών. Ιδιαίτερα εφαρμογές που διαχειρίζονται ευαίσθητα δεδομένα, όπως κωδικούς πρόσβασης, προσωπικές πληροφορίες και διαπιστευτήρια πρόσβασης, αποτελούν συχνό στόχο επιθέσεων. Η εκμετάλλευση ευπαθειών σε τέτοιες εφαρμογές μπορεί να οδηγήσει σε σοβαρές παραβιάσεις εμπιστευτικότητας, ακεραιότητας και διαθεσιμότητας των δεδομένων. Σκοπός της παρούσας εργασίας είναι η μελέτη, ανάδειξη και αντιμετώπιση κενών ασφάλειας σε μία απλή διαδικτυακή εφαρμογή διαχείρισης κωδικών πρόσβασης (password manager). Η εφαρμογή αυτή, αν και λειτουργικά επαρκής, υλοποιεί βασικές λειτουργίες χωρίς να λαμβάνει υπόψη θεμελιώδεις αρχές ασφάλειας λογισμικού. Μέσα από την ανάλυση πραγματικών σεναρίων επίθεσης, η εργασία στοχεύει να καταδείξει πώς φαινομενικά μικρές παραλείψεις στον κώδικα μπορούν να οδηγήσουν σε σοβαρές ευπάθειες. Η προσέγγιση που ακολουθείται είναι πρακτική και πειραματική. Αρχικά δημιουργείται ένα ελεγχόμενο περιβάλλον εκτέλεσης της εφαρμογής. Στη συνέχεια εντοπίζονται και εκμεταλλεύονται συγκεκριμένα κενά ασφάλειας, τεκμηριώνονται οι επιπτώσεις τους και τέλος προτείνονται και υλοποιούνται διορθωτικά μέτρα. Με τον τρόπο αυτό επιτυγχάνεται τόσο η κατανόηση των μηχανισμών επίθεσης όσο και η εμπέδωση καλών πρακτικών ασφαλούς ανάπτυξης λογισμικού. \subsection{Παραδοτέα} Τα παραδοτέα της εργασίας αποτελούνται από: \begin{itemize} \item Την παρούσα αναφορά. \item Τον κατάλογο \textit{passman-dev/} με τον κώδικα της εφαρμογής μετά τις αλλαγές. \item Το \href{\repo}{σύνδεσμο} με το αποθετήριο που περιέχει όλο το project με τον κώδικα της εφαρμογής, τον κώδικα της αναφοράς, τα branch των διορθώσεων και τα παραδοτέα. \end{itemize} \section{Περιγραφή της εφαρμογής} Η εφαρμογή που μελετάται αποτελεί ένα απλό web-based password manager, υλοποιημένο σε PHP και με χρήση σχεσιακής βάσης δεδομένων MySQL. Η βασική της λειτουργία περιλαμβάνει τη δημιουργία λογαριασμών χρηστών, την αυθεντικοποίησή τους και την αποθήκευση διαπιστευτηρίων πρόσβασης σε τρίτους διαδικτυακούς τόπους. Η εφαρμογή βασίζεται σε session management για τη διαχείριση της κατάστασης σύνδεσης και σε απλές SQL εντολές για την αλληλεπίδραση με τη βάση δεδομένων. Η δομή της εφαρμογής είναι απλή και αποτελείται από διακριτά αρχεία PHP, καθένα εκ των οποίων είναι υπεύθυνο για συγκεκριμένη λειτουργικότητα: \begin{itemize} \item \textit{index.html}: Αρχική σελίδα της εφαρμογής, η οποία παρέχει συνδέσμους προς τις επιμέρους λειτουργίες (εγγραφή, σύνδεση, πίνακας ελέγχου, σημειώσεις). \item \textit{register.php}: Υλοποιεί τη διαδικασία εγγραφής νέων χρηστών στη βάση δεδομένων. Δέχεται στοιχεία από φόρμα και τα αποθηκεύει στον πίνακα χρηστών. \item \textit{login.php}: Υλοποιεί τη διαδικασία αυθεντικοποίησης χρηστών. Ελέγχει τα παρεχόμενα διαπιστευτήρια και δημιουργεί session σε περίπτωση επιτυχούς σύνδεσης. \item \textit{dashboard.php}: Αποτελεί την κεντρική σελίδα μετά τη σύνδεση. Παρέχει τη δυνατότητα προβολής, εισαγωγής και διαγραφής αποθηκευμένων διαπιστευτηρίων για ιστοσελίδες. \item \textit{notes.php}: Επιτρέπει την αποθήκευση και προβολή σημειώσεων ή ανακοινώσεων που σχετίζονται με τον χρήστη. \item \textit{logout.php}: Τερματίζει τη συνεδρία χρήστη και καταστρέφει τα δεδομένα session. \item \textit{config.php}: Περιέχει τις ρυθμίσεις σύνδεσης με τη βάση δεδομένων και χρησιμοποιείται από τα υπόλοιπα αρχεία μέσω include. \end{itemize} \section{Περιβάλλον δοκιμών και μεταφορά σε Docker} Πριν την ανάλυση των κενών ασφάλειας κρίθηκε απαραίτητο να δημιουργηθεί ένα \textbf{ελεγχόμενο και αναπαραγώγιμο} περιβάλλον δοκιμών. Αντί της χρήσης του προτεινόμενου περιβάλλοντος XAMPP, επιλέχθηκε η μεταφορά (porting) της εφαρμογής σε περιβάλλον \textbf{Docker}, το οποίο θεωρείται πιο σύγχρονη και ευέλικτη λύση. Η επιλογή του Docker επιτρέπει την ακριβή καθοδήγηση των εκδόσεων λογισμικού που χρησιμοποιούνται, την εύκολη αναπαραγωγή του περιβάλλοντος σε διαφορετικά συστήματα και τον σαφή διαχωρισμό των επιμέρους υπηρεσιών. Συγκεκριμένα, η εφαρμογή διαχωρίστηκε αρχικά σε δύο containers: έναν web server (Apache με PHP) και έναν database server (MariaDB). Τον MariaDB τον χρησιμοποιούμε από το επίσημο αποθετήριο, αλλά τον web, τον χτίζουμε με δικό μας Dockerfile. Στο τέλος της διαδικασίας προστέθηκε ένας ακόμα (caddy) που αναλαμβάνει το reverse proxy για το https. Κατά τη διαδικασία μεταφοράς απαιτήθηκαν ορισμένες στοχευμένες αλλαγές: \begin{itemize} \item \textbf{Διαχείριση HTTP headers και sessions}: Σε αντίθεση με το XAMPP, το περιβάλλον του Apache-php στο Docker χρησιμοποιεί αυστηρότερες ρυθμίσεις όσον αφορά την αποστολή HTTP headers. Αυτό αποκάλυψε προβλήματα στη ροή του κώδικα, όπου HTML output προηγούνταν κλήσεων όπως \textit{session\_start()} και \textit{header()}. Η λύση ήταν η αναδιάταξη του κώδικα, ώστε όλη η λογική ελέγχου και ανακατεύθυνσης να εκτελείται πριν από οποιοδήποτε HTML output. \item \textbf{Συμβατότητα χαρακτήρων και collation βάσης δεδομένων}: Η αρχική βάση δεδομένων χρησιμοποιούσε \textit{latin1} encoding, το οποίο προκάλεσε σφάλματα σε σύγχρονες εκδόσεις MariaDB. Η λύση που επιλέχθηκε ήταν η ελάχιστη τροποποίηση του αρχείου αρχικοποίησης της βάσης ώστε να χρησιμοποιείται \textit{utf8mb4}, διατηρώντας κατά τα λοιπά το αρχικό schema. \item \textbf{Αποθήκευση δεδομένων βάσης}: Για την αποφυγή δημιουργίας πλήθους αρχείων στον κατάλογο του project, χρησιμοποιήθηκε Docker named volume για το data directory της MariaDB. Με τον τρόπο αυτό επιτυγχάνεται καθαρή δομή έργου και μόνιμη αποθήκευση δεδομένων. \end{itemize} Οι παραπάνω αλλαγές ήταν απολύτως αναγκαίες για τη σωστή λειτουργία της εφαρμογής σε σύγχρονο περιβάλλον, χωρίς να αλλοιώνουν τη λογική ή τη δομή της αρχικής υλοποίησης. \subsection{Δημιουργία και εκτέλεση της εφαρμογής} Για την εκτέλεση και δοκιμή της εφαρμογής στο περιβάλλον Docker απαιτείται η ύπαρξη εγκατεστημένου Docker και Docker Compose στο σύστημα. Αφού ληφθεί ο πηγαίος κώδικας της εφαρμογής από το αποθετήριο, η διαδικασία εκκίνησης είναι πλήρως αυτοματοποιημένη. Η δημιουργία των απαραίτητων images και η εκκίνηση των containers πραγματοποιείται με την εντολή: \begin{minted}[fontsize=\small]{bash} docker compose up -d --build \end{minted} Με την παραπάνω εντολή δημιουργείται το image του web server (Apache με PHP), εκκινείται η υπηρεσία της βάσης δεδομένων MariaDB και εκτελείται αυτόματα το αρχείο αρχικοποίησης της βάσης δεδομένων. Τα δεδομένα της βάσης αποθηκεύονται σε Docker named volume, εξασφαλίζοντας τη διατήρησή τους μεταξύ επανεκκινήσεων των containers. Μετά την επιτυχή εκκίνηση, η εφαρμογή είναι προσβάσιμη μέσω web browser στη διεύθυνση:\\ \texttt{http://localhost/passman}.\\ Για περισσότερες πληροφορίες μπορείτε να επισκεφτείτε το \href{\repo}{αποθετήριο} της εφαρμογής. Για λόγους δοκιμών και ανάλυσης, είναι επίσης δυνατή η απευθείας πρόσβαση στη βάση δεδομένων μέσω τερματικού, χρησιμοποιώντας την εντολή: \begin{minted}[fontsize=\small]{bash} docker compose exec db mariadb -uroot -prootpass \end{minted} Μέσω της παραπάνω πρόσβασης είναι δυνατή η εκτέλεση SQL εντολών για την επιβεβαίωση της κατάστασης της βάσης δεδομένων, καθώς και η τεκμηρίωση των επιπτώσεων επιθέσεων και διορθωτικών παρεμβάσεων που παρουσιάζονται στα επόμενα τμήματα της εργασίας. \section{SQL Injection} \label{sec:sqli} Το πρώτο κενό ασφάλειας που εξετάζεται αφορά την ευπάθεια σε επιθέσεις SQL Injection. Η ευπάθεια αυτή προκύπτει όταν δεδομένα που παρέχονται από τον χρήστη \textbf{ενσωματώνονται απευθείας} σε SQL εντολές, χωρίς κατάλληλη επικύρωση ή χρήση μηχανισμών παραμετροποίησης (prepared statements). Αποτέλεσμα είναι ο επιτιθέμενος να μπορεί να αλλοιώσει τη λογική του SQL query, οδηγώντας σε μη εξουσιοδοτημένη πρόσβαση ή/και διαρροή δεδομένων. Στην εφαρμογή, η ευπάθεια εντοπίζεται σε πολλά σημεία και παρότι θα τα λύσουμε όλα, εδώ παραθέτουμε ως παράδειγμα την ευπάθεια στο \textit{login.php}, καθώς αυτό είναι ίσως οτ πιο σοβαρό. Η είσοδος του χρήστη (username/password), όπως φαίνεται στο απόσπασμα παρακάτω, εισάγεται απευθείας στο query αυθεντικοποίησης μέσω string concatenation. \begin{minted}{php} $sql_query = "SELECT * FROM login_users WHERE username='{$username}' AND password='{$password}';"; \end{minted} \label{lst:sqli-login-query} Στο συγκεκριμένο σημείο, το περιεχόμενο των μεταβλητών \textit{\$username} και \textit{\$password} \textbf{δεν υφίσταται κανέναν έλεγχο ή escaping}. Επομένως, ένας επιτιθέμενος μπορεί να εισάγει ειδικούς χαρακτήρες (π.χ. \texttt{'}) και SQL τελεστές (π.χ. \texttt{OR}) ώστε να παρακάμψει τη συνθήκη αυθεντικοποίησης. \begin{figure}[!ht] \centering \begin{subfigure}{.48\textwidth} \centering \includegraphics[width=\linewidth]{img/sqli-LoginScreen.png} \caption{Φόρμα σύνδεσης με εισαγωγή κακόβουλου payload στο πεδίο username.} \label{fig:sqli_login_screen} \end{subfigure} \hfill \begin{subfigure}{.48\textwidth} \centering \includegraphics[width=\linewidth]{img/sqli-LoginProof.png} \caption{Επιτυχής πρόσβαση στον πίνακα ελέγχου μετά από SQL injection.} \label{fig:sqli_login_proof} \end{subfigure} \caption{SQL Injection.} \end{figure} Στην Εικόνα~\ref{fig:sqli_login_screen} παρουσιάζεται η φόρμα σύνδεσης, στην οποία εισάγεται payload της μορφής \texttt{u1' OR '1'='1} στο πεδίο username, με αυθαίρετη τιμή στο password. Με τον τρόπο αυτό, η συνθήκη του \texttt{WHERE} αλλοιώνεται ώστε να επιστρέφει εγγραφές ανεξάρτητα από το πραγματικό password. Στη συνέχεια, ο χρήστης αποκτά πρόσβαση στον πίνακα ελέγχου (dashboard), όπως φαίνεται στην Εικόνα~\ref{fig:sqli_login_proof}. Η επιτυχής εκμετάλλευση της ευπάθειας μπορεί να τεκμηριωθεί και σε επίπεδο βάσης δεδομένων, μέσω του \textit{general\_log} της MariaDB, όπου καταγράφεται το τελικό query που εκτελέστηκε από την εφαρμογή. \begin{figure}[!ht] \centering \includegraphics[width=1.0\textwidth]{img/sqli-DBquery.png} \caption{Καταγραφή του τελικού SQL query στη MariaDB (\textit{general\_log}), όπου φαίνεται η αλλοίωση της συνθήκης αυθεντικοποίησης από το injected input.} \label{fig:sqli_db_query} \end{figure} Η Εικόνα~\ref{fig:sqli_db_query} δείχνει ότι το query αυθεντικοποίησης πλέον περιέχει την πρόσθετη συνθήκη \texttt{OR '1'='1'}, με αποτέλεσμα η αυθεντικοποίηση να μπορεί να παρακαμφθεί. Τέλος, στην Εικόνα~\ref{fig:sqli_db_users} παρουσιάζονται τα περιεχόμενα του πίνακα \texttt{login\_users}, επιβεβαιώνοντας ότι η πρόσβαση αποκτήθηκε χωρίς να τροποποιηθούν τα δεδομένα της βάσης (δηλαδή πρόκειται για bypass του μηχανισμού αυθεντικοποίησης και όχι για αλλαγή/εισαγωγή δεδομένων). \begin{figure}[!ht] \centering \includegraphics[width=0.70\textwidth]{img/sqli-DBusers.png} \caption{Περιεχόμενα του πίνακα \texttt{login\_users} στη βάση δεδομένων (επιβεβαίωση ότι τα credentials παραμένουν αμετάβλητα).} \label{fig:sqli_db_users} \end{figure} Η συγκεκριμένη ευπάθεια θεωρείται \textbf{ιδιαίτερα σοβαρή}, καθώς υπονομεύει πλήρως τον μηχανισμό αυθεντικοποίησης και \textbf{επιτρέπει μη εξουσιοδοτημένη πρόσβαση σε ευαίσθητα δεδομένα}. \subsection{Αντιμετώπιση SQL Injection} \label{sec:sqli_fix} Η αντιμετώπιση του SQL Injection βασίζεται στη θεμελιώδη αρχή του διαχωρισμού \textit{δεδομένων} από \textit{εντολές}. Το πρόβλημα στην αρχική υλοποίηση προέκυπτε επειδή το SQL query κατασκευαζόταν μέσω \textbf{απλής συνένωσης (string concatenation)} με δεδομένα που παρείχε ο χρήστης. Έτσι, ειδικοί χαρακτήρες και τελεστές SQL μπορούσαν να εισαχθούν ως μέρος της συμβολοσειράς, με αποτέλεσμα να αλλοιώνεται η σύνταξη και η λογική της 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{Υλοποίηση της διόρθωσης} Στο αρχείο \textit{login.php} αντικαταστάθηκε η δυναμική δημιουργία του SQL query με prepared statement και bind παραμέτρων. Στο Απόσπασμα~\ref{lst:sqli-fix-login} παρουσιάζεται ο διορθωμένος κώδικας. Η SQL εντολή ορίζεται με placeholders (\texttt{?}) και οι τιμές \textit{\$username} και \textit{\$password} περνούν μέσω \textit{bind\_param()}, ώστε να μην μπορούν να επηρεάσουν τη δομή της εντολής. \begin{minted}{php} // SQL injection mitigation: use a prepared statement with bound parameters. // User input is treated strictly as data, not as part of the SQL syntax. $stmt = $conn->prepare("SELECT id FROM login_users WHERE username = ? AND password = ?"); if ($stmt === false) { die("Prepare failed."); } $stmt->bind_param("ss", $username, $password); $stmt->execute(); $stmt->store_result(); // Authentication succeeds only if exactly one row matches. if ($stmt->num_rows >= 1) { $_SESSION['loggedin'] = true; $_SESSION['username'] = $username; $stmt->close(); $conn->close(); // ... } \end{minted} \label{lst:sqli-fix-login} \subsection{Επαλήθευση της διόρθωσης} Η αποτελεσματικότητα της διόρθωσης επιβεβαιώθηκε πειραματικά με το ίδιο payload που χρησιμοποιήθηκε στην επίδειξη της ευπάθειας. Στην Εικόνα~\ref{fig:sqli_fix_login_screen} φαίνεται η προσπάθεια σύνδεσης με το injection string στο πεδίο username. Η εφαρμογή πλέον απορρίπτει την προσπάθεια σύνδεσης και εμφανίζει μήνυμα αποτυχίας, όπως φαίνεται στην Εικόνα~\ref{fig:sqli_fix_login_proof}. \begin{figure}[!ht] \centering \begin{subfigure}{.48\textwidth} \centering \includegraphics[width=\linewidth]{img/sqli-LoginScreenFix.png} \caption{Δοκιμή του ίδιου SQLi payload μετά τη διόρθωση.} \label{fig:sqli_fix_login_screen} \end{subfigure}% \hfill \begin{subfigure}{.48\textwidth} \centering \includegraphics[width=\linewidth]{img/sqli-LoginProofFix.png} \caption{Αποτυχία σύνδεσης μετά τη διόρθωση.} \label{fig:sqli_fix_login_proof} \end{subfigure}% \caption{SQL Injection: Fixed.} \end{figure} Περαιτέρω επιβεβαίωση παρέχεται από τα logs της βάσης (general log), όπου φαίνεται ότι η εντολή εκτελείται ως παραμετροποιημένο statement με placeholders και όχι ως query που περιέχει ενσωματωμένο το injected payload. Στην Εικόνα~\ref{fig:sqli_fix_db_query} παρατηρείται η παρουσία της μορφής \texttt{username = ? AND password = ?}, γεγονός που υποδηλώνει ότι ο server λαμβάνει το statement ανεξάρτητα από τα δεδομένα εισόδου. \begin{figure}[!ht] \centering \includegraphics[width=1.0\textwidth]{img/sqli-DBqueryFix.png} \caption{Καταγραφή στη MariaDB μετά τη διόρθωση: εκτέλεση παραμετροποιημένου statement (\texttt{?}) αντί για δυναμικά κατασκευασμένο query.} \label{fig:sqli_fix_db_query} \end{figure} Συνεπώς, η χρήση prepared statements εξαλείφει το συγκεκριμένο κενό ασφάλειας στο σημείο αυθεντικοποίησης, διατηρώντας παράλληλα αμετάβλητη τη λειτουργικότητα της εφαρμογής. \subsection{Περαιτέρω σημεία ευπάθειας σε SQL Injection} Πέρα από το αρχικό κενό ασφάλειας στον μηχανισμό αυθεντικοποίησης, εντοπίστηκαν \textbf{επιπλέον σημεία} στην εφαρμογή όπου SQL εντολές κατασκευάζονταν δυναμικά μέσω συνένωσης συμβολοσειρών με δεδομένα προερχόμενα από τον χρήστη ή από session μεταβλητές. Τα σημεία αυτά δημιουργούν πρόσθετες επιφάνειες επίθεσης και καθιστούν την εφαρμογή ευάλωτη σε άμεσες ή έμμεσες (second-order) επιθέσεις SQL Injection. \subsubsection{Εγγραφή νέων χρηστών (\textit{register.php})} Στο αρχείο \textit{register.php}, η εγγραφή νέου χρήστη υλοποιούνταν με απευθείας ενσωμάτωση των πεδίων \textit{username} και \textit{password} σε εντολή \texttt{INSERT}. Το Απόσπασμα παρακάτω παρουσιάζει τον προβληματικό κώδικα: \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}');"; $result = $conn->query($sql_query); \end{minted} \label{lst:sqli-register} \subsubsection{Αποθήκευση σημειώσεων (\textit{notes.php})} Στο \textit{notes.php}, η αποθήκευση νέων σημειώσεων πραγματοποιούνταν με δυναμική κατασκευή εντολής \texttt{INSERT}, η οποία περιλάμβανε τόσο το περιεχόμενο της σημείωσης όσο και το \textit{username} του συνδεδεμένου χρήστη. Ο προβληματικός κώδικας φαίνεται στο Απόσπασμα εδώ: \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}')"; $result = $conn->query($sql_query); \end{minted} \label{lst:sqli-notes} Παρότι το \textit{username} προέρχεται από session μεταβλητή, δεν μπορεί να θεωρηθεί έμπιστο, καθώς μπορεί να αλλοιωθεί σε σενάρια XSS ή session hijacking. \subsubsection{Διαχείριση διαπιστευτηρίων ιστοσελίδων (\textit{dashboard.php})} Στο αρχείο \textit{dashboard.php} εντοπίστηκαν πολλαπλά σημεία SQL Injection που αφορούν την εισαγωγή, διαγραφή και προβολή αποθηκευμένων διαπιστευτηρίων. Ενδεικτικά, το Απόσπασμα παρακάτω δείχνει τον τρόπο εισαγωγής νέας εγγραφής: \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}');"; $result = $conn->query($sql_query); \end{minted} \label{lst:sqli-dashboard-insert} Αντίστοιχα, η διαγραφή εγγραφών βασιζόταν σε δυναμικά κατασκευασμένη εντολή \texttt{DELETE}, όπως φαίνεται στο επόμενο Απόσπασμα. \begin{minted}{php} // Vulnerable DELETE statement: identifier injected directly into SQL. $sql_query = "DELETE FROM websites WHERE webid='{$webid}';"; $result = $conn->query($sql_query); \end{minted} \label{lst:sqli-dashboard-delete} Τέλος, ακόμη και η προβολή της λίστας διαπιστευτηρίων βασιζόταν σε SQL εντολή με απευθείας ενσωμάτωση session μεταβλητών. \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}';"; $result = $conn->query($sql_query); \end{minted} \label{lst:sqli-dashboard-select} Η συνολική αντιμετώπιση των παραπάνω σημείων βασίστηκε στην ίδια αρχή με το αρχικό SQL Injection, ότι \textbf{καμία τιμή που επηρεάζει τη σύνταξη SQL εντολής δεν ενσωματώνεται πλέον απευθείας στο query}. Όλα τα παραπάνω αντικαταστάθηκαν με prepared statements και δεσμευμένες παραμέτρους, εξαλείφοντας τόσο άμεσα όσο και έμμεσα σενάρια SQL Injection. Ο αναγνώστης μπορεί \href{\fixsqlitag}{εδώ} να βρει το branch με όλες τις αλλαγές. % ===================================================================== % Stored XSS % ===================================================================== \section{Stored XSS} \label{sec:xss_notes} Η συγκεκριμένη εφαρμογή είναι ευάλωτη σε \textit{Stored Cross-Site Scripting (Stored XSS)} σε αρκετά σημεία. Όπως και πριν, αρχικά θα επικεντρωθούμε σε ένα και πιο συγκεκριμένα σε αυτό μέσω της λειτουργίας σημειώσεων (\textit{notes}), ώστε να παρουσιάσουμε το πρόβλημα και τη τεχνική αντιμετώπισης, και έπειτα θα αναφέρουμε και τα υπόλοιπα σημεία. Η ευπάθεια προκύπτει όταν περιεχόμενο που εισάγει ο χρήστης αποθηκεύεται στη βάση δεδομένων και στη συνέχεια προβάλλεται -- πιθανόν σε άλλους χρήστες -- χωρίς κατάλληλη κωδικοποίηση εξόδου (output encoding). Ως αποτέλεσμα, κακόβουλος κώδικας JavaScript μπορεί να εκτελεστεί στον browser του θύματος με τα δικαιώματα του αντίστοιχου session. \subsection{XSS στο \textit{notes.php}} Στο \textit{notes.php}, οι αποθηκευμένες σημειώσεις προβάλλονται απευθείας μέσα σε HTML με χρήση \texttt{echo}, χωρίς escaping. Ενδεικτικά, το Απόσπασμα παρακάτω δείχνει το προβληματικό σημείο: το \texttt{\$row["note"]} (δεδομένο από τη βάση) θεωρείται ως έμπιστο και εισάγεται αυτούσιο στο DOM. \begin{minted}{php} while ($row = $result -> fetch_assoc()) { echo "
"; echo "
" . $row["note"] . "
"; echo "
by " . $row["username"] . "
"; // ... \end{minted} \label{lst:xss-notes-echo} \subsection{Attacker-side υποδομή} Για την τεκμηρίωση της επίθεσης, χρησιμοποιήθηκε ο υποκατάλογος \textit{xss/} ως ``attacker side''. Το \textit{getcookie.php} δέχεται μία τιμή μέσω παραμέτρου \texttt{v} και την αποθηκεύει στο \textit{stolencookies.txt}. Στη συνέχεια, το \textit{listcookies.php} εμφανίζει τα ``κλεμμένα'' cookies, ενώ το \textit{usecookie.php} επιτρέπει την επαναχρησιμοποίησή τους (session hijacking demonstration). \subsection{Σενάριο A: Stored XSS με άμεση εξαγωγή cookie} \label{sec:xss_scn_a} Στο πρώτο σενάριο εισήχθη σημείωση που περιείχε JavaScript payload, το οποίο αποστέλλει το document.cookie στον attacker-side logger, όπως φαίνεται παρακάτω: \begin{verbatim} \end{verbatim} Στην Εικόνα~\ref{fig:xss_scn_a_listcookies} εμφανίζεται το αποθηκευμένο session cookie (\texttt{PHPSESSID}) στη λίστα κλεμμένων cookies. \begin{figure}[!ht] \centering \includegraphics[width=0.55\textwidth]{img/xss-ScA-ListCookie.png} \caption{Σενάριο A: Εμφάνιση του ``κλεμμένου'' session cookie (\texttt{PHPSESSID}) μέσω \textit{listcookies.php}.} \label{fig:xss_scn_a_listcookies} \end{figure} Κατά την προβολή της σελίδας, ο browser εκτελεί το payload και πραγματοποιεί HTTP request προς \textit{getcookie.php}, όπως τεκμηριώνεται στα logs του web server (Εικόνα~\ref{fig:xss_scn_a_weblogs}). \begin{figure}[!ht] \centering \includegraphics[width=\textwidth]{img/xss-ScA-WebLogs.png} \caption{Σενάριο A: Καταγραφή στο web server που δείχνει HTTP request προς \textit{/passman/xss/getcookie.php} με παράμετρο \texttt{v=PHPSESSID=...}.} \label{fig:xss_scn_a_weblogs} \end{figure} \subsection{Σενάριο B: Παραλλαγή payload με obfuscation} \label{sec:xss_scn_b} Στο δεύτερο σενάριο χρησιμοποιήθηκε παραλλαγή payload που επιτυγχάνει την ίδια λειτουργία (εξαγωγή cookie) αλλά με διαφορετική μορφή (obfuscation), ώστε να καταδειχθεί ότι το πρόβλημα δεν περιορίζεται σε ένα συγκεκριμένο ``μοτίβο'' εισαγωγής. Η σημείωση που χρησιμοποιήθηκες είναι: \begin{verbatim} \end{verbatim} Στην Εικόνα~\ref{fig:xss_scn_b_listcookies} φαίνεται ότι το cookie αποθηκεύεται επιτυχώς και σε αυτήν την περίπτωση. \begin{figure}[!ht] \centering \includegraphics[width=0.55\textwidth]{img/xss-ScB-ListCookie.png} \caption{Σενάριο B: Καταγραφή session cookie στη λίστα κλεμμένων cookies.} \label{fig:xss_scn_b_listcookies} \end{figure} Στα logs (Εικόνα~\ref{fig:xss_scn_b_weblogs}) παρατηρείται και πάλι request προς \textit{getcookie.php}. \begin{figure}[!ht] \centering \includegraphics[width=\textwidth]{img/xss-ScB-WebLogs.png} \caption{Σενάριο B: Καταγραφή στο web server που δείχνει την επιτυχή κλήση του \textit{getcookie.php} από το payload.} \label{fig:xss_scn_b_weblogs} \end{figure} \paragraph{Επιβεβαίωση αποθήκευσης payload στη βάση} Επειδή πρόκειται για \textit{stored} XSS, το payload αποθηκεύεται στη βάση δεδομένων και εκτελείται κάθε φορά που προβάλλεται η σελίδα. Στην Εικόνα~\ref{fig:xss_db_entries} φαίνεται ότι οι κακόβουλες σημειώσεις αποθηκεύτηκαν στον πίνακα \texttt{notes}. \begin{figure}[!ht] \centering \includegraphics[width=0.92\textwidth]{img/xss-ScX-MySQLEntries.png} \caption{Καταγραφή στη βάση: αποθηκευμένες σημειώσεις που περιέχουν τα XSS payloads (persistent storage).} \label{fig:xss_db_entries} \end{figure} \subsubsection{Session hijacking demonstration μέσω \textit{usecookie.php}} \label{sec:xss_hijack} Ο αντίκτυπος της ευπάθειας είναι ιδιαίτερα σοβαρός, καθώς το \textbf{session cookie μπορεί να χρησιμοποιηθεί για πλαστοπροσωπία} (session hijacking). Στην Εικόνα~\ref{fig:xss_usecookie} φαίνεται η χρήση του \textit{usecookie.php} με παράμετρο το κλεμμένο \texttt{PHPSESSID}. Στη συνέχεια, η πρόσβαση σε προστατευμένη σελίδα της εφαρμογής (π.χ. \textit{dashboard.php}) είναι εφικτή ως ο χρήστης-θύμα, όπως φαίνεται στην Εικόνα~\ref{fig:xss_dashboard_after_hijack}. \begin{figure}[!ht] \begin{subfigure}{.48\textwidth} \centering \includegraphics[width=\linewidth]{img/xss-ScX-UseCookie.png} \caption{Επαναχρησιμοποίηση του κλεμμένου \texttt{PHPSESSID} μέσω \textit{usecookie.php}.} \label{fig:xss_usecookie} \end{subfigure} \hfill \begin{subfigure}{.48\textwidth} \centering \includegraphics[width=\linewidth]{img/xss-SxX-StolenSession.png} \caption{Πρόσβαση στο \textit{dashboard.php} μετά την επαναχρησιμοποίηση του session cookie.} \label{fig:xss_dashboard_after_hijack} \end{subfigure} \caption{Session hijacking demonstration.} \end{figure} Με βάση τα παραπάνω, επιβεβαιώνεται ότι η εφαρμογή επιτρέπει αποθήκευση και εκτέλεση κακόβουλου JS κώδικα μέσω του μηχανισμού σημειώσεων, με αποτέλεσμα τόσο την εξαγωγή cookies όσο και την πρακτική επίδειξη πλαστοπροσωπίας. \subsection{Αντιμετώπιση Stored XSS στο \textit{notes.php}} \label{sec:xss_fix} Η αντιμετώπιση του Stored XSS βασίζεται στην αρχή ότι \textbf{οποιοδήποτε περιεχόμενο προέρχεται από τον χρήστη ή/και τη βάση δεδομένων πρέπει να θεωρείται μη-έμπιστο}. Στην αρχική υλοποίηση, το περιεχόμενο των σημειώσεων εμφανιζόταν απευθείας στο HTML, με αποτέλεσμα ο browser να το ερμηνεύει ως markup και να εκτελεί τυχόν ενσωματωμένο JavaScript. Η καθιερωμένη πρακτική για την αποτροπή XSS σε \textit{HTML body context} είναι το \textbf{context-aware output encoding}, δηλαδή η κωδικοποίηση ειδικών χαρακτήρων (π.χ. \texttt{<}, \texttt{>}, \texttt{"}, \texttt{'}) πριν από την προβολή τους. Με αυτόν τον τρόπο, ακόμη και αν στη βάση υπάρχουν αποθηκευμένα payloads, αυτά προβάλλονται ως απλό κείμενο και δεν είναι δυνατόν να εκτελεστούν. \subsubsection{Υλοποίηση της διόρθωσης} Η διόρθωση πραγματοποιήθηκε με ελάχιστη αλλαγή στο \textit{notes.php}, στο σημείο προβολής των σημειώσεων. Συγκεκριμένα, πριν την εκτύπωση του περιεχομένου, εφαρμόστηκε \texttt{htmlspecialchars()} με επιλογές \texttt{ENT\_QUOTES} και \texttt{UTF-8}, έτσι ώστε να γίνεται ασφαλής απόδοση των δεδομένων στο HTML. Το Απόσπασμα παρακάτω παρουσιάζει τον διορθωμένο κώδικα: \begin{minted}{php} // Escape output to prevent stored XSS (DB content must be treated as untrusted). $safe_note = htmlspecialchars($row["note"], ENT_QUOTES | ENT_SUBSTITUTE, "UTF-8"); $safe_user = htmlspecialchars($row["username"], ENT_QUOTES | ENT_SUBSTITUTE, "UTF-8"); echo "
" . $safe_note . "
"; echo "
by " . $safe_user . "
"; \end{minted} \subsubsection{Επαλήθευση της διόρθωσης} Μετά την εφαρμογή του output encoding, τα ήδη αποθηκευμένα payloads στη βάση δεν απαιτείται να διαγραφούν. Αντίθετα, προβάλλονται ως απλό κείμενο, όπως φαίνεται στην Εικόνα~\ref{fig:xss_fix_notes_plaintext}, όπου οι συμβολοσειρές \texttt{} εμφανίζονται χωρίς να εκτελούνται. \begin{figure}[!ht] \centering \includegraphics[width=0.4\textwidth]{img/xss-SxX-NotesSubmitedFix2.png} \caption{Μετά τη διόρθωση: τα XSS payloads εμφανίζονται ως απλό κείμενο (δεν εκτελείται JavaScript).} \label{fig:xss_fix_notes_plaintext} \end{figure} Επιπλέον, η απουσία εκτέλεσης επιβεβαιώνεται και από τα logs του web server: ενώ καταγράφονται αιτήματα προς \textit{notes.php}, δεν καταγράφονται πλέον αιτήματα προς \textit{/passman/xss/getcookie.php} (δηλαδή δεν πραγματοποιείται cookie exfiltration), όπως φαίνεται στην Εικόνα~\ref{fig:xss_fix_weblogs}. \begin{figure}[!ht] \centering \includegraphics[width=\textwidth]{img/xss-SxX-WebLogsFix.png} \caption{Μετά τη διόρθωση: καταγράφονται μόνο αιτήματα προς \textit{notes.php} και απουσιάζουν τα αιτήματα προς \textit{/passman/xss/getcookie.php}.} \label{fig:xss_fix_weblogs} \end{figure} Συνεπώς, η εφαρμογή \textit{context-aware output encoding} στο σημείο προβολής \textbf{εξαλείφει την εκτέλεση αποθηκευμένων XSS payloads} στο συγκεκριμένο context, διατηρώντας αμετάβλητη τη λειτουργικότητα της εφαρμογής. \subsection{Επέκταση της αντιμετώπισης XSS στα επιπλέον σημεία} Η ευπάθεια Stored XSS που παρουσιάστηκε μέσω της λειτουργίας σημειώσεων \textbf{δεν αποτελεί μεμονωμένο περιστατικό}, αλλά ενδεικτικό ενός γενικότερου προγραμματιστικού μοτίβου στην εφαρμογή. Δεδομένα προερχόμενα από χρήστες ή από τη βάση δεδομένων προβάλλονταν απευθείας στο HTML χωρίς κατάλληλη κωδικοποίηση εξόδου. Για τον λόγο αυτό πραγματοποιήθηκε εκτενέστερη ανάλυση και εντοπίστηκαν επιπλέον σημεία με δυνητική ή άμεση ευπάθεια σε XSS. \subsubsection{Εμφάνιση διαπιστευτηρίων ιστοσελίδων (\textit{dashboard.php})} Στο \textit{dashboard.php}, τα πεδία \textit{web\_url}, \textit{web\_username} και \textit{web\_password} προέρχονται από δεδομένα που εισάγονται από τον χρήστη, αποθηκεύονται στη βάση δεδομένων και στη συνέχεια εμφανίζονται στη διεπαφή. Στην αρχική υλοποίηση, τα δεδομένα αυτά προβάλλονταν απευθείας, όπως φαίνεται εδώ: \begin{minted}{php} // Vulnerable output: database-backed user input rendered without escaping. echo "" . $row["web_url"] . ""; echo "" . $row["web_username"] . ""; echo "" . $row["web_password"] . ""; \end{minted} Η πρακτική αυτή επιτρέπει την αποθήκευση και εκτέλεση κακόβουλου HTML ή JavaScript κώδικα (\textit{Stored XSS}), με εκτέλεση του payload κάθε φορά που προβάλλεται η σελίδα. \subsubsection{Εμφάνιση ονόματος χρήστη από session (\textit{dashboard.php})} Στην ίδια σελίδα, το όνομα του συνδεδεμένου χρήστη εμφανιζόταν στο header της σελίδας. Η τιμή αυτή προέρχεται από session μεταβλητή και στην αρχική υλοποίηση εμφανιζόταν χωρίς κωδικοποίηση εξόδου, όπως φαίνεται εδώ: \begin{minted}{php} // Vulnerable output: session-derived value treated as trusted. echo "

Entries of " . $username . "

"; \end{minted} Παρότι τα session δεδομένα συχνά θεωρούνται έμπιστα, στην πράξη μπορούν να αλλοιωθούν σε σενάρια XSS, session hijacking ή cookie tampering. Η συγκεκριμένη περίπτωση αποτελεί χαρακτηριστικό παράδειγμα \textbf{second-order XSS}. Η αντιμετώπιση βασίστηκε στην κωδικοποίηση της τιμής πριν την εμφάνιση της. \subsubsection{Reflected XSS σε μηνύματα σφάλματος (\textit{login.php}, \textit{register.php})} Κατά την ανάλυση εξετάστηκε και η περίπτωση εμφάνισης μηνυμάτων σφάλματος προς τον χρήστη. Στα αρχεία \textit{login.php} και \textit{register.php} τα μηνύματα αυτά εμφανίζονται με χρήση μεταβλητής, όπως παρακάτω: \begin{minted}{php} // Display login or registration status message. echo "

" . $login_message . "

"; \end{minted} Στην παρούσα υλοποίηση, οι μεταβλητές αυτές λαμβάνουν μόνο \textbf{στατικές συμβολοσειρές} και δεν ενσωματώνουν δεδομένα εισόδου χρήστη. Ως εκ τούτου, δεν είναι πρακτικά εκμεταλλεύσιμες για \textit{reflected XSS} στην τρέχουσα μορφή της εφαρμογής. Για τον λόγο αυτό, δεν πραγματοποιήθηκε περαιτέρω εκμετάλλευση, αλλά το σημείο καταγράφεται ως θεωρητικό σενάριο που θα απαιτούσε αντιμετώπιση σε μελλοντική επέκταση της εφαρμογής. Ο αναγνώστης μπορεί \href{\fixxsstag}{εδώ} να βρει το branch με όλες τις αλλαγές. % ===================================================================== % Plaintext authentication credentials (login passwords) + mitigation % ===================================================================== \section{Αποθήκευση κωδικών αυθεντικοποίησης σε απλό κείμενο} \label{subsec:plaintext-auth} Η εφαρμογή, στην αρχική της υλοποίηση, διαχειριζόταν τους \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 = ?}, με άμεση σύγκριση του κωδικού που εισάγει ο χρήστης με τον αποθηκευμένο κωδικό. \begin{minted}{php} $sql_query = "SELECT * FROM login_users WHERE username='{$username}' AND password='{$password}';"; $result = $conn->query($sql_query); if (!empty($result) && $result->num_rows >= 1) { $_SESSION['username'] = $username; $_SESSION['loggedin'] = true; // ... } \end{minted} \item \textbf{Αποθήκευση κωδικών τρίτων ιστοσελίδων (\texttt{dashboard.php}):} Οι κωδικοί πρόσβασης ιστοσελίδων αποθηκεύονται και εμφανίζονται επίσης ως απλό κείμενο. Η λύση του συγκεκριμένου ζητήματος απαιτεί τη δημιουργία επιπλέον κώδικα για την κωδικοποίηση και αποκωδικοποίηση των δεδομένων, αλλά και αρκετές αλλαγές σε επίπεδο αρχητεκτονικής. Για παράδειγμα πρέπει να αποφασιστεί που θα αποθηκεύονται τα κλειδιά κωδικοποίησης, πως θα γίνει η αναδιάρθρωση του κώδικα για να αρχικοποιούνται τα κλειδιά κλπ. Για το λόγο αυτό \textbf{η υλοποίησή κρίθηκε εκτός ορίων για τη συγκεκριμένη εργασία}. Αν όμως θα υλοποιούσαμε τέτοια αλλαγή, η λύση θα έπρεπε να προσθέσει ένα νέο αρχείο, π.χ. \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 χρήση συναρτήσεων \textbf{hashing} με ενσωματωμένο salt κατά την εγγραφή, \item \textbf{επαλήθευση} του κωδικού κατά τη σύνδεση μέσω σύγκρισης hash και \textbf{όχι μέσω SQL}. \end{itemize} Στην παρούσα εργασία χρησιμοποιούνται οι συναρτήσεις \texttt{password\_hash()} και \texttt{password\_verify()} της PHP, οι οποίες θεωρούνται βέλτιστη πρακτική για την αποθήκευση κωδικών. \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 (\textit{login.php}), η επαλήθευση στον κωδικό γίνεται με την \textit{password\_verify}. \begin{minted}{php} $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{Απαιτούμενες αλλαγές περιβάλλοντος και βάσης δεδομένων} Μετά την αλλαγή του μηχανισμού αυθεντικοποίησης, οι ήδη αποθηκευμένοι plaintext κωδικοί πρέπει να αντικατασταθούν από hashes. Για τον προ-εγκατεστημένο χρήστη δοκιμών (\texttt{u1/p1}), η διαδικασία πραγματοποιήθηκε χειροκίνητα στο περιβάλλον Docker, ως εξής: Αρχικά δημιουργήσαμε ένα hashed κωδικό για τον υπάρχον χρήστη: \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{minted} Έπειτα αλλάξαμε των κωδικό χειροκίνητα στη βάση: \begin{minted}{sql} MariaDB [pwd_mgr]> SELECT * FROM login_users; +----+----------+----------+ | id | username | password | +----+----------+----------+ | 1 | u1 | p1 | +----+----------+----------+ MariaDB [pwd_mgr]> UPDATE login_users > SET password = '$2y$10$L18u5/PyVkDgsce/DsUOQu0sKhTzh854Euhog3cVb1W4YAfgRzY8W' > WHERE username='u1'; MariaDB [pwd_mgr]> SELECT * FROM login_users; +----+----------+--------------------------------------------------------------+ | id | username | password | +----+----------+--------------------------------------------------------------+ | 1 | u1 | $2y$10$L18u5/PyVkDgsce/DsUOQu0sKhTzh854Euhog3cVb1W4YAfgRzY8W | +----+----------+--------------------------------------------------------------+ \end{minted} Η παραπάνω διαδικασία επιβεβαιώνει την επιτυχή αντικατάσταση του plaintext κωδικού με ασφαλές hash, διασφαλίζοντας τη συμβατότητα της βάσης δεδομένων με τη νέα υλοποίηση αυθεντικοποίησης. Τέλος ενημερώσαμε τον ίδιο κωδικό και στο αρχείο αρχικοποίησης (\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} Στην Εικόνα~\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} % ===================================================================== % DB admin credentials (root) used by the web app + mitigation % ===================================================================== \section{Χρήση διαπιστευτηρίων διαχειριστή (root)} \label{subsec:db-root-user} Ένα επιπλέον σημαντικό πρόβλημα ασφάλειας της εφαρμογής είναι ότι η \textbf{σύνδεση} προς τη βάση δεδομένων πραγματοποιείται με \textbf{διαπιστευτήρια διαχειριστή} (\textit{administrator credentials}, χρήστης \texttt{root}). Η πρακτική αυτή παραβιάζει τη θεμελιώδη αρχή του \textit{least privilege} (ελάχιστα απαραίτητα προνόμια), διότι οποιοσδήποτε επιτιθέμενος αποκτήσει δυνατότητα εκτέλεσης SQL εντολών (π.χ. μέσω SQL injection ή μέσω πρόσβασης στη βάση) δεν περιορίζεται από δικαιώματα και μπορεί να πραγματοποιήσει καταστροφικές ενέργειες. Το πρόβλημα εντοπίζεται στα ακόλουθα σημεία. \begin{itemize} \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} \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} \label{lst:compose-web-root} \item SQL init (δεν υπάρχει dedicated χρήστης εφαρμογής). Στο αρχείο αρχικοποίησης \textit{01-create-pwd\_mgr-db-withData.sql} δημιουργούνται tables και demo δεδομένα, όμως δεν δημιουργείται ξεχωριστός χρήστης βάσης δεδομένων με περιορισμένα δικαιώματα (least privilege). \end{itemize} \subsection{Επιπτώσεις ασφάλειας} Η χρήση διαπιστευτηρίων διαχειριστή σημαίνει ότι σε περίπτωση παραβίασης: \begin{itemize} \item είναι δυνατή η \textbf{πλήρης ανάγνωση} όλων των δεδομένων (π.χ. users, notes, websites), \item είναι δυνατές \textbf{καταστροφικές ενέργειες} (\texttt{DROP}, \texttt{TRUNCATE}, μαζικά \texttt{DELETE}), \item είναι πιθανή \textbf{μόνιμη παραβίαση} (π.χ. αλλαγές σε grants/χρήστες), ανάλογα με τη ρύθμιση του DB server. \end{itemize} Με άλλα λόγια, ακόμη και αν διορθωθούν SQLi σε επίπεδο κώδικα, η χρήση root αυξάνει σημαντικά τον αντίκτυπο (impact amplification) οποιασδήποτε επιτυχούς επίθεσης. \subsection{Αντιμετώπιση: least privilege χρήστης βάσης δεδομένων} Η αντιμετώπιση βασίζεται στη δημιουργία ενός dedicated χρήστη εφαρμογής (π.χ. \texttt{passman\_app}), στον οποίο εκχωρούνται μόνο τα απολύτως απαραίτητα δικαιώματα στο schema \texttt{pwd\_mgr}. \begin{itemize} \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). -- Grant only the required privileges on the application database. CREATE USER IF NOT EXISTS 'passman_app'@'%' IDENTIFIED BY 'passman_app_pw'; GRANT SELECT, INSERT, UPDATE, DELETE ON pwd_mgr.* TO 'passman_app'@'%'; FLUSH PRIVILEGES; \end{minted} \label{lst:db-create-app-user} Με αυτόν τον τρόπο, η εφαρμογή μπορεί να λειτουργήσει κανονικά, ενώ παράλληλα \textbf{δεν επιτρέπονται επικίνδυνα δικαιώματα} όπως \texttt{DROP}, \texttt{ALTER}, \texttt{CREATE USER}, \texttt{GRANT OPTION}. \item Αλλαγή στο docker-compose.yml: web \textbf{συνδέεται ως passman\_app}. Στη συνέχεια, στο \textit{docker-compose.yml} αλλάχθηκαν τα credentials της web υπηρεσίας ώστε να χρησιμοποιείται ο νέος χρήστης: \begin{minted}{yaml} services: web: environment: DB_HOST: db DB_USER: passman_app DB_PASS: passman_app_pw DB_NAME: pwd_mgr \end{minted} \label{lst:compose-web-appuser} Ο χρήστης \texttt{root} παραμένει διαθέσιμος μόνο για διαχειριστικές εργασίες στο DB container (\textit{administration}), αλλά η εφαρμογή δεν τον χρησιμοποιεί πλέον. \item Αλλαγή στο config.php: ασφαλέστερα defaults / αποφυγή fallback σε root. Τέλος, στο \textit{config.php} αφαιρέθηκε το επικίνδυνο fallback σε root. Υπάρχουν δύο ασφαλείς επιλογές: \begin{itemize} \item \textbf{Επιλογή 1 (fail closed):} αν λείπουν env vars, η εφαρμογή σταματάει με μήνυμα σφάλματος. \item \textbf{Επιλογή 2 (safe defaults):} fallback σε \texttt{passman\_app} αντί για \texttt{root}. \end{itemize} Στην παρούσα εργασία ακολουθήθηκε η \textit{safe defaults} επιλογή: \begin{minted}{php} $DB_HOST = getenv('DB_HOST') ?: 'db'; $DB_USER = getenv('DB_USER') ?: 'passman_app'; $DB_PASS = getenv('DB_PASS') ?: 'passman_app_pw'; $DB_NAME = getenv('DB_NAME') ?: 'pwd_mgr'; \end{minted} \label{lst:db-config-fixed} \end{itemize} Με τις παραπάνω αλλαγές, \textbf{η εφαρμογή λειτουργεί με περιορισμένα δικαιώματα στη βάση δεδομένων}, μειώνοντας σημαντικά τον αντίκτυπο (impact reduction) πιθανών επιθέσεων. Η πρακτική αυτή αποτελεί βασικό μέτρο \textbf{defense-in-depth} και ευθυγραμμίζεται με θεμελιώδεις αρχές secure design. Ένα παράδειγμα φαίνεται και στο στιγμιότυπο παρακάτω, όπου η βάση απορρίπτει τη διαγραφή πίνακα, στον χρήστη passman\_app: \begin{minted}{sql} -- $ docker compose exec db mariadb -upassman_app -ppassman_app_pw Welcome to the MariaDB monitor. Commands end with ; or \g. Your MariaDB connection id is 5 Server version: 11.8.5-MariaDB-ubu2404 mariadb.org binary distribution Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. MariaDB [(none)]> DROP TABLE pwd_mgr.login_users; ERROR 1142 (42000): DROP command denied to user 'passman_app'@'localhost' for table `pwd_mgr`.`login_users` MariaDB [(none)]> \end{minted} % ===================================================================== % HTTP -> HTTPS (Transport security) + mitigation % ===================================================================== \section{Χρήση HTTP αντί HTTPS} \label{subsec:http-https} Η εφαρμογή αρχικά λειτουργεί αποκλειστικά μέσω του μη ασφαλούς πρωτοκόλλου HTTP. Όπως αναφέρεται και στην εκφώνηση, η χρήση HTTP επιτρέπει σε έναν επιτιθέμενο που παρακολουθεί την κίνηση του δικτύου (\textit{network observer / man-in-the-middle}) \textbf{να υποκλέψει τις πληροφορίες} που εμφανίζονται στον χρήστη και τα δεδομένα που αυτός αποστέλλει (π.χ. username/password στα forms). Σε πραγματικό περιβάλλον, αυτό σημαίνει ότι: \begin{itemize} \item τα \textbf{credentials} μπορούν να διαρρεύσουν σε \textbf{plaintext}, \item το \textbf{session cookie} μπορεί να \textbf{υποκλαπεί}, \item το περιεχόμενο σελίδων (π.χ. αποθηκευμένοι κωδικοί ιστοσελίδων) μπορεί να \textbf{αναγνωστεί από τρίτους}. \end{itemize} \subsection{Πού εμφανίζεται στην υλοποίηση} Στο Docker Compose, η web υπηρεσία εκθέτει προς τα έξω μόνο την πόρτα 80 (HTTP), χωρίς καμία μορφή TLS. \begin{minted}{yaml} services: web: build: . ports: - "80:80" \end{minted} \label{lst:http-compose-before} Επιπλέον, στο \textit{index.html} υπάρχουν hard-coded σύνδεσμοι προς \texttt{http://localhost/...}, οι οποίοι επιβάλλουν τη χρήση HTTP από τον browser. \begin{minted}{html} Login Page // ... Dashboard Notes \end{minted} \label{lst:http-index-before} \subsection{Αντιμετώπιση} \label{subsec:https-fix} Για να επιτευχθεί ασφαλής μεταφορά δεδομένων (\textit{transport security}), απαιτείται η χρήση HTTPS. Σε περιβάλλον Docker, ένας πρακτικός και καθαρός τρόπος είναι να προστεθεί ένα reverse proxy (π.χ. Caddy / NGINX) μπροστά από τον Apache web server. Το reverse proxy αναλαμβάνει: \begin{itemize} \item \textbf{TLS termination} (διαπραγμάτευση κρυπτογράφησης), \item αυτόματη \textbf{ανακατεύθυνση} από HTTP σε HTTPS, \item \textbf{προώθηση} (proxying) \textbf{της κίνησης} προς το εσωτερικό container της εφαρμογής. \end{itemize} Με αυτή την αρχιτεκτονική, το container \texttt{web} \textbf{δεν εκτίθεται πλέον} απευθείας προς τα έξω. Εκτίθενται μόνο οι πόρτες 80/443 του reverse proxy. \subsection{Αλλαγές στο docker-compose.yml} Αφαιρέθηκε το \texttt{ports: "80:80"} από τη web υπηρεσία και προστέθηκε νέα υπηρεσία \texttt{proxy} (Caddy) η οποία εκθέτει τις θύρες 80 (redirect) και 443 (HTTPS): \begin{minted}{yaml} services: proxy: image: caddy:2 ports: - "80:80" - "443:443" volumes: - ./Caddyfile:/etc/caddy/Caddyfile:ro - caddy_data:/data - caddy_config:/config depends_on: - web volumes: caddy_data: caddy_config: \end{minted} \label{lst:https-compose-after} \subsection{Ρύθμιση reverse proxy -- Caddyfile} Για χρήση σε τοπικό περιβάλλον (\texttt{localhost}), \textbf{επιλέχθηκε self-signed} (\texttt{tls internal}). Αυτό είναι κατάλληλο για demo/testing, καθώς δεν απαιτεί domain name ή δημόσιο πιστοποιητικό. Το \textit{Caddyfile} είναι: \begin{minted}{nginx} # HTTP site: redirect everything to HTTPS http://localhost { redir https://{host}{uri} permanent } # HTTPS site https://localhost { reverse_proxy web:80 tls internal # Optional: security headers (defense-in-depth) header { X-Content-Type-Options "nosniff" X-Frame-Options "DENY" Referrer-Policy "no-referrer" } } \end{minted} \label{lst:caddyfile} \paragraph{Σημείωση για τοπικά πιστοποιητικά (browser warning).} Η χρήση \texttt{tls internal} δημιουργεί self-signed πιστοποιητικό, το οποίο ενδέχεται να προκαλέσει \textbf{warning στον browser}. Το γεγονός αυτό είναι αναμενόμενο σε τοπικό demo περιβάλλον και δεν αναιρεί τη λειτουργία κρυπτογράφησης. Σε παραγωγικό περιβάλλον θα χρησιμοποιούνταν έγκυρο πιστοποιητικό (π.χ. μέσω Let's Encrypt) και domain name. \subsubsection{Αλλαγές στο index.html} Για να αποφευχθεί το hard-coding του scheme (\texttt{http://}) και να λειτουργεί σωστά τόσο σε HTTP$\rightarrow$HTTPS redirect όσο και σε μελλοντικό deployment, οι σύνδεσμοι άλλαξαν ώστε να είναι \textbf{relative URLs}: \begin{minted}{html} Login Page Dashboard Notes \end{minted} \label{lst:https-index-after} \subsection{Επαλήθευση} Όπως φαίνεται και στην Εικόνα~\ref{fig:https-sample}, μετά τις αλλαγές, η εφαρμογή είναι διαθέσιμη μέσω:\\ \texttt{https://localhost/passman}. \begin{figure}[!ht] \centering \includegraphics[width=0.55\textwidth]{img/https-Sample.png} \caption{λειτουργία μέσω https (και εμφάνιση του warning).} \label{fig:https-sample} \end{figure} Επιπλέον, η πρόσβαση σε \texttt{http://localhost/passman} οδηγεί σε αυτόματη ανακατεύθυνση προς HTTPS, επιβεβαιώνοντας ότι η μεταφορά δεδομένων προστατεύεται σε επίπεδο δικτύου. \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), ενισχύοντας τη συνολική ασφάλεια της εφαρμογής. \section{Συμπεράσματα} Στην παρούσα εργασία μελετήθηκε μια απλή διαδικτυακή εφαρμογή διαχείρισης κωδικών πρόσβασης, με στόχο την \textbf{ανάδειξη και αντιμετώπιση κρίσιμων κενών ασφάλειας} που προκύπτουν από μη ασφαλείς προγραμματιστικές πρακτικές. Η προσέγγιση που ακολουθήθηκε ήταν πειραματική και βασισμένη σε πραγματικά σενάρια επίθεσης. Αρχικά εντοπίστηκαν ευπάθειες όπως SQL Injection, Stored Cross-Site Scripting, αποθήκευση κωδικών σε απλό κείμενο, χρήση διαπιστευτηρίων διαχειριστή για τη βάση δεδομένων και απουσία κρυπτογράφησης στο επίπεδο μεταφοράς (HTTP). Για κάθε περίπτωση \textit{παρουσιάστηκε ο τρόπος εκμετάλλευσης, τεκμηριώθηκαν οι επιπτώσεις και στη συνέχεια εφαρμόστηκαν στοχευμένα διορθωτικά μέτρα}, με \textbf{ελάχιστη αλλά ουσιαστική παρέμβαση στον κώδικα} και στη συνολική αρχιτεκτονική. Οι διορθώσεις υλοποιήθηκαν με βάση θεμελιώδεις αρχές ασφαλούς σχεδίασης, όπως ο \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}