Nouveau : Datasets open source gratuits disponibles !Decouvrir →
⚛️
Intermediaire 25 min React

Securiser une application React

Pourquoi Securiser une application React ?

Dans un monde où les menaces cybernétiques continuent à évoluer, la sécurité est devenue une priorité absolue pour toutes les applications, y compris celles développées en React. Un développement sécurisé n'est pas seulement une question d'éviter les pires scénarios, mais aussi de fournir une expérience utilisateur fiable et sécurisée.

Un cas concret de la nécessité de sécuriser une application React est lorsqu'un utilisateur entre des informations sensibles comme son mot de passe ou ses coordonnées bancaires. Si ces données ne sont pas correctement protégées, elles pourraient être volées et utilisées par des individus malveillants.

Prerequis

  • Connaissances en JavaScript ES6+
  • Familiarité avec React
  • Connaissance de l'architecture du projet Create React App
  • Installation de Node.js (v14 ou plus tard)
  • Installation de npm ou yarn pour la gestion des dépendances
  • Connaissance de bases de l'authentification et de la gestion des sessions

Concepts fondamentaux

1. Authentification et Autorisation

L'authentification est le processus par lequel une application vérifie l'identité d'un utilisateur. L'autorisation, quant à elle, détermine les actions que cet utilisateur peut effectuer dans l'application.

Schéma mental :

+-------------------+
|  Authentification |
|                   |
|   Identifiant     |
|   Mot de passe    |
|                   |
+-------------------+
          |
          v
+-------------------+
|  Autorisation     |
|                   |
|   Permissions     |
|   Roles           |
|                   |
+-------------------+

Code fonctionnel :

// Auth.js

import React, { useState } from 'react';

