Cloud Native LAB 2 Mr NMIRACH
Lab 02 : Authentifier une API REST avec JWT
Les API sont un moteur essentiel pour fournir des données à vos applications. Dans ce cours, nous allons
découvrir les différentes options de sécurisation de votre API RESTful qui peuvent vous aider à assurer la
sécurité de vos données d'application et de vos utilisateurs. On va commencer le cours par un aperçu des
principales menaces de sécurité et une introduction à l'Open Web Application Security Project (OWASP),
une ressource importante sur la sécurité. On explique ensuite comment configurer et sécuriser une API
Node et Express, y compris comment ajouter des gestionnaires (handlers) pour l'enregistrement et la
connexion, finaliser les endpoints sécurisés et tester votre API finalisée. On va conclure, par partager
quelques alternatives pour sécuriser les API.
Plan du Lab :
1. Setting up
• Aperçu des menaces de sécurité
• Présentation de l'OWASP
• Présentation de JWT
• POSTMAN
2. Setting up Node API
• Créer Model User
• Ajouter bcrypt password hashing
3. Sécurisé Node API
• Ajout du handlers pour loginRequired
• Ajout des handlers pour l’inscription (registration)
• Ajouter les handlers pour la connexion (login)
• Finaliser les endpoints sécurisés
• Ajouter la configuration JWT dans l'index
• Testez l'API avec Postman
NB : Compte tenu de la nature dynamique des outils cloud, vous pouvez rencontrer des modifications
de l'interface utilisateur qui se produisent après le développement de ce contenu de pedagogique. Par
conséquent, les instructions de lab et les étapes de lab peuvent ne pas s'aligner correctement.
Cloud Native LAB 2 Mr NMIRACH
1. Setting up
Aperçu des menaces de sécurité
Il existe une liste croissante de menaces pour les développeurs Web, et vous devez vous tenir au courant
de tous ces problèmes de sécurité quotidiennement. Donc, au-delà de la portée de ce cours, vous devez
continuer à lire sur le sujet et à examiner les techniques et outils courants pour prévenir ces problèmes.
OWASP, que je présenterai, est un excellent point de départ et où je vais toujours pour comprendre les
dernières menaces et comment les atténuer. La prévention doit toujours être votre priorité numéro un
par rapport à la gestion d'une attaque après coup.
Voici donc une brève liste des cinq attaques les plus récurrentes :
1. Les attaques par injection. C'est lorsque des données non fiables sont envoyées à un
interpréteur dans le cadre d'une commande ou d'une requête.
Cloud Native LAB 2 Mr NMIRACH
2. L'authentification cassée. Souvent, la mise en œuvre de l'authentification est interrompue et
constitue donc une excellente occasion pour les attaquants d'obtenir des mots de passe, des
jetons, etc.
3. L'exposition des données sensibles. Le plus souvent, les API ne protègent pas correctement les
informations sensibles et offrent donc facilement des opportunités de vol d'identité et d'autres
fuites d'informations malveillantes.
4. Les entités XML. Les processeurs XML plus anciens ou mal écrits peuvent être exploités avec une
injection XML avec un contenu hostile.
5. Et le dernier est le contrôle d'accès cassé (broken access control). Les restrictions sur ce à quoi
les utilisateurs peuvent accéder sont souvent mal mises en œuvre et permettent aux autres
utilisateurs de voir des données sensibles.
Cloud Native LAB 2 Mr NMIRACH
Voici donc cinq des attaques les plus signalées, mais il y en a plus, et le premier endroit où je voudrais
aller pour bien comprendre tout cela est le rapport OWASP Top 10.
Avec ce bref aperçu des principales attaques, nous espérons qu'il vous donnera un point de départ pour
vos recherches.
Présentation de l'OWASP
Si vous êtes sérieux au sujet de la sécurité, l'OWASP est toujours un excellent endroit pour commencer
votre recherche de menaces et de solutions potentielles. Il s'appelle Open Web Application Security
Project, ou OWASP, et comprend une grande communauté et de nombreuses ressources à votre
disposition. Rendez-vous donc sur www.owasp.org.
Donc, tout d'abord, si vous êtes complètement nouveau dans le domaine de la sécurité et de l'OWASP,
rendez-vous dans la zone des médias sociaux et rejoignez le groupe Facebook, les canaux Slack ou l'une
des autres communautés potentielles disponibles.
Ensuite, si vous souhaitez obtenir plus d'informations sur tout type d'attaques par nom, accédez à la
zone de référence ici.
Cloud Native LAB 2 Mr NMIRACH
Ainsi, par exemple, si vous allez dans la section Attaques, vous pouvez trouver toutes sortes d'attaques
par nom.
Ainsi, par exemple, si nous recherchons une attaque par brut force, vous pouvez la trouver ici, puis vous
pouvez cliquer sur ce lien et obtenir plus d'informations sur cette attaque spécifique.
Et si vous souhaitez obtenir des solutions réelles ou des extraits de code sur plusieurs menaces de
sécurité, rendez-vous dans la zone Code Snippets, qui se trouve juste en dessous de la section Attack :
Cloud Native LAB 2 Mr NMIRACH
Enfin, je vous suggère de visiter également la section Vulnérabilité pour en savoir plus sur les domaines
potentiels où vos applications pourraient être vulnérables aux menaces.
Cloud Native LAB 2 Mr NMIRACH
Ce site regorge de ressources pour vous aider à démarrer et vous aider dans vos recherches. Assurez-
vous donc de vous inscrire à leur Newsletter pour obtenir les dernières informations sur les menaces de
sécurité.
Présentation de JWT
JWT, ou JSON Web Token, est une norme ouverte utilisée pour transmettre en toute sécurité des
informations entre les parties. L'objet JSON se compose principalement de trois éléments, un en-tête, la
payload et la signature.
Allez donc sur jwt.io, puis faites défiler jusqu'à ce que vous voyiez les sections Encoded et Décoded.
Cloud Native LAB 2 Mr NMIRACH
Cela vous aidera à comprendre ce qu’on est en train d’expliquer.
Ainsi, l'en-tête (header) comporte généralement deux parties, le type de jeton et l'algorithme de
hachage utilisé pour chiffrer le jeton. Ensuite, la payload se compose des métadonnées de la partie
demandeuse qui sont requises du serveur. Certaines informations typiques que vous verrez dans la
payload sont l'émetteur de la demande, l'expiration, le nom, etc, enfin, la signature est ce qui prouve que
le demandeur est bien celui qu'il prétend être et c'est ainsi que la demande est correctement validée,
c'est ce que vous voyez dans cette section.
Des services comme AuthO utilisent JWT et nous parlerons brièvement du service plus tard. En fait, la
documentation JWT a été élaborée par l'équipe AuthO. Alors, à quoi sert JWT ?
Cloud Native LAB 2 Mr NMIRACH
C'est le meilleur moyen de transmettre en toute sécurité des informations entre les parties sur le Web.
Ainsi, la charge utile pourrait être utilisée pour tout type d'informations que vous souhaitez transmettre à
partir de deux parties. Et bien évidemment, cela peut être utilisé pour authentifier un utilisateur. Ainsi,
lorsque l'utilisateur est enregistré sur un site, le jeton sera utilisé pour valider l'utilisateur car il effectue
plusieurs demandes de données.
POSTMAN :
Nous avons déjà vue POSTMAN en détails lors de notre dernier cours,
Cloud Native LAB 2 Mr NMIRACH
Maintenant on va juste mentionner l'une des choses que j'aime vraiment dans cette application est
qu'elle fournit des extraits de code (code snippet) pour votre application ou la langue que vous utilisez.
Donc, si vous souhaitez utiliser l'un de ces codes, lorsque vous avez fait votre demande, vous pouvez
cliquer sur le code, puis vous obtiendrez le code pour votre langue spécifique. Ainsi, par exemple, si nous
utilisons le NodeJS natif, il s'agit du code que vous entreriez dans votre frontal. Si vous utilisez d'autres
langages, par exemple, C, C#, JS, etc., vous pouvez obtenir les extraits de code pour ces langages
spécifiques.
2. Setting up Node API
Alors maintenant, ce que nous allons faire est se baser sur les fichiers du projet original que nous avons
créé lors de notre cours précédent :
Créer Model User
La prochaine étape de la création d'une API sécurisée consiste à ajouter des utilisateurs qui peuvent
s'inscrire et se connecter à l'application. Et pour cela, nous avons besoin d'un schéma User.
Revenons donc à VSCode, et dans le dossier models, je vais créer nouveau fichier ici, puis appeler ce
modèle userModel.js
import mongoose from 'mongoose';
const Schema = mongoose.Schema;
export const UserSchema = new Schema({
username: {
type: String,
required: true
},
email: {
type: String,
required: true
Cloud Native LAB 2 Mr NMIRACH
},
hashPassword: {
type: String,
required: true
},
created_date: {
type: Date,
default: Date.now
}
});
Ajouter bcrypt password hashing
Bcrypt, c'est un package qui permet de chiffrer et de déchiffrer des données, comme un mot de passe.
Donc, nous allons installer bcrypt et pendant que nous installons de nouveaux packages, nous
installerons également jsonwebtoken.
Passons donc à notre terminal, ou vous pouvez installer bcrypt puis jsonwebtoken. Puis tapez ces
commandes :
npm i bcrypt jsonwebtoken
et puis sur notre fichier userModel.js :
import mongoose from 'mongoose';
import bcrypt from 'bcrypt';
const Schema = mongoose.Schema;
export const UserSchema = new Schema({
username: {
type: String,
required: true
},
email: {
type: String,
required: true
},
hashPassword: {
type: String,
required: true
},
created_date: {
type: Date,
default: Date.now
Cloud Native LAB 2 Mr NMIRACH
}
});
//fonction qui compqre les mot de passe
UserSchema.methods.comparePassword = (password, hashPassword) => {
return bcrypt.compareSync(password, hashPassword);
};
3. Sécurisé Node API
Ajout du handlers pour loginRequired:
Continuons et créons les contrôleurs pour notre modèle utilisateur. Au sein du dossier controllers on va
créer notre fichier userControllers.js :
import mongoose from 'mongoose';
import bcrypt from 'bcrypt';
import jwt from 'jsonwebtoken';
import { UserSchema } from '../models/userModel';
const User = mongoose.model('User', UserSchema);
export const loginRequired = (req, res, next) => {
if (req.user) {
next();
} else {
return res.status(401).json({ message: 'Unauthorized user!'});
}
}
Ajout des handlers pour l’inscription (registration) :
Nous devons créer la fonction d'enregistrement de nouveaux utilisateurs.
Dans notre fichier userControllers.js :
import mongoose from 'mongoose';
import bcrypt from 'bcrypt';
import jwt from 'jsonwebtoken';
import { UserSchema } from '../models/userModel';
const User = mongoose.model('User', UserSchema);
export const loginRequired = (req, res, next) => {
Cloud Native LAB 2 Mr NMIRACH
if (req.user) {
next();
} else {
return res.status(401).json({ message: 'Unauthorized user!'});
}
}
export const register = (req, res) => {
const newUser = new User(req.body);
newUser.hashPassowrd = bcrypt.hashSync(req.body.password, 10);
newUser.save((err, user) => {
if (err) {
return res.status(400).send({
message: err
});
} else {
user.hashPassword = undefined;
return res.json(user);
}
})
}
Ajouter les handlers pour la connexion (login)
Ainsi, la fonction finale que nous pouvons utiliser pour nos contrôleurs d'utilisateurs est la fonction de
connexion (login). Donc, ca va nous permettre de se connecter aux API.
Dans notre fichier userControllers.js :
import mongoose from 'mongoose';
import bcrypt from 'bcrypt';
import jwt from 'jsonwebtoken';
import { UserSchema } from '../models/userModel';
const User = mongoose.model('User', UserSchema);
export const loginRequired = (req, res, next) => {
if (req.user) {
next();
} else {
return res.status(401).json({ message: 'Unauthorized user!'});
}
}
export const register = (req, res) => {
Cloud Native LAB 2 Mr NMIRACH
const newUser = new User(req.body);
newUser.hashPassword = bcrypt.hashSync(req.body.password, 10);
newUser.save((err, user) => {
if (err) {
return res.status(400).send({
message: err
});
} else {
user.hashPassword = undefined;
return res.json(user);
}
})
}
export const login = (req,res) => {
User.findOne({
email: req.body.email
}, (err, user) => {
if (err) throw err;
if (!user) {
res.status(401).json({ message: 'Authentication failed. No user
found'});
} else if (user) {
if (!user.comparePassword(req.body.password, user.hashPassword)) {
res.status(401).json({ message: 'Authentication failed. Wrong
password'});
} else {
return res.json({token: jwt.sign({ email: user.email, username:
user.username, _id: user.id}, 'RESTFULAPIs')});
}
}
});
}
Finaliser les endpoints sécurisés
Nous devons donc ajouter nos routes pour l'enregistrement et la connexion.
Allez donc dans le dossier routes puis ouvrez crmRoutes.js :
import {
addNewContact,
Cloud Native LAB 2 Mr NMIRACH
getContacts,
getContactWithID,
updateContact,
deleteContact
} from '../controllers/crmController';
import { login, register, loginRequired } from '../controllers/userControllers'
const routes = (app) => {
app.route('/contacts')
.get((req, res, next) => {
// middleware
console.log(`Request from: ${req.originalUrl}`)
console.log(`Request type: ${req.method}`)
next();
}, loginRequired, getContacts)
// POST endpoint
.post(loginRequired, addNewContact);
app.route('/contact/:contactId')
// get specific contact
.get(loginRequired, getContactWithID)
// put request
.put(loginRequired, updateContact)
// delete request
.delete(loginRequired, deleteContact);
// registration route
app.route('/auth/register')
.post(register);
// login route
app.route('/login')
.post(login);
}
export default routes;
Cloud Native LAB 2 Mr NMIRACH
Ajouter la configuration JWT dans l'index
Bon alors maintenant, la dernière pièce que nous devons faire est de faire la configuration JWT à
l'intérieur de notre index.
Alors allons dans le fichier index.js :
import express from 'express';
import mongoose from 'mongoose';
import bodyParser from 'body-parser';
import jsonwebtoken from 'jsonwebtoken';
import routes from './src/routes/crmRoutes';
const app = express();
const PORT = 3000;
// mongoose connection
mongoose.Promise = global.Promise;
mongoose.connect('mongodb://localhost/CRMdb', {
useNewUrlParser: true,
useUnifiedTopology: true
});
// bodyparser setup
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
// JWT setup
app.use((req, res, next) => {
if (req.headers && req.headers.authorization &&
req.headers.authorization.split(' ')[0] === 'JWT') {
jsonwebtoken.verify(req.headers.authorization.split(' ')[1],
'RESTFULAPIs', (err, decode) => {
if (err) req.user = undefined;
req.user = decode;
next();
});
} else {
req.user = undefined;
next();
}
});
routes(app);
Cloud Native LAB 2 Mr NMIRACH
// serving static files
app.use(express.static('public'));
app.get('/', (req, res) =>
res.send(`Node and express server is running on port ${PORT}`)
);
app.listen(PORT, () =>
console.log(`your server is running on port ${PORT}`)
);
Testez l'API avec Postman
On va commencer par créer un utilisateur :
Alors allons-y, connectez-vous et rappelez-vous que la route de connexion est simplement /login
Et encore une fois, c'est un POST et nous pouvons réutiliser les mêmes informations afin que nous
puissions aller de l'avant et les envoyer aux API. Et maintenant, nous obtenons un jeton. Donc, en
théorie, votre frontal tirerait parti de ce jeton pour effectuer des transactions avec l'API.
Cloud Native LAB 2 Mr NMIRACH
Donc, ce que nous allons faire, c'est simuler cela via Postman en voulant copier le jeton entre les
guillemets, puis ce que nous allons faire, par exemple, obtenir la liste des contacts, donc nous allons pour
aller à GET.
Faites /contacts comme nous l'avons fait auparavant. Et maintenant, nous devons entrer quelques
éléments. Il n'y a donc rien dont nous ayons besoin pour obtenir un contact afin que nous puissions
supprimer ces données ici.
Et puis nous envoyons certaines choses à l'intérieur de nos en-têtes. Nous devons donc envoyer une
autorisation. Et nous devons ajouter JWT, puis coller le jeton.
Donc, si vous vous en souvenez, c'est le processus que nous avons entré dans notre contrôleur et c'est là
que nous allons vérifier s'il y a un utilisateur connecté via la commande que nous avons créée à l'intérieur
de notre index ici.
Cela va donc vérifier que nous avons JWT plus le jeton que nous avons et c'est le mot-clé qui vérifiera
notre jeton ici. Revenons donc à Postman. Et avec ces commandes, nous pouvons essentiellement
effectuer une transaction avec l'API et nous devrions être prêts à partir. Et si vous voyez une liste de
contacts, cela signifie que vous avez envoyé les bonnes informations au serveur. Donc, le jeton
d'autorisation avec JWT, si vous voyez la liste des contacts à l'intérieur de votre base de données, tout va
bien.
Cloud Native LAB 2 Mr NMIRACH
Félicitaion !!!! Vous avez donc votre propre système d'authentification de base Node avec
une API qui lui est attachée.