Pourquoi cela nous concerne : nous sommes Geonode et nous vendons des proxys ; nous voyons donc passer un grand nombre de commandes contenant des identifiants — souvent à la fois un mot de passe de proxy et un mot de passe cible sur la même ligne. La mise en garde pratique qu’il convient de souligner d’emblée est que les identifiants figurant dans une commande curl se retrouvent dans l’historique du shell, dans la liste des processus et dans tout ce que vous collez dans un ticket d’assistance. Nous avons reçu à plusieurs reprises des captures d’écran contenant des mots de passe actifs. La méthode « .netrc » décrite ci-dessous permet de résoudre ce problème en une minute environ, sans aucun coût, et s’applique également aux identifiants de proxy, comme expliqué vers la fin.
La syntaxe de base
curl -u username:password https://api.example.com/private
Le manuel de curl explique ce qu’est l’-u, --user <user:password>
: « Spécifiez le nom d’utilisateur et le mot de passe à utiliser pour l’authentification auprès du serveur. »
« Basic » est le schéma par défaut ; par conséquent, --basic
est généralement superflu. Le manuel le précise d’ailleurs : « Utilisez l’authentification HTTP Basic avec l’hôte distant. Cette méthode est celle par défaut et cette option est généralement inutile, sauf si vous l’utilisez pour remplacer une option précédemment définie qui spécifie une méthode d’authentification différente. »
Ce qui est réellement transmis sur le réseau est un en-tête :
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=
Il s’agit d’username:password
, encodé en base64 — encodé, et non chiffré. Quiconque peut voir la requête peut la décoder en une seule étape. C’est pourquoi l’authentification Basic sur HTTP en clair revient à envoyer votre mot de passe en texte clair, et pourquoi elle ne devrait jamais être utilisée que sur HTTPS.
Une contrainte syntaxique tirée du manuel : « Le nom d’utilisateur et le mot de passe sont séparés par le premier deux-points, ce qui rend impossible l’utilisation d’un deux-points dans le nom d’utilisateur avec cette option. Le mot de passe, en revanche, peut en contenir. » Ainsi, un deux-points dans un mot de passe est autorisé ; un deux-points dans un nom d’utilisateur ne l’est pas.
Pourquoi la ligne de commande n'est pas le bon endroit
Le manuel est très clair à ce sujet :
Sur les systèmes où cela fonctionne, curl masque l'argument d'option fourni dans les listes de processus. Cela ne suffit pas à protéger les identifiants contre une éventuelle consultation par d'autres utilisateurs du même système, car ils restent visibles pendant un instant avant d'être effacés. Ces données sensibles devraient plutôt être récupérées à partir d’un fichier ou d’un support similaire, et ne jamais être utilisées en clair dans une ligne de commande.
Quatre cas de fuite distincts, tous réels :
L’historique du shell. ~/.bash_history ou l’équivalent zsh, en texte clair, indéfiniment.
Les listes de processus. Visibles par les autres utilisateurs de la machine pendant le bref laps de temps avant que curl ne les efface.
Journaux. Tout ce qui enregistre les commandes exécutées par un script.
Sorties collées. Rapports de bogues, outils de suivi des problèmes, messages de chat, captures d’écran.
Ce dernier cas est le plus courant dans la pratique et le moins pris en compte.
Trois méthodes plus sûres
1. Laisser curl demander le mot de passe. Indiquez uniquement le nom d'utilisateur ; curl vous demandera le mot de passe de manière interactive, en le lisant sans l'afficher à l'écran :
curl -u username https://api.example.com/private
Rien n'est stocké, rien n'est consigné. C'est la bonne approche pour tout ce que vous saisissez manuellement.
**2. Utilisez un fichier « .netrc
».** Le manuel décrit la commande « -n, --netrc
» : « Demandez à curl de parcourir le fichier .netrc situé dans le répertoire personnel de l’utilisateur à la recherche du nom d’utilisateur et du mot de passe… Si cette commande est utilisée avec HTTP, curl active l’authentification de l’utilisateur. »
Créez le fichier ~/.netrc
:
machine api.example.com
login myusername
password mypassword
. Limitez ensuite les droits d’accès à ce fichier, car curl ne le fera pas à votre place — le manuel précise que « curl ne signale pas d’erreur si ce fichier ne dispose pas des permissions appropriées (il ne doit être lisible ni par tous, ni par le groupe) » :
chmod 600 ~/.netrc
curl -n https://api.example.com/private
Trois détails utiles tirés du manuel. « Le fichier netrc fournit des identifiants pour un nom d’hôte, indépendamment du protocole et du numéro de port utilisés », de sorte qu’une seule entrée couvre un hôte. --netrc-file
« remplace toutes les autres méthodes permettant de déterminer le fichier », ce qui est pratique pour les fichiers d’identifiants spécifiques à un projet. Et depuis curl 8.16.0, une variable d’environnement NETRC
permet de spécifier le nom du fichier. Sous Windows, les fichiers .netrc
et _netrc
sont tous deux recherchés dans le répertoire personnel, la priorité étant donnée au premier.
--netrc-optional
est la variante qui utilise le fichier s’il est présent et ne génère pas d’erreur s’il est absent — ce qui convient mieux aux scripts susceptibles de s’exécuter dans l’un ou l’autre cas.
3. Lire à partir d’une variable d’environnement. Lorsqu’un fichier n’est pas envisageable, veillez au moins à ne pas l’enregistrer dans l’historique :
read -rs API_PASS
curl -u "myuser:${API_PASS}" https://api.example.com/private
Notez l’option -s
sur read
afin que le mot de passe ne soit pas affiché à l’écran. Celui-ci reste toutefois visible dans l’environnement du processus ; il s’agit donc d’une solution intermédiaire plutôt que d’une bonne solution.
Pour les scripts, la solution consiste à utiliser ``.netrc`
avec ``chmod 600
`. C'est l'option recommandée par le manuel et celle qui élimine tous les risques d'exposition mentionnés ci-dessus.
Le protocole Basic par rapport aux autres protocoles
curl prend en charge plusieurs protocoles, et savoir les distinguer permet d'éviter une certaine confusion.
| Option | Protocole | Mot de passe transmis en clair |
|---|---|---|
--basic | HTTP Basic (par défaut) | Encodé en Base64, équivalent à un mot de passe en clair |
--digest | HTTP Digest | Système de type « challenge-response » haché |
--ntlm | NTLM | Environnements Windows |
--negotiate | SPNEGO / Kerberos | Basé sur un ticket |
--anyauth | Automatique | Dépend de l’option choisie |
--oauth2-bearer | Jeton « Bearer » | Le jeton lui-même |
Digest est décrit comme suit : « Active l’authentification HTTP Digest. Ce schéma d’authentification évite d’envoyer le mot de passe en clair sur le réseau. Utilisez cette option en combinaison avec l’option standard --user pour définir le nom d’utilisateur et le mot de passe. »
--anyauth est une option pratique dont le manuel précise clairement le coût : « Détermine automatiquement la méthode d’authentification et utilise la plus sécurisée que le site distant déclare prendre en charge. Pour ce faire, une requête est d’abord envoyée et les en-têtes de réponse sont vérifiés, ce qui peut entraîner un aller-retour réseau supplémentaire. »
Un aller-retour supplémentaire par requête n’est pas gratuit lorsque le volume est important. Et le manuel met en garde contre un problème spécifique : «L’utilisation de --anyauth n’est pas recommandée si vous effectuez des téléchargements depuis stdin, car cela peut nécessiter l’envoi des données deux fois et le client doit alors être capable de revenir en arrière. Si cela s’avère nécessaire lors d’un téléchargement depuis stdin, l’opération échouera. »
Par conséquent : utilisez --anyauth lorsque vous ne savez vraiment pas ce que le serveur attend, et précisez le schéma dès que vous le savez.
Les jetons Bearer sont ceux que la plupart des API modernes utilisent en réalité, et ils n’ont absolument rien à voir avec l’authentification de base :
curl --oauth2-bearer "mF_9.B5f-4.1JqM" https://api.example.com/me
Cela revient à définir manuellement Authorization: Bearer .... Notez qu’un jeton Bearer est l’identifiant d’authentification — toute personne qui le détient peut l’utiliser — et qu’il doit donc être traité comme un mot de passe.
Lecture de la réponse
Une procédure de diagnostic rapide lorsque l'authentification ne fonctionne pas.
**401 Unauthorized
** signifie que le serveur demande des identifiants ou qu'il a rejeté ceux que vous lui avez envoyés. La réponse contient un en-tête « WWW-Authenticate
» indiquant le schéma attendu ; le lire vous évite d'avoir à deviner :
curl -sS -o /dev/null -D - https://api.example.com/private | grep -i www-authenticate
Si l'en-tête indique « Digest
» et que vous avez envoyé « Basic », vous avez votre réponse.
**403 Forbidden
** est un cas différent et souvent mal interprété. Vous vous êtes authentifié avec succès, mais vous n’êtes pas autorisé à effectuer cette action. Changer votre mot de passe ne servira à rien ; modifier vos droits d’accès pourrait toutefois vous aider.
**407 Proxy Authentication Required
** signifie que c’est le proxy qui demande des identifiants, et non la cible. Il s’agit d’un en-tête différent, avec une option différente, qui sera abordée ci-après.
**Une 200
avec une page de connexion** signifie que le point de terminaison n’utilise pas du tout l’authentification HTTP — il utilise un formulaire et un cookie de session, et -u
ne sert à rien dans ce cas. Vérifiez l’Content-Type
: si vous vous attendiez à du JSON et que vous avez obtenu text/html
, c’est probablement ce qui s’est passé.
Pour vérifier ce que vous avez réellement envoyé :
curl -v -u user:pass https://api.example.com/private 2>&1 | grep -i '^> authorization'
N’oubliez pas l’avertissement du manuel indiquant que la sortie détaillée « peut contenir des données sensibles, notamment des noms d’utilisateur, des identifiants ou des données confidentielles » — masquez ces informations avant de partager.
Créer soi-même l'en-tête
Il arrive parfois que -u
ne donne pas le résultat escompté, et le fait de savoir ce qu’il génère réellement vous permet de contourner ses limites.
Lorsque le nom d’utilisateur contient deux-points. -u
effectue la séparation au niveau des deux-points, ce qui rend impossible l’expression d’un nom d’utilisateur tel que service:reader
. Créez l’en-tête directement :
CRED=$(printf '%s' 'service:reader:mypassword' | base64 -w0)
curl -H "Authorization: Basic ${CRED}" https://api.example.com/private
Notez printf
plutôt que echo
, car ce dernier ajoute un retour à la ligne qui se retrouve à l’intérieur des identifiants encodés et génère une erreur 401 déroutante. Et utilisez base64 -w0
pour éviter les sauts de ligne — GNU base64
effectue un saut de ligne par défaut à 76 caractères, et un en-tête contenant un retour à la ligne est considéré comme une requête mal formée. Sous macOS, la commande simple base64
ne génère pas de saut de ligne et l’option n’est donc pas nécessaire.
Lorsque vous avez besoin des identifiants provenant d’un gestionnaire de secrets. La plupart des outils de gestion de secrets affichent la sortie sur stdout, et conserver la valeur dans une variable plutôt que dans un fichier limite sa durée de vie :
TOKEN=$(vault kv get -field=token secret/api)
curl -H "Authorization: Bearer ${TOKEN}" https://api.example.com/me
Lorsque vous souhaitez que l’en-tête figure dans un fichier de configuration plutôt que dans la commande. curl lit les options à partir de ~/.curlrc
, ou d’un fichier dont le nom se termine par -K
:
# api-auth.conf
--user "myuser:mypassword"
--header "Accept: application/json"
curl -K api-auth.conf https://api.example.com/private
Limitez le fichier à l’aide de chmod 600
. C’est un compromis raisonnable lorsque .netrc
ne convient pas — par exemple lorsque vous avez besoin d’un jeton plutôt que d’un nom d’utilisateur et d’un mot de passe.
Une mise en garde concernant spécifiquement ~/.curlrc
. Cela s’applique à chaque appel curl effectué par cet utilisateur, y compris ceux que vous n’avez pas écrits. Y placer des identifiants revient à les envoyer à n’importe quel serveur vers lequel un script pourrait envoyer une requête. Utilisez un fichier nommé avec -K
pour toute information sensible, et réservez ~/.curlrc
aux paramètres par défaut inoffensifs tels que --show-error
et --location
.
L'authentification du proxy est distincte
C'est la distinction qui suscite le plus de confusion dans notre file d'attente d'assistance.
Deux jeux d’identifiants indépendants peuvent être en jeu : l’un pour le proxy, l’autre pour la cible. Ils utilisent des en-têtes, des codes d’état et des options curl différents.
curl -x http://proxy.example.com:9000 \
--proxy-user proxyuser:proxypass \
-u apiuser:apipass \
https://api.example.com/private
--proxy-user
s’authentifie auprès du proxy ; -u
s’authentifie auprès de la cible. Les confondre entraîne l’affichage d’un code 407 alors que vous vous attendiez à un 401, ou inversement.
Le schéma du proxy comporte des options parallèles — --proxy-basic
, --proxy-digest
, --proxy-anyauth
, --proxy-negotiate
— qui font écho à celles du côté cible.
Deux remarques pratiques.
Les identifiants dans l’URL du proxy présentent le même problème d’exposition, avec un risque supplémentaire. -x http://user:pass@proxy:9000
place le mot de passe dans la ligne de commande et dans toute variable d’environnement contenant l’URL du proxy, là où http_proxy
se trouve généralement. Encodez en pourcentage tout @
, :
ou /
présent dans le mot de passe, sinon l’analyseur d’URL effectuera la coupure au mauvais endroit.
Identifiez quel nœud vous a refusé l’accès plutôt que de deviner :
curl -sS -o /dev/null -x "$PROXY" \
-w 'connect=%{http_connect} status=%{response_code}\n' \
https://api.example.com/private
connect=407
signifie que le proxy vous a rejeté et que la cible n’a jamais été atteinte. connect=200 status=401
signifie que le proxy a fonctionné et que la cible demande des identifiants. Deux solutions différentes.
Et une remarque spécifique à l’authentification par proxy : de nombreux fournisseurs proposent une liste blanche d’adresses IP comme alternative au nom d’utilisateur et au mot de passe. Si votre adresse source est stable, cela supprime entièrement les identifiants de vos commandes, ce qui constitue la solution la plus propre qui soit : pas de fichier, pas de variable d’environnement, rien qui puisse faire l’objet d’une fuite.
Lorsque le site n'utilise pas du tout l'authentification HTTP
Une grande partie des signalements du type « l'authentification de base avec curl ne fonctionne pas » concernent des cas où le site n'a tout simplement jamais utilisé l'authentification HTTP.
Comment le savoir ? Envoyez une requête vers l’URL protégée sans identifiants et examinez la réponse :
curl -sS -o /dev/null -D - https://example.com/dashboard
Une réponse de type « 401
» avec un en-tête « WWW-Authenticate
» indique une authentification HTTP, et -u
est l’outil approprié. Une réponse de type « 200
» renvoyant une page de connexion, ou une réponse de type « 302
» redirigeant vers /login
, signifie que le site utilise un formulaire et un cookie de session — et aucune requête de type « -u
» ne sera efficace, car rien ne lit cet en-tête.
Optez plutôt pour le modèle de connexion par formulaire. Envoyez les identifiants à l’endpoint de connexion, conservez les cookies et réutilisez-les :
curl -c jar.txt -d "username=ada&password=secret" \
https://example.com/login
curl -b jar.txt https://example.com/dashboard
-c
écrit un cookie dans le « cookie jar » et -b
en lit un. Utilisez les deux lors des requêtes suivantes (-b jar.txt -c jar.txt
) si le serveur renouvelle le cookie de session, ce que font de nombreux serveurs.
La complication à laquelle vous serez confronté : les jetons CSRF. La plupart des formulaires de connexion incluent un jeton caché qui doit être envoyé avec les identifiants, et celui-ci est généré à chaque session. Cela implique une séquence en deux étapes : récupérer le formulaire, extraire le jeton, puis l’envoyer avec les cookies de la première étape :
TOKEN=$(curl -sS -c jar.txt https://example.com/login \
| grep -o 'name="csrf_token" value="[^"]*"' \
| cut -d'"' -f4)
curl -b jar.txt -c jar.txt \
-d "csrf_token=${TOKEN}" -d "username=ada" -d "password=secret" \
https://example.com/login
L'utilisation de grep et de cut sur du code HTML est peu fiable, et cela convient uniquement pour un diagnostic ponctuel. Pour toute tâche récurrente, vérifiez d'abord si le service propose une API avec authentification par jeton — c'est presque toujours le cas, et cela vous demandera nettement moins d'efforts que de maintenir un scraper de votre propre formulaire de connexion.
Et si la connexion nécessite JavaScript, curl ne peut absolument pas s’en charger. Il ne s’agit pas d’une limitation de curl à contourner ; c’est un signal indiquant qu’il faut rechercher le point de terminaison de l’API que la page elle-même appelle, que vous pouvez trouver dans l’onglet « Réseau » de votre navigateur et reproduire directement.
Questions fréquentes
Comment utiliser l'authentification de base avec curl ?
curl -u username:password URL. L'authentification de base est le schéma par défaut de curl ; par conséquent, --basic est superflu, sauf si vous remplacez une méthode précédemment définie. Utilisez toujours HTTPS, car l'authentification de base encode les identifiants en base64 au lieu de les chiffrer.
Comment faire en sorte que curl me demande un mot de passe ?
Indiquez uniquement le nom d’utilisateur : curl -u username URL. curl demande le mot de passe de manière interactive et ne l’affiche pas à l’écran ; ainsi, rien n’est enregistré dans l’historique de votre shell ni dans la liste des processus. C’est la bonne approche pour tout ce qui est saisi manuellement.
L’authentification de base avec curl est-elle sécurisée ?
Uniquement via HTTPS. Les identifiants sont encodés en Base64, ce qui est facilement réversible ; via HTTP en clair, ils sont donc effectivement en texte clair. Via TLS, le transport les protège, et le risque réside alors dans l’endroit où vous les stockez sur votre propre machine.
Comment stocker les identifiants curl dans un fichier ?
Utilisez ~/.netrc avec les lignes machine, login et password, puis exécutez-le avec chmod 600 et appelez curl avec -n. curl n’émet pas d’avertissement en cas de permissions incorrectes ; c’est donc à vous de les configurer. --netrc-file pointe vers un emplacement alternatif, et --netrc-optional évite les échecs lorsqu’aucun fichier n’existe.
Pourquoi curl renvoie-t-il un code 401 alors que mon mot de passe est correct ?
Plusieurs possibilités : le serveur attend un schéma différent — vérifiez l’en-tête WWW-Authenticate —, ou le point de terminaison utilise une connexion par formulaire avec des cookies plutôt qu’une authentification HTTP, ou encore votre nom d’utilisateur contient deux deux-points, ce que -u ne peut pas représenter car il sépare les éléments au premier deux-points.
Quelle est la différence entre 401 et 403 ?
Le code 401 signifie que vous n’êtes pas authentifié : vous n’avez pas fourni d’identifiants, ou ceux-ci sont incorrects. Le code 403 signifie que vous vous êtes authentifié avec succès, mais que vous n’êtes pas autorisé à effectuer cette action. Réessayer avec des identifiants différents résout le premier cas, mais pas le second.
Comment s’authentifier auprès d’un proxy avec curl ?
Utilisez « --proxy-user user:password », qui est distinct de « -u » pour la cible. Un statut 407 signifie que le proxy demande des identifiants ; un 401 signifie que c’est la cible qui les demande. Si votre adresse source est stable, demandez plutôt à votre fournisseur de vous ajouter à une liste blanche d’adresses IP — cela supprime complètement les identifiants de vos commandes.
Que fait l’option --anyauth ?
Elle permet à curl de détecter le schéma préféré du serveur en envoyant une requête et en lisant les en-têtes de réponse, puis en s’authentifiant avec la méthode la plus sécurisée proposée. Le coût est un aller-retour supplémentaire par requête, et le manuel prévient que cela peut échouer lors d’un envoi depuis stdin, car les données peuvent devoir être envoyées deux fois.
Conclusion
La syntaxe est simple : « -u username:password » et vous êtes authentifié, le schéma par défaut de curl étant « basic ». Le point important à bien comprendre concerne l'emplacement du mot de passe.
Le manuel de curl lui-même est très clair à ce sujet : les identifiants « doivent être récupérés à partir d’un fichier ou d’un système similaire et ne doivent jamais être utilisés en clair dans une ligne de commande » — et les risques de fuite auxquels il met en garde sont tous bien réels. L'historique du shell les conserve indéfiniment, les listes de processus les exposent brièvement à toute personne utilisant la machine, et les sorties de terminal copiées-collées ont une capacité étonnante à se retrouver là où vous ne le souhaitiez pas.
Deux habitudes permettent d’y remédier. Pour une utilisation interactive, indiquez uniquement le nom d’utilisateur et laissez curl vous demander le mot de passe. Pour les scripts, placez les identifiants dans ~/.netrc avec chmod 600 et utilisez -n. Aucune de ces deux méthodes ne prend plus de temps que la saisie du mot de passe.
Et ne confondez pas les deux niveaux d’authentification. Une erreur 401 provient de la cible et nécessite -u ; une erreur 407 provient du proxy et nécessite --proxy-user. Si votre adresse est stable, une liste blanche permet de supprimer complètement le deuxième identifiant de vos commandes — ce qui est le seul endroit véritablement sûr où conserver un secret.
