none
Credentials WebClient RRS feed

  • Question

  • Bonjour tout le monde,

    Je cherche à récupérer un contenu sur une page avec authentification.

    Le mot de passe est crypté dans un fichier INI, j'ai pu me connecter avec un webBrowser en déclenchant l'événement du bouton, mais la page retournée est la page principale, le formulaire qui m'intéresse se trouvant dans un iframe.

    Firefox sait afficher le contenu de l'iframe en pleine page, il semblerait que IE non. Et du reste, récupérer la source de l'iframe n'est pas si évident que cela.

    Alors je m'y suis pris comme ça :

        String strURL = api.ini.GetIniString("LitPlaceLibre", "url", "***", 255,
            Application.StartupPath + "\\LitPlaceLibre.ini");
        string strText = "";
        using (System.Net.WebClient client = new System.Net.WebClient())
        {
            client.Credentials = new System.Net.NetworkCredential(strNomBoite, DecryptString(strMDP));
            Stream inStream = client.OpenRead(strURL.Replace("#", ""));
            byte[] content = new byte[48000];
            inStream.Read(content, 0, 48000).ToString();
            strText = System.Text.Encoding.UTF8.GetString(content);
            inStream.Close();
        }
        MessageBox.Show(strText);

    Avant d'arriver au MessageBox je regarde ce qu'il y a dans strText, et je vois qu'il y a pas mal de zéros, je veux dire par là que le contenu est plus court que prévu. Et puis, en regardant de plus près, je m'aperçois que c'est le code de la page d'authentification qui se trouve dans strText, et au demeurant, en exécutant en pas à pas, après l'initialisation de strText je peux aller boire un café et revenir, quand j'affiche le contenu de strText, ça m'affiche une fenêtre IE avec la page d'authentification, un peu comme pour me donner une chance d'obtenir le contenu authentifié, mais ... trop tard.

    Si je dis que webBrowser est connecté, c'est que c'est avec le même nom et le même mot de passe.

    Doit y avoir gourure quelque part, pas vrai ?

    Tout ça pour lire un nombre, sur la page web.




    • Modifié Gloops samedi 2 mai 2015 19:25
    dimanche 12 avril 2015 16:56

Réponses

  • Bonjour,

    Les credentials sont utilisés avec des authentifications Basic ou Digest par exemple.

    Vous avez l'air de parler d'un formulaire de connexion. Donc il me semble que ce vous devez essayez de faire c'est de simuler un envoi de formulaire (verbe POST) et de maintenir les cookies car il y a de fortes chances que votre authentification soit maintenu grâce à un cookie de session.

    Cordialement,


    Yan Grenier

    Merci de bien vouloir "Marquer comme réponse", les réponses qui ont répondues à votre question, et de noter les réponses que vous avez trouvé utiles.

    dimanche 12 avril 2015 18:48

