Forum Programmation.php Accès réservé à des fichiers vendus

Posté par .
Tags : aucun
0
19
août
2009
Bonjour à tous,

En train de construire un petit site web pour vendre de la musique en ligne, je suis confronté à un problème.

Tout d'abord, les contraintes:
- site multi-plateforme et multi-os (Apache/IIS, Linux/Windows);
- pas de cookie utilisateur.

Les fichiers musicaux vendus sont situés dans un répertoire fichiers/, répertoire étant protégé (-Indexes, deny from all) pour éviter le téléchargement direct, bien évidemment.

La procédure désirée est la suivante:

1. Un client (ayant déjà payé) va sur le site, rentre ses identifiants dans une page html.

2.Cette page redirige vers une page php qui va, selon les identifiants rentrés, afficher une liste des fichiers situés dans le répertoire fichiers/ accessibles au téléchargement. Cette liste est un ensemble de liens <[a]>.

3. Le client clique sur l'un des liens pour télécharger le fichier. Comme le répertoire fichiers/ est protégé, le téléchargement ne marche pas.

4. J'ai eu l'idée alors de changer la cible des liens <[a]> vers une autre page (push.php) qui selon un paramètre passé dans l'URL dans l'attribut href (méthode GET), va chercher le fichier musical en question et l'envoie vers le navigateur (via la fonction header(), en utilisant Content-Disposition) qui n'a alors plus qu'à le sauvegarder. Comme push.php est situé sur le serveur, il a librement le droit de lire les fichiers musicaux situés dans l'emplacement protégé.

Le problème, c'est que push.php n'est pas protégé en lui-même, donc quelqu'un qui irait directement sur le fichier en passant le bon paramètre dans l'URL pourrait avoir accès aux fichiers. J'ai pensé à une protection qui vérifierait HTTP Referer, mais cela s'usurpe trop facilement.

