.htaccess中的白名单

时间:2011-03-30 13:18:11

标签: apache .htaccess mod-rewrite

我想使用白名单,而不是列入黑名单无法访问的目录(例如deny all)。基本上,我需要这个功能:

  1. 如果uri请求/ public目录中存在的文件,则显示它;
  2. 否则将请求路由到/public/index.php;
  3. 请求字符串中不需要
  4. 'public'字符串:http://site.com/flower.jpg显示文件系统中的DOCUMENT_ROOT/public/flower.jpg文件;
  5. 实施例

    目录结构:

     public\
       flower.jpg
       index.php
     data\
       secret_file.crt
    

    请求字符串和预期结果:

    • site.com/flower.jpg
      • flower.jpg显示
    • site.com/data/secret_file.crt
    • site.com/public/flower.jpg
    • site.com/public
    • site.com/data
    • site.com/any/random_url
      • 请求被路由到public / index.php

    我现在拥有的:

    (甚至 有外部帮助)

    # the functionality described in #1 above
    RewriteCond %{DOCUMENT_ROOT}/public%{REQUEST_URI} -f
    RewriteRule .* public%{REQUEST_URI} [L]
    
    # I'd like to take out the following line so ALL other requests route to index.php
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule .* public/index.php
    

    如果我删除

    RewriteCond %{REQUEST_FILENAME} !-f

    行,它抓住了工作,我已经尝试了无数的配置,阅读modRewrite文档,但无法弄清楚为什么这个简单的东西拒绝简单地运作。

    任何人都可以帮助我或指出正确的方向吗?


    完整的最终解决方案


    RewriteEngine On
    
    # following line stops mod_rewrite from looping because this rule has already been applied
    RewriteCond %{REQUEST_URI} !^/public/index.php
    RewriteCond %{DOCUMENT_ROOT}/public%{REQUEST_URI} -f
    RewriteRule .* /public%{REQUEST_URI} [L]
    
    # don't apply this rule if the first rule has been applied
    RewriteCond %{REQUEST_URI} !^/public/
    RewriteRule .* /public/index.php [L]
    

    当应用程序位于http://site.com/uk/这样的子目录中时,这会有点复杂,但这很有效。

4 个答案:

答案 0 :(得分:1)

这可能有效:

RewriteCond %{DOCUMENT_ROOT}/public%{REQUEST_FILENAME} -f [OR]
RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} -f [OR]
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} -f
RewriteRule (.*) public$1 [QSA,L]
RewriteRule .* public/index.php

优化版本也可以使用,但我不确定:

RewriteCond %{DOCUMENT_ROOT}(/public|public|)%{REQUEST_FILENAME} -f
RewriteRule (.*) public$1 [QSA,L]
RewriteRule .* public/index.php

顺便说一句,你的逻辑很奇怪:以下规则:

RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule .* public/index.php

表示:"如果请求不是文件,请重写为public/index.php"。问题出在这里:如果它是一个文件,那是怎么回事?没有。 RewriteRule被忽略。这不安全,想象一下,如果它是您可能不希望用户访问的文件?只需删除此规则,它就没用了,没有它,它会更安全(从我的角度来看)。


我可以请你告诉我优化版本是否有效吗?


请尝试使用RewriteLog指令:它可以帮助您找出此类问题:

# Trace:
# (!) file gets big quickly, remove in prod environments:
RewriteLog "/web/logs/mywebsite.rewrite.log"
RewriteLogLevel 9
RewriteEngine On

告诉我它是否有效。

答案 1 :(得分:1)

好的,解释起来会有点混乱。您遇到的问题是,当mod_rewrite重写某些内容时,如果没有[R]或[P],它会在内部重定向,并且所有重写规则都会再次应用。这种情况一直持续到重写的uri与未重写的uri相同。因此,您拥有的第一条规则是由第二条规则重写。你需要防止这种情况发生。

首先,让我们看看第一条规则。你有什么是完全没问题的,除了你需要为警告添加一个条件site.com/public/flower.jpg rerouted to public/index.php。这意味着如果请求本身中包含/public/,它将不会提供请求(并让第二个规则处理事务)。另外需要注意的是,如果您在“/ public”中有一个“public”目录,就像DOCUMENT_ROOT/public/public/一样,它将无法访问。

# Make sure the request itself isn't for /public/
RewriteCond %{THE_REQUEST} !^[A-Z]+\ /public/
# Make sure the filename exists.
RewriteCond %{DOCUMENT_ROOT}/public%{REQUEST_URI} -f
RewriteRule ^ /public%{REQUEST_URI} [L]

在这里,我们对GET /public/flower.jpg之类的请求进行了额外检查,如果匹配,我们会完全跳过此规则。此外,如果您尝试访问/public/中的目录,此规则将会中断。例如,如果“/ public”中有一个目录“stuff”并尝试通过请求site.com/stuff/访问它,则此规则将不允许您查看内容(即使有index.html文件) in / stuff /)因为您没有检查目录是否存在。您可以通过为-d添加此条件来实现此目的,如下所示:

# Make sure the request itself isn't for /public/
RewriteCond %{THE_REQUEST} !^[A-Z]+\ /public/
# Make sure the filename/directory exists.
RewriteCond %{DOCUMENT_ROOT}/public%{REQUEST_URI} -f [OR]
RewriteCond %{DOCUMENT_ROOT}/public%{REQUEST_URI} -d
RewriteRule ^ /public%{REQUEST_URI} [L]

-d条件和-f的[OR]表示:if %{DOCUMENT_ROOT}/public%{REQUEST_URI}是常规文件还是目录。 (See the RewriteCond docs

现在对于第二条规则,这看起来有点令人困惑,因为我们必须处理第一条规则条件的否定。如果第一个规则通过并且URI被重写,则会发生以下两件事:

  1. 请求不是从以下内容开始的:GET / public /
  2. uri被改写为“/ public / [something]”
  3. 因此,我们将有2个条件来处理这个问题。如果第一条规则重写了URI,我们不想再次触摸它。这解决了我在第一段中提到的问题。此外,我们不希望URI被重新重写,从而导致重写循环。因此,如果已经应用了第二条规则,我们需要添加一个条件来停止重写,这意味着URI现在是/public/index.php。以下是这些条件的组合:

    # stops mod_rewrite from looping because this rule has already been applied
    RewriteCond %{REQUEST_URI} !^/public/index.php
    # don't apply this rule if the first rule has been applied
    RewriteCond %{THE_REQUEST} ^[A-Z]+\ /public/  [OR]
    RewriteCond %{REQUEST_URI} !^/public/
    RewriteRule ^ /public/index.php [L]
    

答案 2 :(得分:0)

我对你的第一套规则感到困惑,因为如果我没有误会,%{REQUEST_URI}将是/public/flower.jpg。我会这样做的:

RewriteCond public/%{REQUEST_FILENAME} -f
RewriteRule ^.*$ public/%{REQUEST_FILENAME} [L] 

RewriteCond public/%{REQUEST_FILENAME} !-f
RewriteRule ^.*$ public/index.php [L]

如果%{REQUEST_FILENAME}为空,我不确定该行为,但基本上规则是:

如果文件名存在于public中,则重写该文件的所有URI,如果它没有重写为index.php

这会对你有用吗?

答案 3 :(得分:0)

您是否考虑过以编程方式创建.htaccess文件,以便将您在用于创建它的文件中设置的白名单中没有的任何内容列入黑名单?如果你问我,你就不会变得更简单。