EV.ma et l'Access Token

Logo EV.ma

Avant de commencer le sujet j'aimerais dire que j'ai aimé le code de l'application EV.ma, un vrai travail.

Dans cet article je vais parler de l'access token que l'application utilise et que j'ai découvert en essayant de voir mieux l'application de l'angle sécurité ainsi j'ai effectué un reverse engineering (numéro 1) et une capture puis une analyse des paquets via Wireshark (numéro 2)  :

  1. Le fichier qui nous intéresse ici c'est le fichier API.java où il existe tous les liens vers l'API de EV.ma :

    01:  package com.synergie.media.ev.net;  
    02: public class Api {
    03: public static final String ADD_CATEGORY_URL = "http://projets.synergie-media.com/ev/api/me/add_categories.json";
    04: public static final String ADD_COMMENT_URL = "http://projets.synergie-media.com/ev/api/comments/add.json";
    05: public static final String DISCOVER_URL = "http://projets.synergie-media.com/ev/api/events/discover.json";
    06: public static final String EVENT_ATTENDEES_URL = "http://projets.synergie-media.com/ev/api/events/%s/attendees.json";
    07: public static final String EVENT_ATTEND_URL = "http://projets.synergie-media.com/ev/api/events/%s/attend.json";
    08: public static final String EVENT_CHECKIN_URL = "http://projets.synergie-media.com/ev/api/events/%s/checkin.json";
    09: public static final String EVENT_CHECKOUT_URL = "http://projets.synergie-media.com/ev/api/events/%s/checkout.json";
    10: public static final String EVENT_COMMENTS_URL = "http://projets.synergie-media.com/ev/api/events/%s/comments.json";
    11: public static final String EVENT_HIDE_URL = "http://projets.synergie-media.com/ev/api/events/%s/hide.json";
    12: public static final String EVENT_INVITED_USERS_URL = "http://projets.synergie-media.com/ev/api/events/%s/invitations.json";
    13: public static final String EVENT_ORGANIZER_URL = "http://projets.synergie-media.com/ev/api/organizers/%s.json";
    14: public static final String EVENT_PIN_URL = "http://projets.synergie-media.com/ev/api/events/%s/pin.json";
    15: public static final String EVENT_SINGLE_URL = "http://projets.synergie-media.com/ev/api/events/%s.json";
    16: public static final String EVENT_SPEAKERS_URL = "http://projets.synergie-media.com/ev/api/events/%s/speakers.json";
    17: public static final String EVENT_UNATTEND_URL = "http://projets.synergie-media.com/ev/api/events/%s/ignore.json";
    18: public static final String EVENT_UNPIN_URL = "http://projets.synergie-media.com/ev/api/events/%s/unpin.json";
    19: public static final String EVENT_USER_URL = "http://projets.synergie-media.com/ev/api/users/%s.json";
    20: public static final String EXPLORE_FRIENDS_URL = "http://projets.synergie-media.com/ev/api/users/search/friend.json";
    21: public static final String EXPLORE_ORGANIZERS_URL = "http://projets.synergie-media.com/ev/api/organizers/search.json";
    22: public static final String EXPLORE_SPEAKERS_URL = "http://projets.synergie-media.com/ev/api/users/search/speaker.json";
    23: public static final String EXPLORE_URL = "http://projets.synergie-media.com/ev/api/events/search.json";
    24: public static final String EXPLORE_USERS_URL = "http://projets.synergie-media.com/ev/api/users/search/user.json";
    25: public static final String FEED_BACK_URL = "http://projets.synergie-media.com/ev/web/feedback";
    26: public static final String GCM_GET_REGID_URL = "http://projets.synergie-media.com/ev/api/me/gcmid.json";
    27: public static final String GCM_SEND_REGID_URL = "http://projets.synergie-media.com/ev/api/me/gcmid.json";
    28: public static final String GET_BLOCKED_USERS = "http://projets.synergie-media.com/ev/api/me/blocked/user.json";
    29: public static final String GET_CITIES_URL = "http://projets.synergie-media.com/ev/api/cities/list.json";
    30: public static final String GET_MY_FEED_URL = "http://projets.synergie-media.com/ev/api/me/feed.json";
    31: public static final String GET_MY_TOKENS_URL = "http://projets.synergie-media.com/ev/api/me/providers.json";
    32: public static final String GET_NOTIFICATIONS_URL = "http://projets.synergie-media.com/ev/api/me/notifications.json";
    33: public static final String GET_ORGANIZER_ACTIVITY_URL = "http://projets.synergie-media.com/ev/api/organizers/%s/feed.json";
    34: public static final String GET_USER_ACTIVITY_URL = "http://projets.synergie-media.com/ev/api/users/%s/feed.json";
    35: public static final String GET_VENUE_URL = "http://projets.synergie-media.com/ev/api/venues/%s.json";
    36: public static final String HELP_URL = "http://projets.synergie-media.com/ev/web/help?";
    37: public static final String MARK_NOTIFICATION_AS_READ_URL = "http://projets.synergie-media.com/ev/api/notifications/mark_as_seen.json";
    38: public static final String ME_URL = "http://projets.synergie-media.com/ev/api/me.json";
    39: public static final String MY_PLANS_URL = "http://projets.synergie-media.com/ev/api/me/plans.json";
    40: public static final String NEARBY_EVENTS_URL = "http://projets.synergie-media.com/ev/api/events/near_me.json";
    41: public static final String ORGANIZER_FOLLOWERS_URL = "http://projets.synergie-media.com/ev/api/organizers/%s/followers.json";
    42: public static final String ORGANIZER_FOLLOW_URL = "http://projets.synergie-media.com/ev/api/organizers/%s/follow.json";
    43: public static final String ORGANIZER_UNFOLLOW_URL = "http://projets.synergie-media.com/ev/api/organizers/%s/unfollow.json";
    44: public static final String POPULAR_EVENTS_URL = "http://projets.synergie-media.com/ev/api/events/popular.json";
    45: public static final String POST_ADD_PROVIDER_CREDENTIELS = "http://projets.synergie-media.com/ev/api/me/providers/%s.json";
    46: public static final String REMOTE_URL_BASE = "http://projets.synergie-media.com/ev/api/";
    47: public static final String SEND_INVITE_URL = "http://projets.synergie-media.com/ev/api/events/%s/invit.json";
    48: public static final String SHARE_ATTENDING_URL = "http://projets.synergie-media.com/ev/api/shares/add.json";
    49: public static final String SINGIN_URL = "http://projets.synergie-media.com/ev/api/signin.json";
    50: public static final String SINGUP_URL = "http://projets.synergie-media.com/ev/api/signup.json";
    51: public static final String UPDATE_ME_URL = "http://projets.synergie-media.com/ev/api/me/edit.json";
    52: public static final String UPDATE_PASSWORD_URL = "http://projets.synergie-media.com/ev/api/me/password.json";
    53: public static final String UPLOAD_AVATAR_URL = "http://projets.synergie-media.com/ev/api/me/change_avatar.json";
    54: public static final String USER_BLOCK_URL = "http://projets.synergie-media.com/ev/api/users/%s/hide.json";
    55: public static final String USER_CATEGORIES_URL = "http://projets.synergie-media.com/ev/api/me/categories.json";
    56: public static final String USER_FIND_FRIENDS_URL = "http://projets.synergie-media.com/ev/api/me/find_friends/%s.json";
    57: public static final String USER_FOLLOWERS_URL = "http://projets.synergie-media.com/ev/api/users/%s/followers.json";
    58: public static final String USER_FOLLOWING_URL = "http://projets.synergie-media.com/ev/api/users/%s/followings.json";
    59: public static final String USER_FOLLOW_URL = "http://projets.synergie-media.com/ev/api/users/%s/follow.json";
    60: public static final String USER_UNBLOCK_URL = "http://projets.synergie-media.com/ev/api/users/%s/unhide.json";
    61: public static final String USER_UNFOLLOW_URL = "http://projets.synergie-media.com/ev/api/users/%s/unfollow.json";
    62: }

  2. Capture de deux requêtes :
