# Projet 2 SR03: Application de banque en ligne sécurisée Ce rapport est réalisé par Valentin CHELLE & Colin LAFOND pour le semestre P20. Il présente une liste des bonnes pratiques pour sécuriser une application web, ainsi qu'une synthèse sur un autre type de sécurité web qui n'a pas été étudié en cours. **Il est recommandé de lire ce rapport directement sur Gitlab, afin d'avoir un meilleur formatage du fichier et de ne pas voir de troncatures dues à la conversion Markdown -> PDF.** Liens git du projet : https://gitlab.utc.fr/chelleva/sr03-td-securite/ ## Organisation du projet : Pour réaliser le projet, nous sommes partis de l'application web utilisée, et corrigée en TD (pas la correction distribuée après coups sur Mattermost, mais notre propre correction). Nous l'avons ensuite réécrite de manière modulaire, en respectant le modèle MVC, et effectué toutes les améliorations sur cette base de code. Tout est sut Gitlab : - Le site web complet est disponible dans le dossier `site-web/` - Les scripts SQL sont disponibles dans le dossier `sql/`. Ils doivent être exécutés dans cet ordre : 1. `devoir_sr03_users_create.sql` 2. `devoir_sr03_users_insert.sql ` 3. `devoir_sr03_messages_create.sql` 4. `devoir_sr03_messages_insert.sql` - Les fichiers de configuration sont dans `site-web/config`. On y trouve notamment : - `database.ini` contenant les identifiants de connexion à notre BDD - `mail.ini` contenant les identifiants de connexion au serveur mail en ligne **Mailo** - `secrets.ini` contenant le poivre utilisé dans le hachage des mots de passe - `mot_de_passe_users_en_clair.md` pour se connecter à l'application - `rapport.md`, qui est ce même rapport ## Introduction : Quand on parle de sécurité des applications, cela concerne tous les moyens mis en place pour rendre l'applicaiton résiliente aux différentes attaques possibles, qu'elles soient passives ou actives. Le terme passif renvoyant aux menaces de type : copie ou écoute d'information illégale. La sécurité est donc devenue un point essentiel de nos jours. En effet une entreprise, qu'importe sa taille, doit prendre conscience qu'elle est vulnérable à la cybercriminalité. Les conséquences d'une telle attaque peuvent être catastrophique. Si des dommages sont causés cela peut demander beaucoup de temps et d'argent pour réparer les dégâts. De plus, certaines applications peuvent utiliser les données des clients afin de les revendre sur le marché noir par exemple. Mais quelles sont les techniques à employer pour sécuriser une application ? Pour répondre à cette question, nous avons utilisé plusieurs moyens : - Un tableau récapitulant les failles corrigées, et très sommairement comment nous les avons bouchées - Une liste de bonnes pratiques pour la sécurisation d'une application, qui explique en détail les techniques employées pour protéger notre application de banque. Plus complet que le tableau précédent. - La question de synthèse effectuée sur le thème du hachage ## 1. Tableau des failles identifiées et correction Faille | Corrections mises en place ------ | -------------------------- Injections **SQL** sur les requêtes avec saisies utilisateur | Utilisation systématique de requêtes paramétrées **Authentification** | Ajout de captchas, de messages d'erreurs non explicites, et de la double authentification par jeton expirable reçu par mail **XSS** virement et message | Utilisation de `htmlspecialchars()` pour échapper les caractères malicieux Protection supplémentaire **XSS** | Utilisation de `ini_set( 'session.cookie_httponly', 1 );` et d'autrs directives pour ne traiter qu'avec un cookie de session HTTPOnly **CDS** | Vérification de la session et redirection vers page d'authentification si non connecté, déconnexion automatique après 10 minutes d'inactivité. Contrôle du mot de passe lors d'un virement. **CSRF** Virement | Ajout d'un jeton unique dans le formulaire, contrôlé lors de l'envoi **RDNS** `myController.php?action=listMessages&userid=XXX` | L'`userId` est passé en session Mots de passe, cookies de session, données de formulaire, headers, etc. transitent **en clair** | Mettre en place une connexion HTTPS et désactiver le HTTP Mot de passe stocké **en clair** dans la BDD | Hachage, salage et poivrage du mot de passe, voir la question de synthèse **Attaque aléatoire** login / mdp | Blocage après 3 tentatives d'authentification successives ratées, et utilisation de captchas Pas de contrôle des champs **envoyés** par le client | Tout les champs sont contrôlés à toutes les étapes. Des messages d'erreurs adéquats s'affichent lorsqu'un formulaire est mal rempli Pas de **contrôle de droit d'utilisateur** | Contraintes évoquées dans le sujet respectées. Des contrôles sont effectuées sur les autorisations de l'utilisateur lors du traitement des formulaires. Fichiers exposés par **HTTP** | Introduction d'un fichier .htaccess à la racine de notre site, et dans les sous-dossiers pour restreindre l'accès uniquement aux fichiers nécessaires. Directive `Options -Indexes` pour supprimer la navigation dans l'arborescence. ## 2. Liste des bonnes pratiques Dans cette partie nous allons développer comment nous avons fait pour détecter les failles et sécuriser l'application. ### Utilisation des failles connues Certaines failles sont connues pour être régulièrement utilisées lors d'attaque. Puisqu'elles sont connues, on peut également "facilement les corriger". En effet, même si on ne s'y connait pas trop on peut trouver des solutions assez rapidement sur internet, ces sujets étant très récurrement traitées en ligne. De plus les langages de programmation intègrent souvent des fonctions toutes prêtes pour corriger certaines de ces failles. Par exemple pour le hachage de mots de passe dans notre base de données (ne plus avoir les mots de passes stocker en claire) nous avons utilisé des fonctions de PHP. ( `password_hash()` ou `crypt()`) Il existe une dizaine de types de vulnérabilité que l'on retrouve régulièrement : - Injection (~40%) - Violation de gestion d’authentification - Violation de gestion de session - Cross-Site Scripting (XSS) (~30%) - Violation de contrôle d’accès - Mauvaise configuration de sécurité - Exposition de données sensibles (~15%) - Falsification de requête intersites (CSRF) - Utilisation de composants avec des vulnérabilités connues - Redirections et renvois non validés Il est donc très utile de vérifier que notre application ne comporte pas ses failles et donc de les corriger. ### Contrôle des données envoyées et reçues Les injections et XSS sont les failles les plus courantes. Elles consistent à envoyer une donnée non fiable à un interpréteur en tant qu'élément de commande. Cela permet de faire exécuter des commandes au système (injections PHP), à la base de donnée (injection SQL), etc. Un pirate peut ainsi d'accéder à des données non autorisées ou utiliser ces informations pour par exemple se connecter à l'application sans identifiants. Pour éviter ce genre de problème nous avons détecté tous les endroits ou il est demandé de rentrer du texte et/ou des valeurs par l'utilisateur. - Toutes les requêtes SQL sont paramétrées. - Lorsqu'un texte saisiepar l'utilisateur doit être affiché à l'écran, on utilise la fonction PHP `htmlspecialchars()` afin d'échapper tous les caractères HTML et Javascript. - Toutes les données saisies par l'utilisateur sont vérifiées individuellement avant d'être traitées. Un message d'erreur est renvoyé si une donnée est incorrecte #### HTTPS Il faut pouvoir protéger toutes les données échangées entre le client et le serveur. Sinon, il ne sert à rien de par exemple hacher le mot de passe, puisque le mot de passe en clair pourrait être écouté sur le réseau. De même pour les virements effectués, les messages envoyés, etc. Le HTTP (hyperText Transfer Protocol) permet cet échange mais n’importe qui peut l'écouter. Cela ne pose pas trop de problème si on est dans le cas de la lecture d’un article web. Mais dans le cas d’une application de banque en ligne comme la nôtre cela pose des soucis. C’est à ce moment que le protocole HTTPS rentre en jeu. Ce protocole utilise des algorithmes cryptographiques tels que SSL, ou son remplaçant TLS pour sécuriser les échanges entre le client et le serveur. On parle indifféremment de certificat SSL ou TLS, mais il faut savoir que le protocole SSL n’est plus d’actualité depuis qu’il a été remplacé par le TLS, une version plus sûre basée sur le même principe. Ce certificat électronique s’applique au site pour sécuriser les échanges de données en assurant leur chiffrement à l’aide d’une clé de cryptage asymétrique. Un site protégé par un certificat SSL (ou TLS) affiche le fameux cadenas prouvant que la communication est sécurisée. Il existe différents types de certificats: - Le certificat SSL gratuit (type Let’s Encrypt) - Le certificat à validation étendue (Extended SSL) - Le certificat à validation de l’organisation (Organization SSL) - Le certificat à validation de domaine (Domain SSL) - Le certificat multi-domaine (WildCard) Ces certificats peuvent être gratuits, comme payant, et sont délivrés par des organismes spécifiques. Dans notre projet, nous n'avons pas implémenté le HTTPS car il serait difficile de déployer un certificat, et que vous puissiez ensuite l'utiliser à travers notre dépot gitlab. Cependant, il serait absolument crucial de mettre un place un tel certificat afin de sécuriser tous les échanges entre un client, et le serveur d'application dans un contexte de production. Dans le cas contraire, de nombreuses autres corrections de failles n'auraient aucun effet. ### Gestion des mots de passe Certaines données sensibles comme les mots de passe ont besoin d'être stockés pour l'identification. Si l'on stocke ces informations dans une base de données en clair, alors n'importe qui accèdant à cette base de données (hacker, hôte de la base de données ou propriétaire de l'application) peut directement visualiser les mots de passe. Pour empêcher cela et protéger les utilisateurs, nous avons utilisé le hachage (avec ARGON2_ID), avec sel et poivre. Cette technique permet de créer une empreinte numérique à partir du mot de passe et donc de stocker dans la base de données uniquement cette empreinte. Le processus inverse n'est théoriquement pas faisable. Plus de détails à propos de ce procédé sont expliqués à la fin du rapport, dans la question de synthèse. Pour notre application, le poivre se trouver dans le fichier `/config/secrets.ini`. On peut générer un mot de passe salé et poivré directement grâce à la fonction ` getHashedSaltedPepperedPassword` du fichier /model/securityModel.php : ``` PHP function getHashedSaltedPepperedPassword(string $plainPassword) { $pwd_peppered = hash_hmac('sha256', $plainPassword, getPepper()); return password_hash($pwd_peppered, PASSWORD_ARGON2ID); } ``` La vérification du mot de passe est assurée dans notre application par `verifyHashedSaltedPepperedPassword(string $plainPassword, string $hashedPassword)`. Cette fonction permet de vérifier qu'un mot de passe en clair est bien le même que la version hachée, salée, et poivrée du mot de passe. On peut ainsi comparer le mot de passe de l'utilisateur saisi en clair lors de l'authentification, avec le mot de passe haché, salé, et poivré stocké en BDD. ### Gestion de sessions Pour éviter que des personnes mal intentionnées puissent utiliser la session d'un autre utilisateur sans autorisation, nous avons ajouter l'expiration automatique de la session. Cela peut être utile dans plusieurs cas. Par exemple, si vous quittez votre poste pour la pause de midi sans verrouiller votre ordinateur. Quelqu'un peut alors tenter d'utiliser votre session ouverte sur votre ordinateur. Seulement, votre session aura expirée automatiquement, après 10 minutes d'inactivité sur le site. L'utilisateur ne pourra donc rien faire s'il arrive 10 minutes après que vous soyez parti, car vous serez déconnecté automatiquement. Au niveau technique, il suffit d'ajouter, en haut d'une page à protéger, l'inclusion du fichier suivant : ``` php require __DIR__ . '/utils/automaticLogoutHandler.php'; ``` Le fonctionnement est simple : à chaque requête sur la page, on compare la dernière date d'accès (enregistrée en session) avec l'heure actuelle. Si l'intervalle de temps est trop grand, alors on appelle le controlleur de déconnexion qui se charge de déconnecter l'utilisateur (sans redirection, donc sans détournement possible pour l'utilisateur). La durée d'expiraiton de la session est à étudier au cas par cas mais pour des applications sensibles comme notre banque en ligne il est largement préférable de mettre un intervalle de temps court, et de laisser l’utilisateur se reconnecter plutôt que de laisser de longues sessions. On retrouve dans le fichier `/controller/utils/automaticLogoutHandler` : ``` PHP if(isset($_SESSION["lastConnectionTimeStamp"])) { if ((strtotime("now") - $_SESSION["lastConnectionTimeStamp"]) > 600) { $automaticDisconnection = TRUE; require __DIR__ . '/../disconnectionController.php'; exit(); } } ``` La gestion de session concerne également les fonctions importantes de l'application, comme les virements par exemple. En effet, il faut pouvoir empêcher l'exécution d'une requête CRSF par un client dupée. Pour pallier à ce problème on peut utiliser des jetons. Ce « jeton » est placé de manière cachée à chaque demande du formulaire. Le serveur ne traitera le formulaire que si le jeton reçu est bien le même qu'il a émis. De ce fait, on évite les attaques CSRF, car la valeur du jeton n'est pas prédictible. Nous avons choisi de protéger la fonction **Virement** avec un jeton pour éviter les attaques CSRF sur cette dernière. Une deuxième manière est de redemander le mot de passe de l'utilisateur pour être certain que le formulaire ait bien été envoyé intentionnellement par ce dernier. La fonction **Virement** est également protégée de cette manière. ### Authentification rigide + 2FA Nous voulions atteindre plusieurs buts pour notre authentification : - Pas d'injections SQL possible (voir au-dessus) - Impossible de faire une attaque de force brute avec login / mot de passe - Impossible de savoir si un utilisateur existe ou non - Second facteur d'authentification obligatoire, avec expiration après de 5 minutes - Blocage automatique après 3 tentatives d'authentifications successivement ratées Avec ce cahier des charges, l'authentification est la partie la plus avancée de notre projet en terme de sécurité. L'authentification se fait en plusieurs étapes : 1. Rentrer son login et son mot de passe 2. Faire le captcha 3. Si le captcha est invalide, retour à 1. Sinon, saisir le jeton reçu par mail (mail = login) 5. Faire le captcha 6. Si le captcha est invalide, retour à 1. Sinon, si le login, le mot de passe, et le jeton sont valides, alors l'utilisateur est connecté. Sinon rediriger sur la page d'authenfification **/!\ Pour plus de facilité pédagogique, le jeton 2FA est également affiché dans la console PHP afin que vous puissiez facilement vous connecter. Ce ne serait naturellement pas le cas en production** #### Protections anti force brute Pour éviter que quelqu'un ne puisse spammer les tentatives d'authentifications dans le but de trouver le mot de passe et le login de quelqu'un, nous avons ajouté deux couches de sécurité : - Un compteur de connexion successive ratée est implémentée, avec blocage après 3 tentatives. Lorsque l'utilisateur tente de se connecter, il doit alors attendre 1 minute après la dernière tentative ratée(le compteur de temps restant est affiché sur la page). Ce compteur fonctionne grâce aux données enregistrées en session, il est donc possible de l'outrepasser en supprimant son cookie de session à chaque tentative. Cependant, cela devrait suffire pour bloquer la majorité des utilisateurs lambdas. Mais pour les pirates, cela ne fonctionnerait pas. - Nous avons donc ajouté des captchas obligatoires (fournis par hCaptcha) lors de la saisie du login / mot de passe, et lors de la saisie du jeton reçu par mail. Ces jetons sont vérifiées avant toute autre saisie. Cela enlève tout espoir de brute-force automatisée (même si des services de résolution de captcha existent, la résolution prend toujours environ 10s par captcha avec ce genre de service) - Même si le login saisi n'existe pas, la page demandant de rentrer le jeton reçu par mail s'affiche tout de même. Le pirate ne peut donc pas savoir si le login qu'il a saisi est réellement celui d'un utilisateur. Si le login n'existe pas, le jeton 2FA n'est pas généré et le pirate ne peut donc pas continuer l'authentification, même si la page s'affiche. - Dans le cas ou le les captchas sont corrects, et le jeton 2FA est correct, mais que le mot de passe est incorrect, le message "Login ou mot de passe incorrect" s'affiche. Encore une fois, un pirate ne pourrait donc pas savoir si le login qu'il a saisi existe réellement en BDD. #### Le Captcha Le captcha utilise le principe du test de Turing mais inversé. Dans le test de turing on met en relation un humain d’un côté et de l’autre soit un autre humain soit un ordinateur. Si la personne n’arrive pas à savoir si elle parle à un humain ou non le test est validé. Dans notre cas le Captcha va justement essayer de savoir s’il a en face de lui un humain ou non. Il existe différents types de captcha. Le captcha à deux mots, où on demande à l’utilisateur de rentrer les deux mots à l’écran. A noter qu’un seul des deux mots est connu le deuxième est inconnu. Une fois qu’un certain nombre d’utilisateur ont répondu la même chose pour ce deuxième mot il est alors ajouté au dictionnaire. Deuxièmement nous avons un captcha développé par google « je ne suis pas un robot » ou il suffit de cliquer sur une case. Il en existe aussi ou il faut identifier des images, reconstituer des puzzle etc… Pour notre application nous utilisons un captcha par identification d’image fournis par hCaptcha. Nous l'avons choisi car contrairement à reCaptcha, il respecte la confidentialité des utilisateurs. Pour utiliser ce captcha il faut en premier inclure la balise : ``` PHP <script src="https://hcaptcha.com/1/api.js" async defer></script> ``` La vérification du captcha se fait dans la fonction `verifyCaptcha( string $captchaReponse)` du fichier `/model/securityModel.php`. ``` PHP function verifyCaptcha(string $captchaResponse) { $data = array( 'secret' => "0x2Db9AB2177D8a54046872896804A013116C76BEb", 'response' => $captchaResponse ); $verify = curl_init(); curl_setopt($verify, CURLOPT_URL, "https://hcaptcha.com/siteverify"); curl_setopt($verify, CURLOPT_POST, true); curl_setopt($verify, CURLOPT_POSTFIELDS, http_build_query($data)); curl_setopt($verify, CURLOPT_RETURNTRANSFER, true); $response = curl_exec($verify); $responseKeys = json_decode($response, true); if ($responseKeys["success"]) { return TRUE; } else { return FALSE; } } ``` #### Le mail contenant le jeton Pour l'envoi du mail contenant le jeton d'authentification, nous avons utilisé la librairie `PHPMailer`, qui permet de se connecter en SMTP à n'importe quel serveur local ou distant. Nous avons créé une adresse mail `sr03@mailo.com`, hébergé chez mailo. A chaque tentative de connexion, les méthodes contenues dans notre modèle `mailModel.php` permettent de se connecter au serveur SMTP de manière sécurisée, et d'envoyer le mail avec le jeton d'authentification unique. Ce mail n'est bien sûr envoyé que si le login saisi correspond bien à un utilisateur enregistré. Le jeton expire au bout de 5 minutes. Voici un exemple du mail : ![](https://i.imgur.com/B1af7HD.png) ### Séparer les différents usagers Une fois connectés il est important de pouvoir distinguer les utilisateurs pour savoir qui a accès à quoi. En effet il ne faudrait pas qu’un utilisateur classique puisse consulter le profil d’autres utilisateurs dans le cas de notre application. On distingue 2 types d'usagers : les clients, et les employés. Il faut donc penser à mettre des vérifications dans le code. Toutes les actions effectuées par le client sont contrôlées côté serveur. Si l'utilisateur n'a pas l'autorisation nécessaire (par exemple un client qui fait un virement depuis le compte d'un autre), alors un message d'erreur s'affiche. Un point d'importance a été mis sur les différents messages permettant d'avertir l'utilisateur du résultat de ses actions. Toutes les contraintes d'autorisations spécifiées dans le sujet ont été implémentées. ### Protection des cookies Pour rappel, un cookie est un petit ensemble de données que l'on stocke côté client. Il existe des cookie de session. Par défaut, ils sont supprimées lorsque l'on ferme son navigateur. Il existe aussi des cookies permanents qui vont perdurer même une fois le navigateur fermé. Ces cookies sont sensible aux attaques de type XSS, et aux aspirateurs à cookies. Il existe un moyen assez simple de s'en protéger. En effet on peut utiliser la directive `HTTPOnly`. Cela va rendre les cookies inaccessible aux scripts et vont être envoyés uniquement au serveur. Quand à `session.use_only_cookies` elle va interdir les cookies de session via l'url. On évite également l'affichage des erreurs serveurs dans les pages envoyées aux clients avec `display_errors` à 0 : ``` PHP ini_set('display_errors', '0'); ini_set('session.cookie_httponly', '1'); ini_set('session.use_only_cookies', '1'); ``` Ces trois lignes sont placées avant chaque démarrage de session : `session_start()`. Il n'est pas nécessaire de les inclure si elles sont déja présentes dans le fichier `php.ini` ### Protection des répertoires Il ne faut pas qu'un pirate puisse accéder à l'arborescence de notre programe. Il pourrait alors consulter des pages qui ne lui sont pas destinées, découvrir l'architecture de l'application, et lire des fichiers de configuration cruciaux. De plus il faut pouvoir protéger ces fichiers et n'autoriser les requêtes demandant qu'un ensemble de ressources précises. Pour ce faire il faut créer un, ou plusieurs fichiers **.htaccess**, qui permettent d'autoriser ou de refuser l'accès à certaines ressources. Ce n'est pas nécessaire si la configuration est réalisée dans `php.ini` : ``` hacess Options -Indexes Require all denied ``` Ces quelques lignes dans le fichier vont rendre l'accès impossible à tous les fichiers pour tous les utilisateur. L'arborescence est également désactivé. Si quelqu'un requête un des fichiers du dossier il obtiendrait: ![](https://i.imgur.com/ds0FSxb.png) Mais ce fichier peut aussi permettre d'effectuer des redirection en cas d'erreur, ou seulement certains utilisateur d'avoir accès à certains fichier. ``` hacess ErrorDocument 404 /index.php ErrorDocument 404 http://google.fr ErrorDocument 410 /index.php ErrorDocument 410 http://google.fr ``` Permet de rediriger l'utilisateur vers une page google si il optient une erreur 404/410. On retrouve souvent la partie admin d'une application en ajoutant admin à la fin de l'URL. Ex `http://localhost/sr03-td-securite/site-web/admin`. Il est donc important de protéger ces fichier car une personne s'y connaissant un peu aura vite fait de les trouver. Dans notre projet, nous n'avons autorisé que les requêtes vers : - `index.php` - `controller/*` ## Pour aller plus loin Nous avons ajouté deux autres bonnes pratiques mais que nous n'avons pas utiliser. En effet notre application étant en place juste le temps du projet il est inutile de mettre à jour (déjà a jour) de même pour les sauvegardes. ### Bien configurer et masquer les composants de l'application On retrouve dans chaque système d'exploitation des vulnérabilités. Une fois détectés, elles sont souvent rapidement corrigées par l'éditeur par d'une mise à jour. Une fois que la vulnérabilité est connue, n'importe quel cybercriminel peut l'utiliser. Cela peut poser problème si le logiciel utilisé n'est pas à jour. C’est pour cela qu’il est aussi important de cacher à l’utilisateur la version utilisée. Si il ne connaît pas la version du serveur web par exemple, il sera plus difficile pour lui de trouver les failles. Encore mieux si le nom du serveur web / du langage de programmation est masqué. Pour notre application, nous avons modifié la configuration Apache pour désactiver la version dans les headers, grâce à : ``` txt # Désactive les headears de apache ServerSignature Off ServerTokens Prod ``` Et supprimé le header PHP contenant PHP + la version avec : ``` expose_php = Off ``` Vous ne pourrez cependant pas le voir car cette configuration dépend de votre installation de Apache et de PHP. Pour aller plus loin et masquer totalement le fait que nous utilisons PHP, nous aurions pu changer le nom du cookie `PHPSESSID` mais nous avons considéré que les modifications précédentes étaients déjà suffisantes. Pour finir il peut être très utile de désactiver les fonctionnalités (modules Apache + PHP, phpMyAdmin) non utilisées, afin de réduire la surface d'attaque. ### Effectuer des sauvegardes régulières Une attaque peut avoir pour but d'endommager un système informatique. Il est donc très utile d'avoir des sauvegardes régulières. De nombreuses solution existent (disque dur, cloud). Mais la plus sur reste encore l'utilisation de Git. Pour finir il peut aussi être intéressant pour les sauvegardes de Base de données par exemple de les crypter. Réduisant ainsi les risques au cas ou ces sauvegardes seraient exposées. ## Organisation du code Deuxièmement il faut être rigoureux dans la création de l'application. Si l'on travaille sur un même projet à plusieurs, une mauvaise gestion en plus d'entrainer des difficultés à coder entrainera des difficultés dans la sécurisation. Il est beaucoup plus difficile de détecter ou de corriger des failles si le code est incompréhensible. En outre, sur le long terme, le créateur ne serez pas forcément toujours celui qui maintiendra le code en place. Il faut donc faire quelque chose de propre dès le départ. . Il est donc nécessaire de **bien gérer ses composant**, et d'utiliser par exemple **git** pour la gestion du code source. ### Programmation modulaire et MVC Répartir en module est très intéressant à plusieurs niveaux. Au-delà du fait qu'elle permet un développement facilité (chaque personne peut travailler sur un module), une taille réduite du code source et une meilleure visibilité. Ce qui nous intéresse surtout c'est une meilleure structuration et une meilleure maintenabilité du programme. On doit plus gérer un code dans son ensemble mais des composants. L’application de banque en ligne a été entièrement réécrité de manière modulaire, en utilisant un modèle `MVC` le plus possible. L'arborescence du projet respecte ce modèle avec `model/`, `controller/` et `view/`. Chaque fonction (écrire un message, virement, fiche client, etc.) a été implémenté au sein d'un module. Un module peut ensuite être placé au sein d'une page web pour être utilisé. Page web et modules sont donc décorrélés. Cela nous a permis de garder exactement le même front-end sur l'application de banque en ligne. Toutes les fonctions principales (hors messagerie) se retrouvent sur la page d'accueil, et non dans des pages séparées. Toutes les actions des utilisateurs connectées sont traitées par le controlleur `accountController.php`. Les modules sont placés dans `/view/module/`, et le fichier permettant de les gérer est `/view/moduleManager.php`. La programmation modulaire permet également de placer un même module dans plusieurs pages web différentes. Nous avons pu placer le module d'écriture de message dans la page d'accueil, et dans la page "Messagerie", sans duplication de code. ## 3. Question de synthèse : une technique de sécurité web, le hachage Il est dangereux de stocker les mots de passes en clair dans une base de données. En effet une fois qu'un utilisateur (autorisé ou non) a accès à cette base, il peut accéder à tous les comptes utilisateurs car il peut voir leur mots de passe. Pour contrecarrer ce problème on utilise une fonction de hachage sur le mot de passe. ### En quoi consiste le hachage ? En informatique, une fonction de hachage prend des blocs de données (ou un flux) en entrée et en produit une empreinte unique de taille fixe (assez courte, usuellement entre 128 et 512 bits en fonction de l'algorithme employé). Ce qui en fait tout son intérêt est sa rapidité de calcul (ou sa lenteur), et qu'il est théoriquement impossible de retrouver le contenu à l'origine de l'empreinte. Un même contenu produira toujours exactement le même hash, mais une infime variation produira un hash totalement différent. ### Comment hasher un mot de passe ? #### Niveau 1: Hash simple Le premier niveau de hachage est assez simple. On peut utiliser des algorithmes tels que `sha256` pour retourner notre mot de passe haché. Il faut veiller à ne pas utiliser d'algorithmes obsolètes tels que `md5` ou `sha1`. Ensuite il suffit de ne stocker que ce hash en BDD. Avec ce hachage, on limite vaguement le risque mais on ne l'élimine pas du tout. En effet, si plusieurs utilisateurs ont des mots de passes similaires, le hash sera le même. De plus, un pirate peut utiliser une rainbow table (table de correspondance 'chaine' -> hash) afin de retrouver les mots de passes les plus communs en une fraction de seconde. Il existe en ligne de nombreux dictionnaires contenant les listes de mots de passe les plus utilisés(rockyou par exemple), qui s'avèrent également redoutables contre un simple hachage du mot de passe. #### Niveau 2: Hash (choisi) + Salt Pour contrer ce processus d'attaque par rainbow table, un deuxième niveau de hachage a été trouvé. On ajoute un salt unique, créé aléatoirement, à notre mot de passe avant de le hasher. Ce sel est stocké à côté du mot de passe en base de données. Enfin, deux mots de passe semblables auront un hash différents puisque le sel ajouté est différent. Cela rend obsolète l'attaque par rainbow table, car il faudrait recalculer toute la table pour chaque sel, afin de pouvoir réaliser l'attaque. De plus, on peut utiliser des algorithmes de hachage spécialement prévus pour être lents à calculer (bcrypt, argon2_id, ...). Grâce à ces algorithmes, le calcul d'un hash relève de la dizaine ou de la centaine de milisecondes, ce qui réduit amplement l'efficacité d'une attaque par force brute, et par dictionnaire. Cependant, les mots de passes triviaux, comme "1234" (disponibles dans notre BDD du projet) peuvent toujours être trouvés relativement rapidement avec des attaques par dictionnaire bien choisis. En PHP, on peut utiliser les fonctions : `crypt()` et `password_hash()`. La valeur retournée inclura automatiquement le salt comme partie intégrante du hash. Il suffit ensuite de stocker la valeur retournée en BDD. Pour la vérification du mot de passe on peut alors utiliser les fonctions `crypt()` et `password_verify()`, afin de vérifier automatiquement qu'un mot de passe en clair correspond bien à un même mot de passe salé et haché. Retour de la fonction password_hash() : ![](https://i.imgur.com/0SIwwZ4.png) #### Niveau 3: Hash + Salt + Pepper = Tartare Les mots de passe très faibles protégés par le niveau précédent restent sensibles aux attaques par dictionnaire. On peut rendre ce système encore plus résistant, en concaténant une autre clé (en plus du sel) avant hachage, appelée pepper. Cette clé est privée, ne doit pas être stocké dans la BDD, et doit être la même pour tous les mots de passe. Cette clé souvent appelé pepper rend l'attaque par dictionnaire inutilisable, car même si le mot de passe de l'utilisateur est très faible, le pepper étant lui-même très fort, l'attaque ne pourra pas réussir. Il faudrait tout d'abord que le pirate réussisse à trouver le pepper. Ainsi, même les mots de passe les plus faibles sont protégés. Il faut cependant que le pepper soit lui-même fort, car sinon il est inutile et sera trouvée lors d'attaques par dictionnaire. Le pepper doit cependant être gardée secrètement et ne doit pas être divulgé, auquel cas il devient inutile. S'il est perdu, toutes les données deviennent inutilisables. Il y a donc une grande responsabilité quant à la gestion de ce pepper. Pour le projet nous avons choisi de hacher, saler, et poivrer les mots de passe avant stockage, avec l'algorithme `sha256` pour l'ajout du poivre, et `ARGON2_ID` pour le salage et hachage. Voir `model/securityModel.php` Vous pourrez trouver, à but pédagogique, un fichier avec les mots de passes en clair (`mot_de_passe_users_en_clair.md `) de chaque utilisateur, vous permettant ainsi de vous connecter à la base de données. La page de création de compte n'étant pas demandée, les utilisateurs sont rentrés en dur dans la base de données grầce à un script SQL.
{"metaMigratedAt":"2023-06-15T07:10:13.424Z","metaMigratedFrom":"Content","title":"Projet 2 SR03: Application de banque en ligne sécurisée","breaks":true,"contributors":"[{\"id\":\"38b40c6e-3b36-420f-a10c-a202c7748faa\",\"add\":117,\"del\":40765},{\"id\":\"55f902e7-d38f-412d-99f5-ccbe12047a0b\",\"add\":22421,\"del\":15360},{\"id\":\"9df6d6e7-6243-4f67-a003-011fca91f34c\",\"add\":46971,\"del\":44544}]"}
Expand menu