使用PHP / Apache限制对静态文件的访问(html,css,img等)

时间:2010-02-02 19:58:18

标签: php security apache .htaccess

假设您在服务器上的目录中有很多html,css,js,img等文件。通常情况下,只需输入完整的URL就可以访问Internet-land中的任何用户:http://example.com/static-files/sub/index.html

现在,如果您只希望授权用户能够加载这些文件,该怎么办?对于此示例,假设您的用户首先从以下网址登录:http://example.com/login.php

如何允许登录用户查看index.html文件(或“static-files”下的任何文件),但将文件限制为其他人?

到目前为止,我已经提出了两种可能的解决方案:

解决方案1 ​​
在“static-files”下创建以下.htaccess文件:

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

然后在authorize.php ...

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

这个authorize.php文件大大简化了,但你明白了。

解决方案2
在“static-files”下创建以下.htaccess文件:

Order Deny,Allow
Deny from all
Allow from 000.000.000.000

然后我的登录页面可以为每个登录的用户附加带有IP的.htaccess文件。显然,这还需要某种清理例程来清除旧的或不再使用的IP。

<小时/> 我担心我的第一个解决方案在服务器上会变得相当昂贵,因为他们访问的用户和文件的数量会增加。我认为我的第二个解决方案会便宜得多,但由于IP欺骗等原因也不太安全。我还担心如果有很多同时用户,将这些IP地址写入htaccess文件可能会成为应用程序的瓶颈。

哪些解决方案听起来更好,为什么?或者,你能想到一个完全不同的解决方案,而不是其中任何一个吗?

9 个答案:

答案 0 :(得分:34)

我会考虑使用PHP加载程序来处理身份验证,然后返回您需要的文件。例如,而不是<img src='picture.jpg' />执行<img src='load_image.php?image=picture.jpg' />

之类的操作

您的图像加载程序可以验证会话,检查凭据等,然后决定是否将所请求的文件返回给浏览器。这将允许您将所有安全文件存储在Web可访问的根目录之外,这样任何人都不会只是WGET它们或者“意外地”浏览它们。

请记住在PHP中返回正确的标题,并在php中执行类似readfile()的操作,并将文件内容返回给浏览器。

我在几个大型安全网站上使用了这个设置,它就像一个魅力。

编辑:我目前正在构建的系统使用此方法来加载Javascript,图像和视频,但我们对安全性并不十分担心。

答案 1 :(得分:7)

X-SENDFILE

有一个用于Apache(和其他HTTP服务器)的模块,它允许您告诉HTTP服务器为您在php代码中的标头中指定的文件提供服务: 所以你的php脚本应该是这样的:

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

2个可能的问题:

  1. 您需要访问重写规则(.htaccess已启用或直接访问配置文件)
  2. 您需要将mod_xsendfile模块添加到您安装的Apache
  3. 在另一个帖子中,这是一个很好的答案: https://stackoverflow.com/a/3731639/2088061

答案 2 :(得分:5)

我一直在考虑同样的问题。我同样不满意为每个服务的小资源运行的PHP引擎。几个月前我以同样的方式提出了一个问题here,但重点不同。

但我只是有一个非常有趣的想法可能工作。

  • 在网络服务器上的某处维护一个名为/sessions的目录。

  • 每当用户登录时,创建一个会话ID为/sessions的空文本文件。例如。 123456

  • 在您的PHP应用中,提供如下图片:/sessions/123456/images/test.jpg

  • 在您的htaccess文件中,有两个重定向命令。

  • /sessions/123456/images/test.jpg翻译为/sessions/123456?filename=images/test.jpg

  • 的人
  • 第二个捕获对//sessions/(.*)的任何调用,并使用-f标志检查指定的文件是否存在。如果/sessions/123456不存在,则表示用户已注销或其会话已过期。在这种情况下,Apache会发送403或重定向到错误页面 - 资源不再可用。

这样,我们在mod_rewrite中进行准会话认证只做一个“文件存在”检查!

我没有足够的例程来动态构建mod_rewrite语句,但它们应该很容易编写。 (我希望你的方向是@Gumbo:)