const Auth = () => {
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');

  const handleSubmit = async (e) => {
    e.preventDefault();
    // Simuler une requête de connexion à un serveur
    if (username === 'admin' && password === 'password') {
      localStorage.setItem('user', JSON.stringify({ username }));
      alert('Connexion réussie');
    } else {
      alert('Identifiants incorrects');
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        placeholder="Username"
        value={username}
        onChange={(e) => setUsername(e.target.value)}
      />
      <input
        type="password"
        placeholder="Password"
        value={password}
        onChange={(e) => setPassword(e.target.value)}
      />
      <button type="submit">Se connecter</button>
    </form>
  );
};

export default Auth;

2. Gestion des Sessions

Une session utilisateur est une période durant laquelle un utilisateur est authentifié dans une application. Elle permet de garder les informations sur l'utilisateur entre les différentes pages.

Schéma mental :

+-------------------+
|  Session          |
|                   |
|   Token           |
|   Expiration      |
|                   |
+-------------------+

Code fonctionnel :

// App.js

import React, { useEffect } from 'react';
import Auth from './Auth';

const App = () => {
  const [user, setUser] = useState(null);

  useEffect(() => {
    const storedUser = localStorage.getItem('user');
    if (storedUser) {
      setUser(JSON.parse(storedUser));
    }
  }, []);

  return (
    <div>
      {user ? (
        <div>
          <h1>Bienvenue, {user.username}</h1>
          <button onClick={() => localStorage.removeItem('user')}>Se déconnecter</button>
        </div>
      ) : (
        <Auth setUser={setUser} />
      )}
    </div>
  );
};

export default App;

3. Protection contre les Cross-Site Scripting (XSS)

L'XSS est une attaque où un attaquant injecte du code malveillant dans une page web qui est ensuite exécutée par d'autres utilisateurs.

Schéma mental :

+-------------------+
|  XSS              |
|                   |
|   Injection       |
|   Affichage       |
|   Exécution         |
|                   |
+-------------------+

Code fonctionnel :

// Commentaires.js

import React from 'react';

const Comment = ({ text }) => {
  return <p>{text}</p>; // XSS vulnérable
};

const CommentsList = () => {
  const comments = [
    { id: 1, text: '<script>alert("XSS")</script>' },
    { id: 2, text: 'Ceci est un commentaire' }
  ];

  return (
    <div>
      {comments.map((comment) => (
        <Comment key={comment.id} text={comment.text} />
      ))}
    </div>
  );
};

export default CommentsList;

Correction :

// Commentaires.js

import React from 'react';

const Comment = ({ text }) => {
  return <p dangerouslySetInnerHTML=__html: text />; // Sécurisé contre XSS
};

const CommentsList = () => {
  const comments = [
    { id: 1, text: '<script>alert("XSS")</script>' },
    { id: 2, text: 'Ceci est un commentaire' }
  ];

  return (
    <div>
      {comments.map((comment) => (
        <Comment key={comment.id} text={comment.text} />
      ))}
    </div>
  );
};

export default CommentsList;

Mise en pratique : projet fil rouge

Nous allons construire un mini-projet complet et réaliste : un gestionnaire de tâches simple. Ce projet comprendra une authentification, des tâches à ajouter et à supprimer, ainsi qu'une liste des tâches.

Étape 1 : Initialisation du projet

npx create-react-app todo-manager
cd todo-manager
npm start

Étape 2 : Authentification

Créez un composant Auth.js pour gérer l'authentification :

// src/Auth.js

import React, { useState } from 'react';
import axios from 'axios';

const Auth = ({ setUser }) => {
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');

  const handleSubmit = async (e) => {
    e.preventDefault();
    try {
      const response = await axios.post('http://localhost:5000/login', { username, password });
      localStorage.setItem('token', response.data.token);
      setUser({ username });
    } catch (error) {
      alert('Identifiants incorrects');
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        placeholder="Username"
        value={username}
        onChange={(e) => setUsername(e.target.value)}
      />
      <input
        type="password"
        placeholder="Password"
        value={password}
        onChange={(e) => setPassword(e.target.value)}
      />
      <button type="submit">Se connecter</button>
    </form>
  );
};

export default Auth;

Étape 3 : Gestion des tâches

Créez un composant TaskManager.js pour gérer les tâches :

// src/TaskManager.js

import React, { useState, useEffect } from 'react';
import axios from 'axios';

const TaskManager = ({ user }) => {
  const [tasks, setTasks] = useState([]);
  const [newTask, setNewTask] = useState('');

  useEffect(() => {
    if (user) {
      fetchTasks();
    }
  }, [user]);

  const fetchTasks = async () => {
    try {
      const response = await axios.get('http://localhost:5000/tasks', {
        headers: { Authorization: `Bearer ${localStorage.getItem('token')}` }
      });
      setTasks(response.data);
    } catch (error) {
      alert('Erreur lors de la récupération des tâches');
    }
  };

  const addTask = async () => {
    try {
      await axios.post('http://localhost:5000/tasks', { text: newTask }, {
        headers: { Authorization: `Bearer ${localStorage.getItem('token')}` }
      });
      fetchTasks();
      setNewTask('');
    } catch (error) {
      alert('Erreur lors de l ajout d une tâche');
    }
  };

  const deleteTask = async (id) => {
    try {
      await axios.delete(`http://localhost:5000/tasks/${id}`, {
        headers: { Authorization: `Bearer ${localStorage.getItem('token')}` }
      });
      fetchTasks();
    } catch (error) {
      alert('Erreur lors de la suppression d une tâche');
    }
  };

  return (
    <div>
      <h1>Gestionnaire de Tâches</h1>
      {user ? (
        <form onSubmit={(e) => e.preventDefault()}>
          <input
            type="text"
            placeholder="Nouvelle tâche"
            value={newTask}
            onChange={(e) => setNewTask(e.target.value)}
          />
          <button onClick={addTask}>Ajouter</button>
        </form>
      ) : (
        <p>Veuillez vous connecter pour utiliser le gestionnaire de tâches.</p>
      )}
      <ul>
        {tasks.map((task) => (
          <li key={task.id}>
            {task.text}
            <button onClick={() => deleteTask(task.id)}>Supprimer</button>
          </li>
        ))}
      </ul>
    </div>
  );
};

export default TaskManager;

Étape 4 : Affichage des composants

Modifiez le App.js pour afficher les composants Auth et TaskManager :

// src/App.js

import React, { useState } from 'react';
import Auth from './Auth';
import TaskManager from './TaskManager';

const App = () => {
  const [user, setUser] = useState(null);

  return (
    <div>
      {user ? (
        <div>
          <h1>Bienvenue, {user.username}</h1>
          <TaskManager user={user} />
        </div>
      ) : (
        <Auth setUser={setUser} />
      )}
    </div>
  );
};

export default App;

Erreurs frequentes et debugging

1. Authentification échoue

Code incorrect :

const handleSubmit = async (e) => {
  e.preventDefault();
  const response = await axios.post('http://localhost:5000/login', { username, password });
};

Correction :

const handleSubmit = async (e) => {
  e.preventDefault();
  try {
    const response = await axios.post('http://localhost:5000/login', { username, password });
    localStorage.setItem('token', response.data.token);
    setUser({ username });
  } catch (error) {
    alert('Identifiants incorrects');
  }
};

2. Ajout de tâche échoue

Code incorrect :

const addTask = async () => {
  await axios.post('http://localhost:5000/tasks', { text: newTask });
  fetchTasks();
  setNewTask('');
};

Correction :

const addTask = async () => {
  try {
    await axios.post('http://localhost:5000/tasks', { text: newTask }, {
      headers: { Authorization: `Bearer ${localStorage.getItem('token')}` }
    });
    fetchTasks();
    setNewTask('');
  } catch (error) {
    alert('Erreur lors de l ajout d une tâche');
  }
};

3. Suppression de tâche échoue

Code incorrect :

const deleteTask = async (id) => {
  await axios.delete(`http://localhost:5000/tasks/${id}`);
  fetchTasks();
};

Correction :

const deleteTask = async (id) => {
  try {
    await axios.delete(`http://localhost:5000/tasks/${id}`, {
      headers: { Authorization: `Bearer ${localStorage.getItem('token')}` }
    });
    fetchTasks();
  } catch (error) {
    alert('Erreur lors de la suppression d une tâche');
  }
};

Pour aller plus loin

1. Utilisation de JWT pour l'authentification

JSON Web Tokens (JWT) sont un moyen sécurisé d'échanger des informations entre parties en tant que token.

Documentation :

2. Sécurité des données sensibles

Pour gérer les données sensibles comme les mots de passe, il est recommandé d'utiliser une hash function robuste et de saler le mot de passe avant son stockage.

Documentation :

3. Sécurité des formulaires

Les formulaires doivent être protégés contre les injections SQL et les attaques CSRF.

Documentation :

Défi pratique

Développez une fonction pour gérer la récupération de mot de passe (forget password) qui implique des vérifications d'e-mail et envoi d'un token de réinitialisation.

Documentation :

En suivant ce tutoriel, vous serez mieux équipé pour développer des applications React sécurisées. N'oubliez pas que la sécurité est un processus continu et que vous devrez toujours rester à jour sur les dernières menaces et meilleures pratiques.

Besoin d'aide sur React ?

Besoin d'aide sur un projet technique ? Decrivez-le pour des conseils personnalises.

Recevoir des conseils

Questions frequentes

Comment protéger les informations sensibles dans un composant React?
Pour protéger les informations sensibles, vous pouvez utiliser des variables d'environnement pour stocker des secrets comme les clés API et les mots de passe. Vous pouvez également utiliser des contextes React pour partager des données de manière sécurisée entre les composants.
Comment éviter les cross-site scripting (XSS) dans une application React?
Pour prévenir les attaques XSS, vous devez toujours échapper les entrées utilisateur avant de les rendre dans votre application. Utilisez des bibliothèques comme `DOMPurify` pour nettoyer automatiquement les entrées et éviter les scripts malveillants.
Comment gérer la sécurité en local lors du développement d'une application React?
En développement, il est important de configurer des serveurs de développement sécurisés. Utilisez HTTPS pour toutes vos requêtes et assurez-vous que vos cookies sont sécurisés (par exemple, avec l'option `Secure`). En outre, utilisez des outils comme `React Developer Tools` pour surveiller les états et les actions réactives dans votre application.

Pages liees

Chaque semaine, le meilleur de la tech francaise

Tendances, salaires, outils et opportunites — directement dans votre boite mail.

Gratuit. Desabonnement en un clic. Pas de spam.