Skip to main content
BlogCalculSe défendre contre une attaque par force brute de l'API de connexion

Se défendre contre une attaque par force brute de l'API de connexion

Un bloc rouge avec le texte suivant : "Se défendre contre une attaque par force brute de l'API de connexion : Parce que couper les coins ronds peut vous paralyser"

Soyons réalistes : lorsque vous êtes pressé de respecter une échéance importante, vous faites des économies. La couverture de vos tests devient irrégulière, votre code n'est pas si DRY, et votre gestion des exceptions est envoyée au cimetière de la dette technique - je veux dire, dans le backlog. Nous sommes tous passés par là.

Mais quand il s'agit de faire des économies, il ne faut pas omettre de mettre en œuvre la mesure de protection "maximum de tentatives de connexion infructueuses". Si votre API de connexion n'est pas dotée de protections adéquates, il est relativement facile de nos jours d'accéder à un compte d'utilisateur par force brute.

Dans cet article, nous allons vous montrer comment un attaquant peut forcer brutalement une API de connexion. Ensuite, nous discuterons des contre-mesures que vous pouvez mettre en place pour défendre vos systèmes.

Un exemple d'API de connexion

Pour le démontrer, nous avons construit un serveur API très basique à l'aide de Node.js et Express. Il est à l'écoute des requêtes POST vers un seul point de terminaison, /login. Le corps de la demande est censé contenir un username et un password. Lorsque les informations d'identification sont fournies avec succès, l'API renvoie l'information suivante 200 OK. Sinon, il renvoie 401 UNAUTHORIZED.

Voici le code de notre serveur Express simple :

const express = require('express');
const users = require('./users.json');

const app = express();
const PORT = 3000;

// Middleware to parse JSON bodies
app.use(express.json());

app.post('/login', (req, res) => {
    const { username, password } = req.body;

    // Check if the username exists and the password matches
    if (users[username] && users[username] === password) {
        return res.status(200).send('OK');
    } else {
        return res.status(401).send('UNAUTHORIZED');
    }
});

// Start the server
app.listen(PORT, () => {
    console.log(`Server is listening on port ${PORT}`);
});

Pour cette démonstration simple, nous conservons la liste des noms d'utilisateur et des mots de passe dans un fichier appelé users.json.

Nous avons déployé notre serveur sur un Linode, en écoutant sur le port 3000. Essayons de répondre à une demande :

~$ curl -i \        -X POST \        --header "Content-type:application/json" \        --data '{"username":"user08","password":"testpassword"}' \         172.233.153.34:3000/login

HTTP/1.1 401 UnauthorizedX-Powered-By: ExpressContent-Type: text/html; charset=utf-8Content-Length: 12ETag: W/"c-MvHJP5yPj49I/9tX+wGrvHWbTRk"Date: Sat, 25 May 2024 19:17:34 GMTConnection: keep-aliveKeep-Alive: timeout=5
UNAUTHORIZED

Notre API de connexion est opérationnelle !

Maintenant, si nous voulions forcer notre API, nous pourrions écrire un script comme celui-ci :

#!/bin/sh

API_ENDPOINT="http://172.233.153.34:3000/login"

USERNAME="user5"

echo "Attempting to login at $API_ENDPOINT as \"$USERNAME\""

for i in {1..8}
do
  PASS="password${i}"
  RESULT=$(curl --silent -X POST --header "Content-type:application/json" --data "{\"username\":\"$USERNAME\",\"password\":\"$PASS\"}" $API_ENDPOINT)
  echo "\"$PASS\": $RESULT"

  if [[ $RESULT == 'OK' ]]; then
    echo "!!! Password for \"$USERNAME\" found: \"$PASS\""
    break
  else
    sleep 1
  fi
done

Lorsque nous exécutons notre script rudimentaire de force brute, le résultat est le suivant :

$ source bruteforce.sh
Attempting to login at http://172.233.153.34:3000/login as "user5"
"password1": UNAUTHORIZED
"password2": UNAUTHORIZED
"password3": UNAUTHORIZED
"password4": UNAUTHORIZED
"password5": OK
!!! Password for user5 found: "password5"

Un attaquant peut-il vraiment deviner un mot de passe ?

Nous avons donc vu à quel point il est facile d'écrire un script qui peut au moins faire défiler quelques mots de passe pour un nom d'utilisateur donné. Vous pourriez dire : "Ok, c'est un exemple intéressant et artificiel. Mais un attaquant peut-il vraiment deviner un mot de passe?"

Voici une liste des 10 000 mots de passe les plus courants. C'est un bon début pour tout attaquant. Pensez-y : avez-vous déjà rencontré quelqu'un qui utilise "password123" ou "qwerty" ? Il se peut même que ce soit vous !

Si un pirate connaît quelques noms d'utilisateur de votre système et qu'il exécute un script qui passe en boucle par ces mots de passe courants, il peut obtenir un résultat.

