Documentation technique : Debian
Documentation basée sur la distribution Debian

> > Connexion et authentification par clé publique en PHP

Connexion et authentification par clé publique en PHP

Problématique :

Dans un certain nombre de cas on souhaite pouvoir faire des transferts de données ou exécuter des commandes sur un serveur depuis des scripts PHP situés sur un autre serveur. Par exemple depuis un serveur B ("local"), on veut pouvoir déclencher via une interface web une sauvegarde d’une base de données d’un serveur A ("distant") puis récupérer le fichier de sauvegarde sur B. Dans ce cas il est donc nécessaire de pouvoir établir une connexion SSH entre B et A à l’aide du script PHP qui gère l’interface web de B.

Bien évidemment autant A que B ont une configuration SSH qui n’accepte pas l’authentification par mot de passe "en clair" (cf Sécurisation SSH poussée : authentification par clé RSA) : il va donc être nécessaire d’utiliser une clé publique pour l’établissement de la connexion.

Ensuite, une fois la connexion SSH établie, on peut utiliser des commandes bash sur le serveur "distant", réaliser des transferts de fichiers par SCP (ou SFTP)... le jeu de fonctions PHP disponibles est relativement vaste : fonctions shell2 de PHP, on peut aussi (ré)utiliser des scripts sh.

Pré-requis :

Il est nécessaire d’avoir créé et configuré une paire de clé SSH privée/publique selon la méthode exposée dans Sécurisation SSH poussée : authentification par clé RSA et réparties de la manière suivante :

  • la machine "locale" (B), sur laquelle le script PHP de connexion est placé, doit posséder le fichier de clé privée, stocké dans un répertoire non-accessible via le web (bien sûr !) /home/mon_user/.ssh/id_rsa par exemple, avec des droits les plus restreints possibles (lecture seule obligatoirement...). Elle doit également posséder une copie du fichier de clé publique (id_rsa.pub). Pour simplifier l’explication suivante, supposons qu’elle est située dans le même répertoire que la clé privée : /home/mon_user/.ssh/id_rsa.pub
  • cette machine devra avoir OpenSSH installé et PHP compilé avec la bibliothèque ssh2
  • sur la machine "distante" (A) on aura un utilisateur ayant le minimum de droits nécessaires pour accomplir les opérations requises (user_B par ex pour la suite), lequel aura dans le sous-répertoire .ssh de son répertoire perso le fichier de clé publique : /home/user_B/.ssh/id_rsa.pub. Bien évidemment on aura également intégré cette clé dans la liste des clés autorisées de  /.ssh/authorized_keys avec la commande :
    1. cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
test_ssh.php
fichier complet de connexion SSH

A partir de cette configuration 3 étapes permettent d’interconnecter les 2 machines par un script PHP : (tous les extraits de code suivants sont à placer dans un fichier PHP sur le serveur B)

1re étape : établir la connexion

Pour une machine A ayant comme FQDN "machine.mon-site.tld", et utilisant le port 22 pour le SSH, le script minimum serait :

  1. $connection = ssh2_connect('machine.mon-site.tld', 22, array('hostkey'=>'ssh-dss'));

On peut faire un peu plus "riche" en fournissant un avertissement en cas de problème de connexion :

  1. // Notification à l'utilisateur si le serveur termine la connexion
  2. function ma_deconnexion_ssh($raison, $message, $language) {
  3.     printf("Serveur deconnecte avec l'erreur code [%d] et le message: %s\n",
  4.          $raison, $message);
  5. }
  6. $callbacks = array('disconnect' => 'ma_deconnexion_ssh');
  7.  
  8. $connection = ssh2_connect('machine.mon-site.tld', 22, array('hostkey'=>'ssh-dss'), $callbacks);
  9. if (!$connection) die('Echec de la connexion');

Télécharger

2nde étape : vérifier l’identité de la machine

Si l’on veut vraiment respecter les règles de sécurité, il est maintenant nécessaire de tester si le serveur connecté est bien la machine voulue (histoire d’éviter les attaques du type "Man in the middle"...). Pour cela on va comparer "l’empreinte" retournée par le serveur connecté avec la valeur connue pour la machine A.

Commençons par récupérer cette "empreinte" pour avoir la valeur qui servira de référence par la suite :

  1. $connection = ssh2_connect('machine.mon-site.tld', 22, array('hostkey'=>'ssh-dss'));
  2. echo ssh2_fingerprint($connection, SSH2_FINGERPRINT_MD5 | SSH2_FINGERPRINT_HEX);

Télécharger

L’affichage retourné par le echo correspond donc à la clé de référence du serveur B : par exemple pour la suite 5172E2D339863712D3D26D4860D976CD

On peut donc maintenant ajouter à notre fichier de connexion la vérification de cette clé :

  1. ...
  2. $connection = ssh2_connect('machine.mon-site.tld', 22, array('hostkey'=>'ssh-dss'), $callbacks);
  3. if (!$connection) die('<br />Echec de la connexion');
  4.  
  5. $fingerprint = '5172E2D339863712D3D26D4860D976CD';
  6. if (ssh2_fingerprint($connection, SSH2_FINGERPRINT_MD5 | SSH2_FINGERPRINT_HEX) != $fingerprint)
  7.     die ('<br />problème d\'identification du serveur: la signature reçue ne correspond pas à son empreinte enregistree!') ;

Télécharger

3me étape : authentification par clé publique

Pour réaliser le processus d’authentification on teste maintenant la clé privée du serveur B en fonction de la clé publique (id_rsa.pub que possède A pour authentifier l’utilsateur user_B) :

  1. ...
  2. if (!ssh2_auth_pubkey_file($connection, 'user_B',
  3.                           '/home/mon_user/.ssh/id_rsa.pub',
  4.                           '/home/mon_user/.ssh/id_rsa', 'passphrase_de_la_cle_privee'))
  5.     die("<br>Erreur lors de l'authentification par cle publique");

Télécharger

Et ensuite ?

Une fois la connexion SSH établie sur A par le script PHP on peut :

  • soit échanger des fichiers par le protocle SCP (ou SFTP) :
    1. // envoyer un fichier truc.local de B vers A (truc.distant)
    2. ssh2_scp_send($connection, 'truc.local', 'tmp/truc.distant', 0644);
    3.  
    4. // télécharger un fichier machin.distant depuis A vers B (machin.local)
    5. ssh2_scp_recv($connection, 'tmp/machin.distant', 'machin.local');

    Télécharger

  • soit exécuter des commandes sh sur le serveur A avec :
    1. $commande = 'ls -l';
    2. //exécution commande shell sur la machine A
    3. if (!$flux = ssh2_exec($connection, $commande))
    4.     echo "<br />erreur d'execution de la commande $commande";

    Télécharger

    Si l’on veut récupérer les sorties des commandes passées (retours et/ou erreurs) http://linux.jpvweb.com/mesrecettes... propose une méthode élégante pour le faire...

  • Auteur :
  • Publié le :
  • Mis à jour : 10/02/18

Aucun commentaire


Qui êtes-vous ?
[Se connecter]
Ajoutez votre commentaire ici

Ce champ accepte les raccourcis SPIP {{gras}} {italique} -*liste [texte->url] <quote> <code> et le code HTML <q> <del> <ins>. Pour créer des paragraphes, laissez simplement des lignes vides.