Requête pour établir une connexion et avoir l'Access token

Requête pour écrire un commentaire

    Conclusion :

    Une simple attaque de l'homme du milieu "Man-in-the-middle attack" fera l'affaire et ce qui va nous permettre d'avoir l'access token ainsi manipuler l'API et envoyer des requêtes avec l'Access token du client piraté.
    Exemple avec mon access token que j'ai récupéré avec Curl:

    Requête pour se connecter et avoir nos informations avec l'Access token

    Requête pour visualiser les événements avec notre Access token

     Solution :
    Je pense que l’utilisation d'une "SSL/TLS Mutual Authentication" va résoudre se problème, car avec nous allons établir une connexion sécurisée.


    Liens utiles :

    Commentaires

    1. Merci Saad pour tes conseils. C'est effectivement l'objectif... Une fois la version bêta publique lancée. Tu n'as qu'à voir les chemins pour constater. ;-)

      NB : encore d'avantage avec l'histoire de Heartbleed

      RépondreSupprimer
      Réponses
      1. Merci à toi également pour cette super application :) .
        Je vais également revoir les chemins :P

        Supprimer
    2. Bonjour Saad, je suis curieux de voir comment "en envoyant un faux formulaire aux clients ce qui va nous permettre d'avoir l'access token". Peux tu élaborer ce point stp?

      RépondreSupprimer
      Réponses
      1. Voir l'explication de @IDRISSI Mohamed et la prochaine fois je vais essayer de bien expliquer le truc ;)

        Supprimer
    3. C'est dangereux de mettre son access_token en clair (je parle de la deuxième capture wireshark).
      Sinon, @Khalid Khalifa: imagine une partie tiers écoute les transferts réseau (un sniffer), mais qui se présente sur le réseau comme un proxy (Le principe du Man-in-the-middle que Saad a mentionné sur son article). Cette partie tiers peut modifier la requête en utilisant le même access_token de l'utilisateur qui a envoyé la requête! et il peut accéder à des données sensibles de l'utilisateur, voire même supprimer ses données...

      RépondreSupprimer
      Réponses
      1. Merci Mohamed pour la remarque (c'est noté) et pour l'explication (Poke @khalid KHALIFA).

        Supprimer
      2. Le danger du man in the middle est évident. Cette situation peut facilement être évitée en passant par du https et en mettant en place le cryptage de la payload, et un token avec un préfixe en plus de la vérification de la source de la requête. Ma question était surtout concernant le " faux formulaire " qui permettrai de récupérer le token.

        Supprimer
      3. je pense qu'il y'aura un risque d'utilisation de SSLStrip pour passer par un port transparent non sécurisé, je ne sais pas bcp dans ce domaine mais il faut tester.

        Supprimer
    4. Bonjour Saad, personnellement je ne vois aucun problème en ça, si on regarde les endpoints de l'API on se rend compte qu'il s'agit de l'environnement de développement, pourquoi utiliser HTTPS dans un environnement de test? Un Man In The Middle suppose que l'attaquant se trouve dans le même réseau local qui est pour notre cas le réseau d'entreprise qui développe l'application, ils vont sans aucun doute passer en HTTPS une fois sur la prod. Naturellement une entreprise ne vas pas se casser la tête à crypter le trafic en local et payer un certificat, c'est une charge de plus.

      RépondreSupprimer
      Réponses
      1. Merci Mohammed, oui tu as raison et c'est ce que Younes Qassimi a dit dans le premier commentaire ;)
        Pour les autres conseils il faut voir dans le groupe "EV pour Android Bêta Testeurs" sur Google plus.
        Voilà des exemples que j'ai écrit:

        Sous le système Android il est possible de vérifier si notre application a été installée via Google Play, ainsi de ne pas permettre l'installation que par ce dernier.
        Voilà un code qui permet de faire cette vérification :

        if("com.google.android.feesback".equals(
        getPackageManager().getInstallerPackageName(getPackageName())))
        {
        // Si l’application est installé via Google Play
        return false;
        }

        -------------> Et pour le cas de l'émulateur :

        String android_id = Secure.getString(getContentResolver(),
        Secure.ANDROID_ID);
        if (android_id == null){
        // L’application tourne sous un émulateur, alors il faut l’arrêter.
        }

        Supprimer

    Enregistrer un commentaire