备注和警告:

  • 必须使用cron作业快速删除过期的会话文件,除非可以在.htaccess中检查文件的mtime(这很可能)。

  • 只要会话存在,图像/资源就可以到任何客户端,因此没有100%的保护。您可以通过将客户端IP添加到等式(=您创建的文件名)中来解决此问题,并对%{REMOTE_ADDR}进行额外检查。这是先进的.htaccess掌握,但我很确定它是可行的。

  • 资源网址不是静态的,每次登录时都必须检索,因此不需要缓存。

非常对这方面的反馈感兴趣,我可能忽略的任何不足或不可能,以及任何成功的实施(我现在没有时间自己设置测试) 。

答案 3 :(得分:3)

创建一个rewrite map,用于验证用户的凭据,并将其重定向到相应的资源或“拒绝访问”页面。

答案 4 :(得分:2)

维护htaccess文件的内容看起来像是一场噩梦。此外,您声明的目标是防止未经身份验证的用户未经过身份验证的客户端IP地址访问此内容 - 因此该方法不适合用途:

多个用户可能看起来来自同一个IP地址

单个用户会话似乎来自多个地址。

  

我担心我的第一个解决方案可能会在服务器上变得相当昂贵   他们访问的用户和文件增加

如果您想阻止内容泄露并且不想使用HTTP身份验证,那么将所有文件访问权限包含在另一层逻辑中是唯一合理的选择。另外,你不知道使用PHP来解决这个 问题 - 你测试过了吗?我认为你会感到惊讶它可以提供多少吞吐量,特别是如果你使用操作码缓存。

我猜你的'简化'包装器解决了mime类型和缓存等问题。

下进行。

答案 5 :(得分:0)

我编写了一个动态Web应用程序并将其部署在Webshere Application Server上,这就是我保护静态文件的方式:

我先添加了

<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>
web.xml中的

将告诉您的网络服务器使用基于表单的身份验证(下面给出了使用登录的代码)。

登录页面的代码:

<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>

要进行基于表单的登录,您必须将Web服务器配置为使用特定的用户注册表,该用户注册表可以是LDAP或数据库。

您可以声明您的安全资源,每当用户尝试访问这些资源时,容器会自动检查用户是否经过身份验证。即使您也可以使用安全资源附加角色。为此,我在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>

因此,在用户角色role1和role2下登录之前,此代码不会让用户看到任何静态文件(因为/ *)。因此,通过这种方式,您可以保护您的资源。

答案 6 :(得分:0)

如果您使用的是apache,则可以在.htaccess或httpd.conf文件中配置,如下所示。下面是一个阻止访问* .inc文件的示例。它对我很有用。

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

请参阅:http://www.ducea.com/2006/07/21/apache-tips-tricks-deny-access-to-certain-file-types/了解更多详情。

答案 7 :(得分:0)

假设您要保护所有静态文件,并且必须从webroot 中的服务它们,您可以保护除HEAD之外的所有HTTP方法。 如果您已获得授权,则会通过标题发送头请求并将filecontent作为正文发送。 当然这是昂贵的,但你受到保护,你也有同样的行为。

答案 8 :(得分:0)

我可能会根据iframeHTTP_REFERER提出建议,但这不是防弹措施,而是取决于您希望通过此访问权限确切保护的内容。

但是如果在没有身份验证的情况下阻止显示完整的静态页面,您可以执行以下操作:

1 - 使用PHP页面对用户进行身份验证

2 - 重定向到另一个PHP页面,其中包含URL中的键和链接到正文中静态内容的iframe:

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

3 - 然后在您的htaccess中,您可以检查HTTP_REFERER中的密钥:

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

4 - 最后如果你想让它变得更动态,每次你都可以按照Ignacio Vazquez-Abrams的回答建议rewrite map使用相同的KEY,或者使用用户IP作为文件名创建一个文件,使用REMOTE_ADDR检查文件是否存在,然后在一段时间后删除该文件。

但请记住,iframe + HTTP_REFERER行为可能因浏览器会话而异,也可能因REMOTE_ADDR而有所不同......