web-dev-qa-db-fra.com

Utiliser PHP/Apache pour restreindre l'accès aux fichiers statiques (html, css, img, etc.)

Disons que vous avez beaucoup de fichiers html, css, js, img, etc. dans un répertoire sur votre serveur. Normalement, tout utilisateur d’Internet-land peut accéder à ces fichiers en tapant simplement l’URL complète, comme suit: http://example.com/static-files/sub/index.html

Maintenant, que se passe-t-il si vous voulez uniquement que les utilisateurs autorisés puissent charger ces fichiers? Pour cet exemple, supposons que vos utilisateurs se connectent d’abord à partir d’une URL telle que celle-ci: http://example.com/login.php

Comment autoriseriez-vous l'utilisateur connecté à afficher le fichier index.html (ou l'un des fichiers sous "fichiers statiques"), tout en le limitant à tout le monde?

J'ai proposé deux solutions possibles jusqu'à présent:

Solution 1
Créez le fichier .htaccess suivant sous "static-files":

Options +FollowSymLinks  
RewriteEngine on  
RewriteRule ^(.*)$ ../authorize.php?file=$1 [NC]

Et puis dans authorize.php ...

if (isLoggedInUser()) readfile('static-files/'.$_REQUEST['file']);
else echo 'denied';

Ce fichier authorize.php est extrêmement simplifié, mais vous en avez l’idée.

Solution 2
Créez le fichier .htaccess suivant sous "static-files":

Order Deny,Allow
Deny from all
Allow from 000.000.000.000

Et ensuite, ma page de connexion pourrait ajouter ce fichier .htaccess à une adresse IP pour chaque utilisateur qui se connecte. De toute évidence, une telle sorte de routine de nettoyage serait également nécessaire pour vider les adresses IP anciennes ou inutilisées.


Je crains que ma première solution ne soit trop coûteuse pour le serveur, car le nombre d'utilisateurs et de fichiers auxquels ils ont accès augmente. Je pense que ma deuxième solution serait beaucoup moins chère, mais également moins sécurisée en raison d'usurpation d'adresse IP, etc. Je crains également que l'écriture de ces adresses IP dans le fichier htaccess ne devienne un goulot d'étranglement de l'application s'il y a plusieurs utilisateurs simultanés. 

Laquelle de ces solutions sonne le mieux et pourquoi? Sinon, pouvez-vous penser à une solution complètement différente qui serait meilleure que l'une ou l'autre?

38
Bart

J'envisagerais d'utiliser un chargeur PHP pour gérer l'authentification, puis renvoyer les fichiers dont vous avez besoin. Par exemple, au lieu de faire <img src='picture.jpg' /> Faites quelque chose comme <img src='load_image.php?image=picture.jpg' />.

Votre chargeur d'images peut vérifier les sessions, vérifier les informations d'identification, etc., puis décider de renvoyer ou non le fichier demandé au navigateur. Cela vous permettra de stocker tous vos fichiers sécurisés en dehors de la racine accessible par le Web, afin que personne ne les lise simplement ou ne les navigue "accidentellement".

Rappelez-vous simplement de renvoyer les bons en-têtes dans PHP et de faire quelque chose comme readfile () en php et qui renverra le contenu du fichier au navigateur. 

J'ai utilisé cette configuration sur plusieurs sites Web sécurisés à grande échelle et cela fonctionne à merveille.

Edit: Le système que je suis en train de construire utilise cette méthode pour charger du Javascript, des images et des vidéos, mais CSS ne nous inquiète pas vraiment.

34
Shane

X-Sendfile

