Multi-Factor Authentication
Qu’est-ce que l’authentification multifacteur ?
On voit de plus en plus, pour des raisons de sécurité, des services vous demandant d’activer l’authentification à double facteur ; en anglais Two-Factor Authentication (2FA) ou Multi-Factor Authentication (MFA). En effet, même si l’authentification multifacteur se limite souvent à deux d’entre eux, rien n’interdit de faire plus.
Les méthodes d’authentification à double facteur les plus courantes sont :
-
l’e-mail ;
-
le SMS ;
-
les applications One Time Password (OTP) ;
-
les codes de secours.
Plus rarement, on trouve :
-
les clés de sécurité (clé USB) ;
-
les OTP physiques.
Parmi ces différentes options, certaines sont plus recommandées que d’autres.
L’e-mail est une bonne option sous réserve que votre boîte e-mail soit protégée par un mot de passe fort et unique que vous changez régulièrement. Dans le cas contraire, ce serait une faille de sécurité ; les adresses e-mail mal protégées étant souvent piratées. L’e-mail a également l’avantage de son faible coût.
Le SMS a souvent été la méthode privilégiée, mais sa sécurité a été remise en cause ces dernières années....
Applications OTP
1. Page principale
Nous allons créer une page de gestion des modes d’authentification à double facteur. Pour ce faire, nous allons commencer par créer un contrôleur dédié à la gestion du compte utilisateur.
Ajoutez au dossier Controllers un fichier ManageAccountController.cs :
[Authorize]
[Route("account/manage")]
public class ManageAccountController : Controller
{
[TempData]
public string? StatusMessage { get; set; }
}
Dans ce contrôleur, nous allons ajouter une méthode qui gérera la page pour l’authentification multifacteur :
[HttpGet("mfa")]
public async Task<IActionResult> MultiFactorAuthentication()
{
ApplicationUser? user = await _userManager.GetUserAsync(User);
if (user == null)
return NotFound();
MultiFactorAuthenticationViewModel model = new()
{
HasAuthenticator =
await _userManager.GetAuthenticatorKeyAsync(user) != null,
Is2faEnabled =
await _userManager.GetTwoFactorEnabledAsync(user),
IsMachineRemembered =
await _signInManager.IsTwoFactorClientRememberedAsync(user),
RecoveryCodesLeft =
await _userManager.CountRecoveryCodesAsync(user)
};
return View(model);
}
Cette méthode va nous permettre de déterminer l’état de l’authentification multifacteur pour l’utilisateur connecté et de lui proposer les options adéquates.
La ViewModel MultiFactorAuthenticationViewModel contient l’ensemble des propriétés à afficher dans la vue :
public class MultiFactorAuthenticationViewModel
{
public bool IsAuthenticatorActivated { get; set; }
public bool HasAuthenticator { get; set; }
public bool IsEmailProviderActivated { get; set; }
public bool IsPhoneProviderActivated { get; set; }
public bool Is2faEnabled { get; set; } ...
Prise en charge de la double authentification
1. Connexion avec les codes OTP
Maintenant que notre application OTP est correctement configurée et délivre bien les tokens nécessaires à la double authentification, il faut que celle-ci soit activée à la connexion.
Précédemment, nous avions inclus dans la méthode de login un code temporaire qui permettait de passer outre la double authentification le temps des tests. Il est maintenant temps d’y substituer le vrai code fonctionnel.
Dans la classe AccountController, dans la méthode LoginPost(), remplacez le bloc suivant :
else if (result.RequiresTwoFactor)
{
await _signInManager.SignInAsync(user, false);
if (user.LockoutEnabled)
await _userManager.ResetAccessFailedCountAsync(user);
return Redirect(returnUrl);
}
Par celui-ci :
else if (result.RequiresTwoFactor)
{
return RedirectToAction(nameof(TwoFactorAuthenticationLogin),
new { returnUrl });
}
Dans le même contrôleur, à la suite de la méthode POST Login(), ajoutez cette méthode :
[HttpGet("2fa-login")]
public async Task<IActionResult>
TwoFactorAuthenticationLogin(string returnUrl)
{
ApplicationUser user =
await _signInManager.GetTwoFactorAuthenticationUserAsync()
?? throw
new InvalidOperationException(_localizer["2FAUserNotFound"]);
return View();
}
Et ajoutez au fichier de ressources AccountController.resx :
2FAUserNotFound |
Impossible de charger l’utilisateur de l’authentification à double facteur |
Puis créez la vue TwoFactorAuthenticationLogin.cshtml :
@inject IViewLocalizer Localizer
@model TwoFactorAuthenticationLoginViewModel
@{
ViewData["Title"] = Localizer["Title"];
ViewData["H1"] = Localizer["H1"];
string? returnUrl = Context.Request.Query["returnUrl"];
} ...
Double authentification par e-mail et SMS
Nous traiterons ces deux méthodes dans le même exemple, car le fonctionnement est identique. Seule la manière de transmettre le code à l’utilisateur change.
Pour l’authentification via e-mail ou SMS, le principe de fonctionnement est un peu différent des applications OTP. Dans ce cas, c’est le SSO qui génère lui-même le code de validation que devrait normalement générer l’application OTP et transmet celui-ci par e-mail ou SMS à l’utilisateur. Ce mode de fonctionnement permet également de faire de la connexion sans mot de passe. Dans ce cas, le code transmis se substitue au mot de passe de l’utilisateur. Une application extrêmement connue qui fonctionne sur ce principe est Medium.
Si vous cliquez sur Sign in with email, Medium vous affiche l’écran suivant :
Medium ne nous permet pas de saisir de mot de passe, juste notre e-mail et il nous envoie un lien « magique » pour la connexion.
Le lien que Medium m’a transmis était celui-ci : https://medium.com/m/callback/email?token=ed6c07811d27&operation=login&state=medium
On voit bien qu’un identifiant est transmis dans le lien « magique » afin de procéder à l’authentification.
Mieux vaut ne pas se faire pirater sa boîte e-mail, car le compte n’étant pas protégé par un mot de passe, n’importe qui ayant un accès à votre boîte e-mail peut accéder à votre compte. Le même principe s’applique pour le SMS car des pirates peuvent tout à fait cloner votre SIM depuis votre opérateur sans avoir besoin d’avoir votre smartphone entre les mains. La probabilité que ça arrive est faible, mais elle existe.
Cet avertissement fait, voyons comment mettre en place une double authentification par e-mail ou SMS.
1. Service d’envoi de codes par e-mail
Le point commun entre les e-mails et les SMS est l’envoi au destinataire. Dans la majorité des cas, cette opération est déléguée à un service externe. Dans ce livre, nous n’allons pas réellement nous connecter à un service d’envoi, ce n’est pas l’objet, mais nous allons mettre en place un service...
Faire cohabiter plusieurs modes en simultané
Parfait, nous avons maintenant à disposition trois méthodes distinctes (quatre avec les codes de secours) pour faire de la double authentification.
Mais comment choisir quelle méthode utiliser ?
À partir du moment où la double authentification est activée, si l’e-mail est confirmé, le fournisseur de double authentification par e-mail est automatiquement ajouté, idem avec le fournisseur pour les SMS. Donc, si nous souhaitons activer la double authentification avec l’application d’authentification, nous nous retrouvons de facto avec plusieurs fournisseurs. Et cela n’est pas sans conséquence, car il faut savoir lequel sélectionner, ou désactiver pour pouvoir gérer correctement ses préférences.
Si vous tentez de vous connecter, vous obtiendrez la vue suivante :
Le message affiché indique l’utilisation d’application OTP alors que le code ci-dessous fait appel au service Email :
else if (result.RequiresTwoFactor)
{
IList<string> providers =
await _userManager.GetValidTwoFactorProvidersAsync(user);
string token = await
_userManager.GenerateTwoFactorTokenAsync(user, "Email");
return RedirectToAction(nameof(TwoFactorAuthenticationLogin),
new { returnUrl });
}
Aucune de ces deux méthode n’est la bonne car le service qui a été activé est celui des SMS !
C’est incompréhensible pour l’utilisateur et ça n’a aucune chance de fonctionner.
Nous allons devoir modifier le système afin qu’il soit à même de gérer plusieurs modes d’authentification à double facteur en simultané, ou limiter à un seul fournisseur disponible en même temps.
1. Limiter à un seul fournisseur disponible à la fois
La première solution va être de limiter le nombre de fournisseurs disponibles à un seul à la fois. La double authentification étant une propriété de l’utilisateur, nous allons ajouter le choix du mode de double authentification à celui-ci.
Dans la classe ApplicationUser, ajoutez la propriété suivante :
[PersonalData] ...