Blog ENI : Toute la veille numérique !
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici
💥 Les 22 & 23 novembre : Accès 100% GRATUIT
à la Bibliothèque Numérique ENI. Je m'inscris !

Tests de sécurité avancés avec Cypress

Tests d’authentification

Les tests d’authentification visent à s’assurer que les mécanismes d’authentification de votre application fonctionnent correctement et que seuls les utilisateurs autorisés ont accès aux fonctionnalités et données sensibles.

Les tests d’authentification vérifient que :

  • les utilisateurs peuvent se connecter avec des identifiants valides ;

  • les utilisateurs ne peuvent pas se connecter avec des identifiants invalides ;

  • les sessions des utilisateurs sont correctement gérées après la connexion (par exemple, expiration de session, déconnexion) ;

  • les utilisateurs ont accès aux fonctionnalités qui leur sont autorisées et sont restreints des autres.

1. Tester l’authentification à deux facteurs

L’authentification à deux facteurs (2FA) renforce considérablement la sécurité des applications web. En plus de la traditionnelle paire nom d’utilisateur et mot de passe, elle exige une preuve supplémentaire d’identité, apportant ainsi une protection renforcée contre les accès non autorisés. Cette vérification secondaire peut se matérialiser sous différentes formes, augmentant la barrière pour les acteurs malveillants.

Parmi les techniques les plus répandues, on trouve l’envoi d’un code de vérification par e-mail. Cette méthode utilise l’adresse électronique de l’utilisateur comme un canal secondaire pour transmettre un code temporaire, qu’il doit ensuite saisir sur le site web pour confirmer son identité. Bien que pratique, cette approche repose sur la sécurité de la boîte mail de l’utilisateur, qui doit être protégée par un mot de passe robuste et, idéalement, par sa propre 2FA.

L’envoi de SMS est une autre méthode couramment employée. Un code de vérification est transmis au numéro de téléphone mobile de l’utilisateur, offrant un moyen relativement simple et direct de confirmer son identité. Cette stratégie bénéficie de la commodité et de la portabilité des téléphones mobiles, mais elle n’est pas sans faille, notamment en raison des risques de détournement de...

Tests de sécurité des API

Les API (Application Programming Interfaces) permettent à différents systèmes, applications, et services de communiquer entre eux. Elles facilitent l’échange de données et la connexion de fonctionnalités, rendant possible l’intégration et l’interaction entre des plateformes diverses.

Une API définit les règles que les développeurs doivent suivre pour interagir avec un service, une application ou un système, incluant les requêtes à envoyer, le format des données à utiliser, et la structure des réponses attendues. Elles peuvent être basées sur différentes technologies et standards, y compris REST (REpresentational State Transfer), SOAP (Simple Object Access Protocol), et GraphQL.

Les API sont omniprésentes dans le développement web, mobile, et de logiciels, servant de ponts pour accéder à des bases de données, des services tiers, des fonctionnalités de cloud, et plus encore. Elles permettent aux développeurs de construire des applications riches et fonctionnelles en s’appuyant sur des services existants.

En combinant les tests d’interface utilisateur et les tests d’API au sein de Cypress, vous pouvez créer une suite de tests complète qui couvre à la fois le frontend et le backend de votre application. Ceci est particulièrement utile pour les tests E2E, où vous voulez vérifier l’intégration complète de tous les composants de votre application.

De plus, tester régulièrement l’API tout au long de son développement et après son déploiement peut simplifier la maintenance et l’ajout de nouvelles fonctionnalités. Cela permet d’identifier les problèmes tôt et réduit le coût et l’effort nécessaires pour les résoudre.

Pour notre projet d’exemple, nous avons créé un swagger qui est accessible à l’adresse http://localhost:3000/api-docs/

images/09RI05.png

Swagger est un langage de description d’interface permettant de décrire des API exprimées à l’aide de JSON. Vous pouvez retrouver le fichier swagger.json à...

Stubs, Spies and Clocks

Stubs, Spies et Clocks sont des commandes utiles lors de l’écriture de tests unitaires et de tests d’intégration autant pour les tests front-end et back-end que pour API.

1. Stubs

Un stub est un moyen de modifier une fonction et de vous déléguer le contrôle de son comportement. Vous supprimez généralement une fonction lorsqu’elle a des effets secondaires que vous essayez de contrôler.

Un stub est le plus souvent utilisé dans un test unitaire mais est toujours utile lors de certains tests d’intégration ou E2E.