Il existe un module pour Apache (et d'autres serveurs HTTP) qui vous permet d'indiquer au serveur HTTP de servir le fichier que vous spécifiez dans un en-tête de votre code php: Votre script php devrait donc ressembler à ceci: 

// 1) Check access rights code
// 2) If OK, tell Apache to serve the file
header("X-Sendfile: $filename");

2 problèmes possibles:

  1. Vous devez avoir accès aux règles de réécriture (.htaccess activé ou accès direct aux fichiers de configuration).
  2. Vous avez besoin du module mod_xsendfile ajouté à votre Apache installé 

Voici une bonne réponse dans un autre fil de discussion: https://stackoverflow.com/a/3731639/2088061

7
DenisS

J'ai beaucoup réfléchi au même problème. Je suis également mécontent du moteur PHP en cours d'exécution pour chaque petite ressource servie. J'ai posé une question dans le même sens il y a quelques mois ici , mais avec un objectif différent. 

Mais je viens d'avoir une terriblement idée intéressante que pourrait fonctionner.

  • Maintenez un répertoire appelé /sessions quelque part sur votre serveur Web.

  • Chaque fois qu'un utilisateur se connecte, créez un fichier texte vide avec l'ID de session dans /sessions. Par exemple. 123456

  • Dans votre PHP application, diffusez des images comme celle-ci: /sessions/123456/images/test.jpg 

  • Dans votre fichier htaccess, deux commandes de redirection. 

  • Celui qui traduit /sessions/123456/images/test.jpg en /sessions/123456?filename=images/test.jpg 

  • Un deuxième qui intercepte tous les appels à //sessions/(.*) et vérifie si le fichier spécifié existe à l’aide de l’indicateur -f. Si /sessions/123456 n'existe pas, cela signifie que l'utilisateur s'est déconnecté ou que sa session a expiré. Dans ce cas, Apache envoie un 403 ou redirige vers une page d'erreur - la ressource n'est plus disponible.

De cette façon, nous avons une authentification de quasi-session dans mod_rewrite qui effectue un seul contrôle "fichier existe"!

Je n'ai pas assez de routine pour construire les instructions mod_rewrite à la volée, mais elles devraient être assez faciles à écrire. (J'espère dans votre direction @Gumbo :)

Notes et mises en garde:

  • Les fichiers de session expirés doivent être supprimés rapidement à l'aide d'un travail cron, à moins qu'il soit possible de vérifier le fichier mtime d'un fichier dans .htaccess (ce qui pourrait bien être possible).

  • L'image/la ressource est disponible sur n'importe quel client tant que la session existe, donc pas de protection à 100%. Vous pourriez peut-être contourner ce problème en ajoutant l'adresse IP du client dans l'équation (= le nom du fichier que vous créez) et effectuer une vérification supplémentaire pour% {REMOTE_ADDR}. C’est une maîtrise avancée de .htaccess mais je suis sûr que c’est faisable.

  • Les URL de ressources ne sont pas statiques et doivent être récupérées à chaque connexion, donc pas de mise en cache.