Chaque tentative autorisée de combinaison d'un nom d'utilisateur et d'un mot de passe augmente les chances de violation d'un compte.

Il s'agit d'un cas classique d'authentification défaillante, qui figure en deuxième position dans le Top 10 des risques de sécurité des API de l'OWASP. Si votre application n'est pas correctement protégée contre les attaques automatisées, vous vous exposez à des problèmes. En l'absence de mesures de protection, les comptes de vos utilisateurs sont exposés à un risque important.

Les mots de passe sont souvent le maillon faible de la sécurité d'un système :

  • Les utilisateurs réutilisent leurs mots de passe sur plusieurs sites.
  • Les utilisateurs choisissent des mots de passe faciles à retenir (et à deviner).
  • Les utilisateurs mettent rarement à jour leurs mots de passe.

Tous ces facteurs rendent les attaques par force brute terriblement efficaces.

Revenons donc à notre question : Un attaquant peut-il vraiment deviner un mot de passe ? Absolument. Et si vous ne prenez pas les bonnes précautions, cela peut arriver plus tôt que vous ne le pensez.

Comment protéger mon API ?

Il existe plusieurs façons de se défendre contre une attaque par force brute de votre API de connexion.

Fixer un nombre maximum de tentatives de connexion infructueuses

Fixez une limite au nombre de tentatives de connexion infructueuses que vous autorisez pour chaque utilisateur. Au fur et à mesure des tentatives de connexion d'un utilisateur, tenez un compte courant des échecs de connexion. Si vous atteignez cette limite, verrouillez temporairement le compte ou bloquez les demandes ultérieures provenant de l'adresse IP de l'expéditeur. Il sera ainsi beaucoup plus difficile pour un attaquant de s'introduire par la force brute.

Utiliser un pare-feu d'application web (WAF)

Un WAF peut contribuer à protéger votre API en détectant et en bloquant les activités malveillantes.

  • Activité des robots: Un bon WAF peut faire la distinction entre les utilisateurs légitimes et les robots, en bloquant les attaques automatisées par force brute.
  • Demandes de connexion suspectes provenant de l'arrière-plan de Tor: De nombreux attaquants utilisent le réseau Tor pour dissimuler leur identité. Le fait de bloquer ou de contester les demandes provenant de nœuds Tor peut réduire le risque d'attaques.

Haltdos, qui est un WAF disponible sur Linode Marketplace, offre ces protections et bien plus encore. En intégrant de tels outils, vous pouvez renforcer considérablement les défenses de votre API.

Mise en place d'une limitation de débit

Limiter le nombre de demandes d'API provenant d'une même adresse IP dans un laps de temps donné. Cela ralentit les attaques par force brute et les rend moins réalisables. La limitation du débit est une bonne pratique pour toutes vos API et tous vos points de terminaison, et pas seulement pour votre API de connexion.

Activer l'authentification multifactorielle (MFA)

Si vous ajoutez une couche de sécurité supplémentaire, comme l'AMF, vous pouvez déjouer les attaques par force brute même si l'attaquant devine correctement le mot de passe. Pour réussir à passer l'ensemble du processus d'authentification, l'utilisateur doit connaître quelque chose (le mot de passe) et posséder quelque chose (un téléphone ou un jeton matériel).

Surveiller et analyser les tentatives de connexion

Mettez en place une solution de surveillance des demandes d'API pour garder un œil sur les tentatives de connexion. Recherchez des modèles qui pourraient indiquer une attaque. Cela peut vous aider à réagir rapidement en cas d'activité suspecte.

La mise en œuvre de ces mesures peut contribuer à protéger votre API contre les attaques par force brute et à sécuriser les comptes de vos utilisateurs.

Conclusion

Il est essentiel de protéger votre API de connexion contre les attaques par force brute. Deviner un mot de passe n'est pas aussi difficile qu'on pourrait le croire, et automatiser les suppositions est un jeu d'enfant. Votre système est-il vulnérable ? Nous avons abordé plusieurs mesures que vous pouvez prendre pour renforcer vos systèmes. Ces mesures amélioreront considérablement votre position en matière de sécurité.

Dans la course effrénée à la création de vos applications et API, nous vous pardonnons de faire des économies ici et là. Tout le monde le fait. Mais ne laissez pas votre API de connexion sans protection ! N'attendez pas qu'une attaque se produise, prenez des mesures dès maintenant pour protéger votre système et vos utilisateurs. Pour des conseils plus détaillés sur la journalisation et la surveillance du système, consultez la documentation de Linode. Si vous prévoyez d'utiliser Apache pour servir vos applications, vous pouvez consulter ce guide sur la configuration de mod_evasive pour aider votre serveur à survivre aux attaques DoS/DDoS.

Soyez prudents !

Commentaires

Laissez un commentaire

Votre adresse électronique ne sera pas publiée. Les champs obligatoires sont marqués d'un *.