Scénarios courants

  • Vous disposez d’une fonction qui accepte un rappel et souhaitez appeler le rappel.

  • Votre fonction renvoie un Promise et vous souhaitez le résoudre ou le rejeter automatiquement.

Une promesse (Promise) est un objet qui représente l’état d’une opération asynchrone et vous permet de définir à l’avance ce que vous allez faire lorsqu’elle est terminée (en succès ou non).

  • Vous disposez d’une fonction qui encapsule window.location et vous ne souhaitez pas que votre application soit parcourue.

  • Vous essayez de tester le « chemin d’échec » de votre application en forçant les choses à échouer.

  • Vous essayez de tester le « chemin heureux » de votre application en forçant les choses à passer.

  • Vous souhaitez « tromper » votre application en lui faisant croire qu’elle est connectée ou déconnectée.

  • Vous utilisez oauth et souhaitez remplacer les méthodes de connexion....

Session

La méthode cy.session() permet de simuler des états de session utilisateur pour tester différents scénarios dans une application, comme les états connecté et déconnecté, sans avoir à se reconnecter manuellement à chaque test. C’est particulièrement utile pour les applications nécessitant une authentification, car cela peut considérablement réduire le temps d’exécution des tests en évitant les opérations répétitives de connexion.

Prenons comme exemple notre application qui implique un système de commentaires avec authentification. Vous avez des endpoints comme /login pour la connexion et /post-comment pour poster un commentaire. L’utilisateur doit être connecté pour accéder à ces deux endpoints.

 Dans le fichier login.cy.js, vous pouvez ajouter ce test. Commencez par créer une session utilisateur authentifiée en utilisant cy.session(). Dans cette session, effectuez une requête POST à votre endpoint de connexion pour simuler une connexion utilisateur :

describe('Authenticated Session Tests', () => {  
  beforeEach(() => {  
    cy.session('userSession', () => {  
      cy.request('POST', '/login', { username: 'user1', 
password: 'pass1' })  
        .then((resp) => {  
          expect(resp.body).to.include('Connection OK');  
          // Stockez ici le cookie de session ou le token retourné 
par votre API si nécessaire  
        });  
    });  
  });  
}); 

Mais comme nous avons un cookie CSRF, vous devez l’ajouter :

  beforeEach(() => {  
    // Se connecte et récupère le token CSRF  
    cy.visit('http://localhost:3000/login');  
    cy.getCookie('_csrf').then((cookie) => {  
      const csrfToken = cookie && cookie.value; // Vérifie si le 
cookie...

Test isolation

L’objectif de l’isolation des tests est de s’assurer que chaque test puisse être exécuté indépendamment des autres tests, garantissant ainsi que le résultat d’un test n’affecte pas les résultats des autres tests. Cela aide à maintenir la fiabilité, la prévisibilité et la facilité de débogage des tests.

Fiabilité : l’isolation garantit que les tests ne dépendent pas de l’état laissé par d’autres tests. Cela signifie que vous pouvez exécuter un test spécifique, ou un groupe de tests, dans n’importe quel ordre sans que cela n’affecte les résultats.

Débogage Facilité : quand un test échoue, vous savez que l’échec est dû à la logique au sein du test lui-même ou à la fonctionnalité qu’il teste, et non à un effet secondaire d’un autre test.

Performance : en s’assurant que les tests sont isolés, vous pouvez paralléliser l’exécution des tests, réduisant ainsi le temps total nécessaire pour exécuter l’ensemble de la suite de tests.

Pour cela, vous devez vous assurer d’isoler vos tests :

 Avant chaque test, réinitialisez l’état de l’environnement de test à un état connu. Cela inclut la base de données, le système de fichiers, les mocks/stubs utilisés, et tout autre état global ou configuration qui pourrait affecter le test.

 Utilisez les hooks (before et/ou beforeEach) pour nettoyer ou réinitialiser l’état de l’application avant et après chaque test. Par exemple, en nettoyant la base de données ou en réinitialisant les sessions utilisateurs.

 Remplacez les dépendances externes (comme les appels réseau, les bases de données, etc.) par des mocks ou des stubs pour simuler leur comportement. Cela permet de s’assurer que les tests ne sont pas affectés par l’état ou le comportement de ces dépendances.

 Ecrivez chaque test pour qu’il soit autosuffisant et indépendant des autres. Évitez les dépendances entre les tests, comme l’exécution d’un test qui nécessite qu’un autre test...