Quelqu'un aurait-il un début de solution pour moi?
  • # Bien sûr ...

    Posté par . Évalué à 10.

    ... à quelle adresse on envoie le devis ?
  • # SID

    Posté par . Évalué à 6.

    En php, un identifiant de session peut-être propagé par un cookie ou par l'URL via get. session.use_trans_sid et SID (la constante php, pas la branche Debian) sont tes amis.

    Remarques :
    - pourquoi rechercher une solution fonctionnant indifféremment sous Apache et IIS ? Si tu cherches à construire un petit site web, et non une solution universelle qui concurrencera iTunes, il me semble indispensable de définir en amont l'environnement de production. Si ton projet doit réellement pouvoir être déployé indifféremment sous Apache et IIS, prends-tu bien en compte les différences entre les deux serveurs ? -Indexes, deny from all sonne pour moi comme une directive Apache... mais je ne connais pas IIS :)
    - ta contrainte sur l'absence de cookie utilisateur est-elle justifiée ? Le passage d'un id de session via l'URL, quoique possible, ne me semble pas une solution propre.
    - Sous Apache, l'utilisation de mod_rewrite te permettra d'utiliser une URL comme mon_site_de_zik.com/download/mon_fichier.mp3, plus propre que mon_site_de_zik.com/push.php?file=mon_fichier.mp3
    - que se passe-t-il si l'utilisateur modifie l'URL à la main, genre push.php?file=index.php ?
    - enfin, un conseil, révise le chapitre sessions de la doc php :)
    • [^] # Re: SID

      Posté par . Évalué à 1.

      Alors, dans l'ordre:

      - je développe sur mon ordi (Linux, Apache), mais le serveur de production est lui en Windows/IIS. Je peux cependant répliquer les fonctionnalités .htaccess avec d'autres commandes.
      - oui, pas de cookie pour éviter les problèmes de sécurité et pour éviter d'alourdir le tout.
      - pas de mod_apache possible, donc non.
      - il peut accécder à d'autres fichiers sans en avoir l'autorisation (mon problème!)
      - PAS DE COOKIE !
      • [^] # Re: SID

        Posté par . Évalué à 4.

        Il peut accéder à d'autres fichiers sans en avoir l'autorisation.
        Deux solutions qui peuvent être combinées :
        - la valeur de ta variable (le nom du fichier téléchargé) doit correspondre à un motif de regex tel que, par exemple, tout premier caractère soit une lettre ou un chiffre et les derniers caractères .ogg
        - la valeur de ta variable doit également être un nom de fichier musical enregistré dans la table correspondante de la BDD

        PAS DE COOKIE !
        Oui, j'ai bien compris, encore qu'à cette heure j'en grignoterai bien un. Le message que j'ai essayé de faire passer, c''est qu'en php, il existe de méthodes de propagation d'un id de session :
        - première méthode, celle interdite : un cookie sur le poste client
        - seconde méthode, ma proposition dans mon poste précédent : l'id est propagé via la constante SID, tous tes liens internes deviennent : href="mon_lien.php?sessionid=mon_id_de_session_kinépadanzincookie"
        • [^] # Re: SID

          Posté par . Évalué à 2.

          - la valeur de ta variable (le nom du fichier téléchargé) doit correspondre à un motif de regex tel que, par exemple, tout premier caractère soit une lettre ou un chiffre et les derniers caractères .ogg

          C'est sur quoi je m'oriente, cela me semble plus propre, même si la sécurité de ce genre de système n'est pas des plus élevée (bien que dans mon cas, au regard de la popularité du site client, cela ne posera vraisemblablement pas de problème). Je pense insérer un code unique dans chaque lien qui sera vérifié par push.php.
          • [^] # Re: SID

            Posté par . Évalué à 5.

            Moi, j'aimerai bien comprendre en quoi l'usage d'un cookie est moins sécurisé qu'une transmission du sid par l'URL.

            Globalement, que reproches-tu à la sécurité des cookies ?

            Et puis, je ne saisis pas non plus de quelle lourdeur tu parles en utilisant des cookies ?

            A lire ce sujet, j'ai l'étrange sensation que tu as de nombreux préjugers sur des choix techniques à la base de nombreuses applications Web y compris commerciales, certaines ont des failles d'autres pas, mais je doute que ce soit lié à l'usage ou non de cookies.

            Quant à la sécurité que tu prétends vouloir meilleure que dans les applications existantes, je pense que réinventer la roue à ta sauce en évitant de t'inspirer de solutions dont les auteurs se sont déjà confrontés à ce problème est une hérésie.

            En me relisant, je constate que je ne t'apporte aucune réponse si ce n'est celle de t'inciter à remettre en question tes idées reçues sur les solutions techniques que tu décries (du verbe décrier) et les produits dont tu pourrais t'inspirer.

            Si vous n'aimez pas ce commentaire c'est qu'il est ironique.

            • [^] # Re: SID

              Posté par . Évalué à 1.

              La sécurité intrinsèque des cookies, ça ne me dérange pas. C'est plutôt les utilisateurs qui m'inquiètent. De plus, dans mon cadre précis, ça ne conviendra pas (raison technique). Enfin, je n'ai pas envie d'avoir à m'occuper d'un cookie session; si j'ai les moyens de faire simple (et je les ai trouvé), autant faire simple (j'arrive à tout caser dans des GET et des POST et c'est assez sécurisé).

              Enfin, pour les solutions déjà existantes, c'est non. Trop de fonctionnalités, trop de choses à maintenir, des potentialités de failles... d'autant plus qu'une partie de la maintenance sera faite par mon client (un ami) qui n'y connaît pas grand-chose et auquel je dois tout expliquer, donc je dois garder ça au plus simple possible.
              • [^] # Re: SID

                Posté par . Évalué à 5.

                Soit je me suis mal exprimé, soit on ne s'est pas compris mais je me permets d'insister.

                Je ne vois pas en quoi l'usage de SID dans l'URL avec des GET et des POST est plus fiable qu'un cookie, sachant que c'est bien plus simple à forger pour les utilisateurs lambda.
                Ton cadre technique restant suffisamment flou, il est vrai qu'il sera difficile de te contredire sur ton choix.

                A noter que la gestion des sessions/cookie en PHP est, pour ainsi dire, native contrairement à l'usine à gaz de la voie que tu sembles prendre et considérer comme simple.

                Enfin, sur les solutions existantes, si tu as lu en détail, je parlais de t'en inspirer pour ne pas réinventer la roue alors que ces patterns ont sûrement déjà été traités par d'autres mais c'est vrai qu'il est toujours plus simple de considérer que notre propre cas ne rentre pas dans les cases et nécessite donc une solution bien spécifique.
                L'avantage d'une telle décision étant de pouvoir toujours innover y compris dans les failles de sécurité desquelles tu tiens tant à te préserver :-)

                Il y'a au moins un point positif, tu te seras fait la main sur une application non critique et tu apprendras de tes erreurs.

                Bon courage à ton client et ami.

                Si vous n'aimez pas ce commentaire c'est qu'il est ironique.

                • [^] # Re: SID

                  Posté par . Évalué à 3.

                  come to the dark side, we have cookies !

                  Il ne faut pas décorner les boeufs avant d'avoir semé le vent

                • [^] # Re: SID

                  Posté par . Évalué à 2.

                  Mais qui a dit que j'utiliserais un SID dans GET ou POST ?

                  En fait, j'ai bien résolu le problème grâce à une identification en deux étapes (identification + vérification au début du téléchargement), et c'est vraiment bien plus léger que de s'occuper d'un cookie, qui présente certes bien plus de fonctionnalités, mais assez inutiles dans mon cas.

                  Pour le cadre technique, sans donner trop de précision, certains acheteurs vont être amenés à posséder plusieurs comptes, et je sais déjà que certains vont s'emmeler les pinceaux et que leur navigateur (IE6 pour ne pas le nommer) va retenir des cookies valides pour une ancienne session (bref, problèmes en vue).

                  Pour les solutions existantes, c'est vraiment non. Je ne pense pas pouvoir maîtriser ce code étranger assez vite, et pour mon ami, je dois garder ça le plus simple possible. Crois-moi, c'est vraiment la meilleure solution pour lui.

                  Et pour mes propres failles, à l'heure actuelle, je n'en vois plus qu'une: un mot de passe est transmis en clair d'une page à l'autre, il faut que je trouve le moyen pour ne transférer que la signature MD5 et ce sera bon. J'ai isolé tous les champs utilisateurs pour éviter les injections PHP. Les mots de passe sont générés aléatoirement, et en l'absence de cookie, une session n'est pas usurpable par un autre. L'avantage de la simplicité !
                  • [^] # Re: SID

                    Posté par . Évalué à 2.

                    un cookie peut :
                    - avoir une durée de vie limitée (1h ?)
                    - etre effacer quand la personne clique sur "logout"
                    • [^] # Re: SID

                      Posté par . Évalué à 3.

                      etre effacer quand la personne clique sur "logout"

                      C'est vraiment rare pour certaines personnes, pour la plupart, surtout si elles sont sur leur ordinateur personnel.

                      Je ne sais pas si on peut limiter la durée de vie d'un cookie à une session. C'est possible (sans que cela dépende de la configuration du navigateur).

                      A bientôt
                      Grégoire
                      • [^] # Re: SID

                        Posté par . Évalué à 3.

                        Sauf erreur, lorsque tu envoies un cookie sans préciser de date d'expiration, celui-ci expire à la fin de la session, lorsque le navigateur est fermé.
                  • [^] # Re: SID

                    Posté par (page perso) . Évalué à 3.

                    En fait, j'ai bien résolu le problème grâce à une identification en deux étapes (identification + vérification au début du téléchargement), et c'est vraiment bien plus léger que de s'occuper d'un cookie, qui présente certes bien plus de fonctionnalités, mais assez inutiles dans mon cas.

                    J'aimerais bien savoir en quoi c'est plus léger qu'un :

                    if (isset($_SESSION['login']) && $_SESSION['login'] === true) { }


                    De plus tu peux très bien utiliser les sessions sans cookies en passant le SID dans l'URL et en forçant l'utilisation du SID en paramètre (en fixant session.use_cookies à 0, session.use_only_cookies à 0 et session.use_trans_sid à 1 via la fonction ini_set())
                    Ce code fonctionne parfaitement sans acceptation des cookies par le client (et en plus il ajoute automatiquement le SID dans l'URL


                    ini_set('session.use_cookies', 0);
                    ini_set('session.use_only_cookies', 0);
                    ini_set('session.use_trans_sid', 1);
                    session_start();

                    if (isset($_SESSION['id']) === false) {
                    echo 'pas de session';
                    $_SESSION['id'] = 42;
                    } else {
                    echo $_SESSION['id'];
                    $_SESSION['id'] = 1;
                    }
                    <a href='./test.php>lien


                    Enfin pour finir avec les sessions, si tu initialise bien ta session lors d'une connexion, tu n'auras aucun problème de cache et tes utilisateurs pourront se connecter avec plusieurs login sans que cela ne pose de problème.

                    Au moins avec cette solution, pour peu que tu sécurises un peu la session, tu n'auras plus de problème avec l'identification.

                    Ensuite, pour les fichiers que l'utilisateur peut télécharger, il suffit de les garder en session pour vérification avant d'envoyer le fichier demandé avec ta page php


                    ini_set('session.use_cookies', 0);
                    ini_set('session.use_only_cookies', 0);
                    ini_set('session.use_trans_sid', 1);
                    session_start();

                    // Dans la page de login
                    // Si le login est réussi
                    // on stocke les fichiers autorise
                    // dans la session
                    if ($login === true) {
                    $files = file_get_contents('./fichier_pour_l_utilisateur_hors_repertoire_web_avec_fichier_autorise');
                    $_SESSION['files'] = explode(';', $files);
                    $_SESSION['login'] = true;
                    } else {
                    unset($_SESSION['files']);
                    unset($_SESSION['login']);
                    }

                    // Dans la page de telechargement
                    // On vérifie que l'utilisateur est connecté
                    if (isset($_SESSION['login']) === false || $_SESSION['login'] !== true) {
                    // l'utilisateur n'est pas connecté
                    // on le renvoie sur la page de login
                    header('Location: login.php');
                    exit();
                    } else {
                    // l'utilisateur est connecté
                    // on vérifie que le fichier demandé
                    // fait parti des fichiers autorisés
                    if (in_array($_GET['music'], $_SESSION['files']) === true) {
                    // le fichier est bin, on l'envoie
                    // code d'envoie du fichier avec les headers
                    } else {
                    // le fichier ne fait pas parti de la liste
                    // on l'indique à l'utilisateur
                    echo 'fichier interdit....';
                    }
                    }
                  • [^] # Re: SID

                    Posté par . Évalué à 2.

                    Que la méthode Coué soit avec toi :-p

                    Allez, on y croit ... ça va marcher...

                    Si vous n'aimez pas ce commentaire c'est qu'il est ironique.

                  • [^] # Re: SID

                    Posté par . Évalué à 4.

                    Franchement, cite moi aujourd'hui un grand site qui gère des utilisateurs et n'utilise pas de cookies ...

                    il faut que je trouve le moyen pour ne transférer que la signature MD5 et ce sera bon
                    Une replay attack, ça te dit quelques chose ? Faire la sécurité soit même et croire que c'est bon parce qu'"on" a pensé à tout, c'est un mauvais départ ...
  • # Impossible

    Posté par (page perso) . Évalué à 6.

    Du fait même de la façon dont HTTP fonctionne (en mode déconnecté), c'est impossible de savoir si celui qui appelle push.php vient réellement de ta page de liens.

    Le mieux que tu pourras faire, c'est probablement générer des liens jetables, qu'on ne pourra cliquer qu'une fois, par exemple en créant un id de lien unique (et pas facilement devinable) quand tu génères la liste des fichiers et en les enregistrant dans une base mysql : quand on appelle push,php avec le bon id, ça supprime le lien de la bdd et ça envoie le fichier.

    Tu peux éventuellement également empêcher quelqu'un de cliquer sur le lien si son IP n'est pas celle de celui qui a généré la liste, mais cela risque d'interférer avec l'utilisation d'un proxy.
    • [^] # Re: Impossible

      Posté par (page perso) . Évalué à 3.

      Le lien jetable me semble effectivement le plus viable mais je ne suis pas sûr que limiter au premier clique soit une bonne idée. Peut-être limiter à 5 clics et 30 minutes (le premier des deux) ou quelque chose comme ça. Si tu as accés à des logs d'un site similaire (version précédente par exemple), tu dois pouvoir les analyser pour régler les limites au mieux.

      pertinent adj. Approprié : qui se rapporte exactement à ce dont il est question.

      • [^] # Re: Impossible

        Posté par . Évalué à 3.

        Merci pour l'info, c'est bien ce que je pensais.

        Pour les liens temporaires, j'y avais pensé, malheureusement, étant limité par la plate-forme de production (pas de liens symboliques, pas de module spécifique à Apache), j'avais abandonné l'idée. Il faudra vraisemblablement que je creuse l'idée.
  • # php/mysql login/password

    Posté par . Évalué à 5.

    et en faisant une base de données des fichiers
    et un liste d'ID autorisé/autorisant le telechargement ?

    1°) l'utilisateur d'identifie (on verifie qu'il a un compte)
    2°) on recupere la liste des fichiers auquels il peut pretendre
    (table contenant des couples id_client, id_morceau)

    3°) tu generes une page contenant cette liste avec les liens qui vont bien

    4°) dans le lien tu reverifies son login/pass (methode get, sessionid...)
    bref tu utilises les fonctionnalités de gestion de session que propose apache/php

    5°) avantage de ce systeme ?
    un morceau peut-etre mis à disposition de plusieurs clients
    • [^] # Re: php/mysql login/password

      Posté par . Évalué à 2.

      C'est actuellement ce que je fais, sans la deuxième phase de vérification login/pass. Mais ça ne passera pas car session utilise un cookie que je n'ai pas envie d'utiliser pour éviter d'alourdir la situation.
      • [^] # Re: php/mysql login/password

        Posté par . Évalué à 4.

        une valeur de session que tu passerais dans l'URL

        1°) login => OK => generation d'une valeur aleatoire pour cette session
        2°) cette valeur est passée dans toutes les pages d'apres car c'est elle qui va faire que le lien login/pass et droit d'acces


        => pas de cookies (bien que ce soit fait pour ca)
        => tu t'assures que l'utilisateur de la page precedente est bien identifié

        mais il me semble que les sessions PHP n'ont pas besoin de cookies pour fonctionner
        • [^] # Re: php/mysql login/password

          Posté par . Évalué à 2.

          mais il me semble que les sessions PHP n'ont pas besoin de cookies pour fonctionner

          Hélas, si (en tout cas, c'est le cas chez moi).

          Globalement, j'ai ajouté un paramètre GET supplémentaire dans l'URL qui permet d'authentifier un tant soi peu le téléchargement.
  • # pourquoi réinventer la roue?

    Posté par . Évalué à 1.

    Bonjour,

    la plupart des logiciels de vente en ligne le font (et d'autres choses).

    Alors, il te suffit d'installer ces logiciels; de les tester, et de garder celui qui te plaît le mieux.

    Ensuite, tu adaptes la charte graphique à ton image et voilà.

    A bientôt
    Grégoire
    • [^] # Re: pourquoi réinventer la roue?

      Posté par . Évalué à 3.

      Et la plupart de ces solutions sont bien plus lourdes, génèrent un code HTML dégueulasse (alors que le mien est 100% valide), sont bourrées de failles de sécurité, ne sont pas libres ni même gratuites... Poubelle directement
      • [^] # Re: pourquoi réinventer la roue?

        Posté par (page perso) . Évalué à 4.

        Que de défauts ;-)
        Notes tout de même qu'elles ont un avantage par rapport à ta solution: ça fonctionne.
        C'est un tout petit avantage, mais bon des fois c'est suffisant.
      • [^] # Re: pourquoi réinventer la roue?

        Posté par . Évalué à 3.

        Bonsoir,

        génèrent un code HTML dégueulasse (alors que le mien est 100% valide)

        C'est sur que si tu ne regardes que OScommerce 1, tu peux être déçu.

        , sont bourrées de failles de sécurité,

        Il n'y a pas que OScommerce... tiens, tu peux regarder la qualité du code source de Thelia, et il existe même un plugin pour Spip pour que Spip s'interface à Thélia.

        ne sont pas libres ni même gratuites... Poubelle directement

        Oui, si c'est pas libre, même pas la peine d'en parler, mais visiblement tu n'a pas essayé les solutions libres existantes.

        J'attends de voir ce que tu ferras, ça donnera un code HTML propre, et 100% valide, sans aucune faille de sécurité.

        Bonne soirée.
        Grégoire
        • [^] # Re: pourquoi réinventer la roue?

          Posté par . Évalué à 2.

          Je viens de regarder Thelia, et c'est garanti que ça ne me conviendra pas, car je ne ferais pas la maintenance du site ad vitam eternam, et pour mon commanditaire, ça ne le fera pas. Je n'utilise même pas mysql, c'est dire (trop compliqué pour le site, et fonctionnalités apportées non nécessaires). De toute façon, je n'aurai jamais eu la liberté que j'ai dans l'aspect graphique général en utilisant un modèle, quoiqu'on en dise.
          • [^] # Re: pourquoi réinventer la roue?

            Posté par . Évalué à 3.

            Je n'utilise même pas mysql

            Bonjour,

            est ce que tu va utiliser un autre système de base de donnée? genre sqlite, ou des fichiers simples?

            C'est vrai que sans base de donnée, beaucoup de choses sont simplifiées. (installation, migrations, sauvegardes, sécurité...).

            A bientôt
            Grégoire
            • [^] # Re: pourquoi réinventer la roue?

              Posté par . Évalué à 2.

              De simples fichiers csv, dont l'accès est restreint aux utilisateurs de la machine locale et compte ftp uniquement. Tout autre accès est prohibé. Et même si ca tourne mal, les mots de passe ne sont évidemmenbt pas en clair de toute façon.
              • [^] # Re: pourquoi réinventer la roue?

                Posté par . Évalué à 5.

                Rassure-moi, avec tous les anti-patterns logiciels mis en oeuvre délibérément, tu n'envisages pas de faire carrière dans le développement ?

                Si vous n'aimez pas ce commentaire c'est qu'il est ironique.

  • # Bon, vous avez gagné...

    Posté par . Évalué à 3.

    Je vais essayer d'utiliser une session SID sans cookie (IE6 powaaa, testé et vérifié), même si d'après ce que j'ai vu, ça ne fera que transposer mes paramètres GET dans SESSION. Avec un peu de chance, ça va légèrement me simplifier la tâche pour l'autorisation sur les téléchargements, même si de ce côté-là, tout était assez clair (identification initiale + vérification à la demande).

    Au moins un avantage de SID, c'est que je pourrai fusionner les sessions anonymes et les utilisateurs enregistrés.

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n'en sommes pas responsables.