Je suis très intéressé par les retours d'informations à ce sujet, par les éventuelles lacunes ou impossibilités que j'ai peut-être oubliées, ainsi que par les implémentations réussies (je n'ai pas le temps de configurer moi-même un test). 

5
Pekka 웃

Créez une mappe rewrite qui vérifie les informations d'identification de l'utilisateur et les redirige vers la ressource appropriée ou vers une page "accès refusé".

3

Maintenir le contenu des fichiers htaccess semble être un cauchemar. En outre, votre objectif déclaré est d'empêcher les utilisateurs non authentifiés users non authentifiés client d'accéder à ce contenu - l'approche n'est donc pas adaptée à l'objectif:

Plusieurs utilisateurs peuvent sembler provenir de la même adresse IP

Une session utilisateur unique peut sembler provenir de plusieurs adresses.

Je crains que ma première solution pourrait devenir assez chère sur le serveur en tant que nombre de les utilisateurs et les fichiers auxquels ils ont accès augmente

Si vous souhaitez empêcher la lixiviation de votre contenu et ne souhaitez pas utiliser l'authentification automatique HTTP, alors le seul choix judicieux consiste à inclure tous les accès aux fichiers dans une couche logique supplémentaire. En outre, vous ne savez pas que l’utilisation de PHP pour ceci est un problème - l’avez-vous testé? Je pense que vous seriez surpris de voir à quel point le débit peut être fourni, en particulier si vous utilisez un cache opcode.

Je suppose que votre "simplification" de l'encapsuleur résout des problèmes tels que le type mime et la mise en cache.

C.

2
symcbean

Si vous utilisez Apache, vous pouvez configurer, comme ci-dessous, dans un fichier .htaccess ou httpd.conf. Voici un exemple pour empêcher l’accès au fichier * .inc. Cela fonctionne grandement pour moi. 

<Files ~ "\.inc$">
Order allow,deny
Deny from all
</Files>

Veuillez vous référer pour plus de détails à: http://www.ducea.com/2006/07/21/Apache-tips-tricks-deny-access-tocert-file-types/ .

0
Hieu Phan

Supposons que vous souhaitiez protéger tous vos fichiers statiques et que vous deviez les serveurs à partir de dans votre racine Web , vous pouvez protéger toutes les méthodes HTTP, à l'exception de HEAD. Si vous êtes autorisé, vous faites une demande de tête, passez par les en-têtes et envoyez le contenu du fichier sous forme de corps ..__ Bien sûr, cela coûte cher, mais vous êtes protégé et vous avez le même comportement.

0
Andreas Dyballa

J'ai écrit une application Web dynamique et l'ai déployée sur Webshere Application Server. Voici comment j'ai sécurisé mes fichiers statiques:

J'ai d'abord ajouté 

<login-config id="LoginConfig_1">
  <auth-method>FORM</auth-method>
    <realm-name>Form-Based Authentication</realm-name>
      <form-login-config>
        <form-login-page>/login.html</form-login-page>
        <form-error-page>/login_error.html</form-error-page>
       </form-login-config>
</login-config>

dans web.xml qui indiquera à votre serveur Web d'utiliser l'authentification basée sur un formulaire (le code pour utiliser le login est donné ci-dessous). 

code pour la page de connexion:

<form id="form1" name="form1" method="post" action="j_security_check" style="padding: 0px 0px 0px 12px;">
        Username: 
          <label>
          <input name="j_username" type="text" class="font2" />
        </label>

        <br />
        <br />
        Password:
        <span class="font2" >
        <label>
      <input name="j_password" type="password" class="font2" />
      </label>
      </span> 
        <br />
        <br />
            <label>
        <input type="submit"  class="isc-login-button" name="Login" value="Login" />
        </label>
    </form></td>

Pour que la connexion par formulaire se produise, vous devez configurer votre serveur Web afin qu’il utilise un registre d’utilisateurs particulier, qui peut être LDAP ou une base de données.

Vous pouvez déclarer vos ressources sécurisées et chaque fois qu'un utilisateur tente d'accéder à ces ressources, le conteneur vérifie automatiquement si l'utilisateur est authentifié ou non. Même vous pouvez attribuer des rôles également avec les ressources sécurisées. Pour ce faire, j'ai ajouté le code suivant dans mon web.xml

<security-constraint>
        <display-name>Authenticated</display-name>
        <web-resource-collection>
            <web-resource-name>/*</web-resource-name>
            <url-pattern>/*</url-pattern>
            <http-method>GET</http-method>
            <http-method>PUT</http-method>
            <http-method>HEAD</http-method>
            <http-method>TRACE</http-method>
            <http-method>POST</http-method>
            <http-method>DELETE</http-method>
            <http-method>OPTIONS</http-method>
        </web-resource-collection>
        <auth-constraint>
            <description>Auth Roles</description>
            <role-name>role1</role-name>            
            <role-name>role2</role-name>            
        </auth-constraint>
    </security-constraint>

    <security-role>
        <role-name>role1</role-name>
    </security-role>
    <security-role>
        <role-name>role2</role-name>
    </security-role>

Donc, ce code ne permettra à l'utilisateur de voir aucun fichier statique (depuis/*) jusqu'à ce qu'il soit connecté sous le rôle role1 et role2. Vous pouvez ainsi protéger vos ressources.

0
GG.

J'aurais peut-être une suggestion basée sur iframe et HTTP_REFERER mais ce n'est pas une solution à toute épreuve et cela dépendra de ce que vous voulez protéger avec cet accès.

Toutefois, pour empêcher l’affichage d’une page statique complète sans authentification, vous pouvez procéder comme suit:

1 - utilisez une page PHP pour authentifier l'utilisateur

2 - redirigez vers une autre page PHP contenant une clé dans l'URL et un iframe menant vers votre contenu statique dans le corps:

<iframe src="static/content.html" />

3 - Ensuite, dans votre htaccess, vous pouvez vérifier la clé dans le HTTP_REFERER comme ceci:

RewriteEngine On
RewriteCond %{HTTP_REFERER} !AUTH_KEY
RewriteCond %{REQUEST_URI} ^/path/to/protected/page$
RewriteRule . - [F]

4 - Enfin, si vous souhaitez dynamiser le site et ne pas utiliser la même clé à chaque fois, vous pouvez utiliser rewrite map comme suggéré par la réponse d'Ignacio Vazquez-Abrams ou créer un fichier avec l'IP de l'utilisateur comme nom de fichier et vérifier si le fichier existe à l'aide de REMOTE_ADDR. supprimez le fichier après un moment.

Mais gardez à l'esprit que le comportement iframe + HTTP_REFERER peut varier d'une session de navigateur à une autre, de même que REMOTE_ADDR ...

0
RafaSashi