Toutes les réponses

  • Bonjour,

    Les credentials sont utilisés avec des authentifications Basic ou Digest par exemple.

    Vous avez l'air de parler d'un formulaire de connexion. Donc il me semble que ce vous devez essayez de faire c'est de simuler un envoi de formulaire (verbe POST) et de maintenir les cookies car il y a de fortes chances que votre authentification soit maintenu grâce à un cookie de session.

    Cordialement,


    Yan Grenier

    Merci de bien vouloir "Marquer comme réponse", les réponses qui ont répondues à votre question, et de noter les réponses que vous avez trouvé utiles.

    dimanche 12 avril 2015 18:48
  • Mince, ça m'arrangeait que ça soit simple :)

    Merci pour cette réponse rapide.

    Si je comprends bien, je devrais trouver Post là-dedans ?

    https://msdn.microsoft.com/fr-fr/library/system.net.webclient_methods%28v=vs.110%29.aspx


    • Modifié Gloops dimanche 12 avril 2015 19:01
    dimanche 12 avril 2015 19:00
  • Raté :)

    Regardez du coté de UploadValues pour transmettre les valeurs d'un formulaire: https://msdn.microsoft.com/fr-fr/library/9w7b4fz7(v=vs.110).aspx il y a un exemple à la fin de la page.

    Cordialement,


    Yan Grenier

    Merci de bien vouloir "Marquer comme réponse", les réponses qui ont répondues à votre question, et de noter les réponses que vous avez trouvé utiles.

    dimanche 12 avril 2015 19:09
  • Bonjour,

    Merci encore d'être plus réactif que moi.

    Si j'ai bien compris c'est le serveur qui décide le mode de connexion, et dans ce cas de figure je devrais essayer ça :

    StackOverflow : C#, WebClient accessing page with credentials

    lundi 20 avril 2015 09:53
  • Bonjour,

    Non c'est le développeur du site qui décide de quelle authentification on dispose sur son site web.

    Et oui le code du lien correspond typiquement à ce que vous voulez faire.

    Cordialement,


    Yan Grenier

    Merci de bien vouloir "Marquer comme réponse", les réponses qui ont répondues à votre question, et de noter les réponses que vous avez trouvé utiles.

    lundi 20 avril 2015 12:26
  • Bonjour,

    J'ai eu quelques semaines bien chargées, d'où mon silence.

    J'ai pu (tenter de) me connecter avec le CookieAwareWebClient, même si ça a été un peu plus long qu'un copier-coller puisque, utilisant Visual Studio 2005 donc .Net 2, j'étais obligé d'utiliser des variables fortement typées, ce qui n'est pas le cas dans le code proposé.

    Il reste une mauvaise surprise : la chaîne retournée par DownloadString correspond toujours au code de la page de login.

    J'ai essayé en remplaçant "username" et "password" par les attributs name, puis id, des champs de saisie correspondants. Même résultat. Je n'ai pas l'impression qu'ajouter une deuxième instruction DownloadString soit la bonne idée.

    Qui dit mieux ?


    • Modifié Gloops mercredi 29 avril 2015 19:31
    mercredi 29 avril 2015 19:28
  • Bonjour,

    En effet avec .Net 2.0 il faut retrouver ces petits :)

    Pour le reste je ne sais pas quoi dire, cette classe http://www.codeproject.com/Articles/624624/Using-a-Cookie-Aware-WebClient-to-Persist-Authenti en 2.0 fonctionne sur un pauvre formulaire chez moi, en revanche avec un formulaire comme celui de Dokuwiki c'est plus compliqué car il y un code de sécurité.

    Voila grosso modo le code que je fais (un peu bourrin mais ca fonctionne):

                using (CookieAwareWebClient web = new CookieAwareWebClient())
                {
                    String dokuUrl = "http://monserveur.com/doku.php";
                    String str = web.DownloadString(dokuUrl+"?id=start&do=login");
                    String prefix = "<input type=\"hidden\" name=\"sectok\" value=\"";
                    int idx = str.IndexOf(prefix);
                    if (idx >= 0)
                    {
                        // Extraction du code
                        int idxEnd = str.IndexOf("\" />", idx);
                        var code = str.Substring(idx + prefix.Length, idxEnd - idx - prefix.Length);
                        // Création des données de formulaire
                        NameValueCollection datas = new NameValueCollection();
                        datas["sectok"] = code;
                        datas["id"] = "start";
                        datas["do"] = "login";
                        datas["u"] = "{USER}";
                        datas["p"] = "{PASSWORD}";
                        // Transmission du "formulaire"
                        web.UploadValues(dokuUrl + "?id=start&do=login", "POST", datas);
                        // Récupération de la page en étant authentifié
                        String html = web.DownloadString(dokuUrl);
                    }
                    else
                    {
                        throw new ApplicationException("Code de sécurité non trouvé");
                    }
                }
    

    Cordialement,


    Yan Grenier

    Merci de bien vouloir "Marquer comme réponse", les réponses qui ont répondues à votre question, et de noter les réponses que vous avez trouvé utiles.

    jeudi 30 avril 2015 09:35
  • Bonjour,

    Pour avoir tout ça de données à passer il doit y avoir une interface de saisie relativement sophistiquée, j'imagine ?

    Pour ma part j'ai un formulaire Login avec une zone de texte Nom de boîte, une zone de texte mot de passe, une case à cocher "se souvenir de moi", et une liste déroulante pour le choix de la langue.

    Dans la source ça s'écrit comme ça :

        <input name="ctl00$MPH$txtUserName" type="text" id="ctl00_MPH_txtUserName" tabindex="1" />
    
        <input name="ctl00$MPH$txtPassword" type="password" id="ctl00_MPH_txtPassword" tabindex="2" />
        <input id="ctl00_MPH_chkAutoLogin" type="checkbox" name="ctl00$MPH$chkAutoLogin" tabindex="3" />
    
            <select name="ctl00$BPH$LanguageList" onchange="javascript:setTimeout(&#39;__doPostBack(\&#39;ctl00$BPH$LanguageList\&#39;,\&#39;\&#39;)&#39;, 0)" id="ctl00_BPH_LanguageList" tabindex="4">
    	<option selected="selected" value="">Use Browser Language</option>
    	<option value="en">English</option>
    	<option value="fr">French</option>
    	<option value="de">German</option>
    	<option value="it">Italian</option>
    	<option value="es">Spanish</option>
    
    </select>

    J'ai fait une nouvelle version de l'application sans le webBrowser, voici le code du bouton :

      using (CookieAwareWebClient client = new CookieAwareWebClient())
      {
          System.Collections.Specialized.NameValueCollection values = new System.Collections.Specialized.NameValueCollection();
          values.Add("#ctl00_MPH_txtUserName", strNomBoite);
          values.Add("#ctl00_MPH_txtPassword", DecryptString(strMDP));
    
          String strURLLog = api.ini.GetIniString("LitPlaceLibre", "urllog", "***", 255,
              Application.StartupPath + "\\LitPlaceLibre.ini");
          String strURL = api.ini.GetIniString("LitPlaceLibre", "url", "***", 255,
              Application.StartupPath + "\\LitPlaceLibre.ini");
          byte[] ret = client.UploadValues(strURLLog, "POST", values);
    
          string reply = client.UploadString(strURL, "POST");
          MessageBox.Show(reply.Length.ToString());
    
          // If the previous call succeeded we now have a valid authentication cookie
          // so we could download the protected page
          string result = client.DownloadString(strURL);
          MessageBox.Show(result.Length.ToString());
      }

    A plusieurs reprises j'ai inscrit le contenu des deux chaînes de retour dans des fichiers texte à l'extension html pour visualiser ça dans un navigateur, afficher la longueur va plus vite pour détecter si il y a du changement, de toute manière j'affiche la chaîne dans la fenêtre d'exécution et je vois bien login loginname ...

    Je précise que j'ai affiché le contenu de strNomBoite et de strMDP, et que ce sont bien les valeurs qui ont permis au webBrowser d'effectuer une connexion authentifiée, juste que je n'ai réussi à en récupérer que le contenu de la page maîtresse, le contenu du ContentPlaceHolder apparaît à l'écran mais pas dans WebBrowser.Document.

    Et alors à toutes fins utiles :

        public class CookieAwareWebClient : WebClient
        {
            public CookieAwareWebClient()
            {
                CookieContainer = new CookieContainer();
            }
    
            private CookieContainer _cookiecontainer;
    
            public CookieContainer CookieContainer 
            { 
                get
                {
                    return _cookiecontainer;
                } 
                private set
                {
                    _cookiecontainer = value;
                }
            }
    
        }
    


    • Modifié Gloops vendredi 1 mai 2015 13:04
    vendredi 1 mai 2015 09:54
  • Ah oui mais là je crois que j'oublie un élément déterminant : faire un test sous IE.

    Alors si je tape dans la barre d'adresse de IE l'adresse de la page de statistiques (celle qui est dans strURL ci-dessus, strURLLog étant comme on peut s'en douter l'adresse de la page de login), ça m'affiche bien entendu la page de login, et une fois que j'ai tapé un nom et un mot de passe corrects, sur la page de courrier entrant ça m'affiche ce message :

    Voulez-vous afficher seulement le contenu sécurisé ?

    Je dois répondre deux fois de suite à ce message, oui ou non peu importe, puis redemander la page de stats en tapant son adresse, et à ce moment je l'obtiens.

    Comment faire la même chose dans WebClient ?

    vendredi 1 mai 2015 13:28
  • A plusieurs reprises j'ai inscrit le contenu des deux chaînes de retour dans des fichiers texte à l'extension html pour visualiser ça dans un navigateur, afficher la longueur va plus vite pour détecter si il y a du changement, de toute manière j'affiche la chaîne dans la fenêtre d'exécution et je vois bien login loginname ...

    Rhoo l'honte ...

    En mode pas à pas, en plaçant le curseur de la souris sur le nom de la variable je vois le début de son contenu apparaître :

    Détail valeur

    Il suffit de cliquer sur la loupe, juste à droite du nom de la variable, pour la faire apparaître avec le visualiseur sélectionné à l'aide de la liste déroulante juste à droite. En l'occurrence, le bon c'est HTML. Et là je vois le contenu sous forme de page web, il n'y a pas à se tromper.

    La copie d'écran n'est pas très propre car il m'a fallu faire un clic droit sur la ligne pour que la touche copie d'écran ne la fasse pas disparaître.

    *

    Ce que je disais au sujet du contenu mixte (sécurisé et non sécurisé) va me poser problème une fois que dans la variable result je vais obtenir la page maîtresse du site, ce qui signifiera que le contenu est obtenu par une connexion authentifiée.

    Jusque là je n'ai réussi à obtenir ceci que dans la version avec un webBrowser. C'est un peu particulier, le webBrowser m'affiche les statistiques une fois que le webClient est authentifié, tout en ouvrant d'ailleurs une fenêtre IE avec la page de login, que je peux d'ailleurs très bien utiliser pour me connecter avec un autre compte. En revanche il ne me retourne pas le contenu des statistiques dans le code de la page (webBrowser.Document), alors qu'il les affiche à l'écran. Le webClient non plus puisque lui ne me retourne que le code de la page d'authentification.

    J'espère réussir à donner les éléments pour que quelqu'un puisse y voir plus clair que moi ?





    • Modifié Gloops vendredi 1 mai 2015 15:41
    vendredi 1 mai 2015 15:15
  • Bonjour,

    Plusieurs choses :

    • Dans votre code CookieAwareWebClient il manque la surcharge de GetWebRequest qui créé la requête.
    • Vous définissez "#ctl00_MPH_txtUserName" alors que le 'name' de votre input est "ctl00$MPH$txtUserName".
    • Apparemment il s'agit d'un site WebForms qui (de mémoire) utilise tout un ensemble de champ "hidden" pour gérer son état. Je ne sais pas dans quelle mesure il peut y avoir un impact dans l'authentification.

    Cordialement,


    Yan Grenier

    Merci de bien vouloir "Marquer comme réponse", les réponses qui ont répondues à votre question, et de noter les réponses que vous avez trouvé utiles.

    samedi 2 mai 2015 07:01
  • Bonjour,

    Merci pour ces précieux indices.

    Effectivement je me rappelle un GetWebRequest que j'ai mis en commentaires, puis supprimé au moment de mettre en ligne, parce qu'il y avait une erreur dedans, et de mémoire  je me suis dit que le Request étant défini ailleurs ça ne devait pas gêner. Apparemment, j'ai dû aller un peu vite en besogne pour dire ça.

    J'étais effectivement hésitant quant à utiliser la propriété name ou id, alors j'ai essayé les deux, mais bien entendu si il manquait quelque chose par ailleurs les deux ont donné le même résultat.

    Effectivement c'est du WebForm, qui conserve les données de session dans des champs hidden. Je crois me rappeler que ça n'apporterait pas grand chose que je les copie ici.

    J'espère que c'est GetWebRequest le point d'achoppement, car si c'est les champs hidden, on n'a pas fini de jouer à cache-cache.

    Je vais consacrer une heure à ça ce soir tard. Si jamais ça venait à ne pas suffire, a priori il est prévu que je reprenne ça Mardi ou Jeudi.

    samedi 2 mai 2015 14:44
  • Alors ce qui a coincé avec le Request au niveau du CookieAwareWebClient est que pour définir un WebRequest je me suis cru bien inspiré de le déclarer de type System.Net.WebRequest, et que du coup il ne connaissait pas le cookie.

    Comme ça la déclaration compile :

            protected override WebRequest GetWebRequest(Uri address)
            {
                System.Net.HttpWebRequest request = (HttpWebRequest)base.GetWebRequest(address);
                request.CookieContainer = CookieContainer;
                return request;
            }
    

    J'ai corrigé aussi ceci :

      System.Collections.Specialized.NameValueCollection values = new System.Collections.Specialized.NameValueCollection();
      values.Add("ctl00$MPH$txtUserName", strNomBoite);
      values.Add("ctl00$MPH$txtPassword", DecryptString(strMDP));
    

    ça me retourne toujours le code de la page de login. Y aurait-il donc à creuser du côté des champs HIDDEN ?

    Mais alors ... Qu'est-ce que j'y cherche ?

    samedi 2 mai 2015 19:00
  • Dans la mesure où des spécificités des sites WebForm ont été évoquées, et qu'une hésitation apparaît dessus, je suis allé demandé un renfort dans le forum aspnetfr, en indiquant l'adresse d'ici ...

    samedi 2 mai 2015 20:00
  • Bonjour, 

    Oui la surcharge de la création de la requête est nécessaire puisque c'est elle qui applique le même conteneur de Cookie à chaque requête et qui permet donc de transporter les cookies à chaque requête.

    Pour le reste, le mieux serait certainement d'intercepter votre requête de login (via Fiddler ou un debugueur Web) pour voir la liste des champs POST transmis, et d'essayer d'émettre exactement le même formulaire en Web Request. C'est ainsi que j'ai du procéder pour tester avec DokuWiki pour comprendre son champ sectok.

    Après je ne connais pas la mécanique de WebForms dans ce domaine (ni en général je ne me suis jamais vraiment servi de cette technologie).

    Cordialement,


    Yan Grenier

    Merci de bien vouloir "Marquer comme réponse", les réponses qui ont répondues à votre question, et de noter les réponses que vous avez trouvé utiles.

    dimanche 3 mai 2015 06:43
  • Bonjour,

    Ah, oui, dans les outils de développement de Firefox c'est l'onglet Réseau. Sous IE il y a eu quelque chose il y a un moment.

    ça promet du sport.

    Merci pour le tuyau.

    dimanche 3 mai 2015 10:42