.htaccess 中的白名单
我想使用白名单,而不是将不可访问的目录列入黑名单(如拒绝所有
)。基本上,我需要这个功能:
- 如果 uri 请求 /public 目录中存在的文件,则显示它;
- 否则将请求路由到/public/index.php;
- 请求字符串中不需要“public”字符串:
http://site.com/flower.jpg
显示文件系统中的DOCUMENT_ROOT/public/flower.jpg
文件;
示例:
目录结构:
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/
,但这效果很好。
Instead of blacklisting inaccessible directories (like with deny all
) I want to use a whitelist. Basically, I need this functionality:
- If the uri requests a file that exists in /public directory, display it;
- Otherwise route the request to /public/index.php;
- 'public' string is not needed in request string:
http://site.com/flower.jpg
displaysDOCUMENT_ROOT/public/flower.jpg
file from the file system;
Example:
Directory structure:
public\
flower.jpg
index.php
data\
secret_file.crt
Request string and expected result:
site.com/flower.jpg
- flower.jpg is displayed
site.com/data/secret_file.crt
site.com/public/flower.jpg
site.com/public
site.com/data
site.com/any/random_url
- request is routed to public/index.php
What I have now:
(and even that with outside help)
# 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
If I remove the
RewriteCond %{REQUEST_FILENAME} !-f
line, it seizes to work, I've experimented countless configurations, read the modRewrite
docs but can't figure out why this simple thing refuses to simply function.
Can anyone help me out or point in the right direction?
Complete final solution for reference
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]
It's a little more complicated when the application is in a subdirectory, like http://site.com/uk/
, but this works great.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
好吧,解释起来有点令人困惑。您遇到的问题是,当 mod_rewrite 重写某些内容时,如果没有 [R] 或 [P],它会在内部重定向,并且所有重写规则都会再次应用。这种情况不断发生,直到重写的 uri 与未重写的 uri 相同。因此,您拥有的第一条规则将被第二条规则重写。你需要防止这种情况发生。
首先,让我们看看第一条规则。您所拥有的完全没问题,只是您需要为将
site.com/public/flower.jpg 重新路由到 public/index.php
的警告添加一个条件。这意味着如果请求本身包含/public/
,它将不会为该请求提供服务(并让第二条规则处理)。这里需要额外注意的是,如果“/public”内有一个目录“public”,如DOCUMENT_ROOT/public/public/
中,它将无法访问。在这里,我们对以
GET /public/flower.jpg
开头的请求进行了额外检查,如果匹配,我们完全跳过此规则。此外,如果您尝试访问/public/
中的目录,此规则也会被破坏。例如,如果您在“/public”中有一个目录“stuff”,并尝试通过请求site.com/stuff/
访问它,则此规则将不允许您查看该内容(甚至如果 /stuff/ 中有一个 index.html 文件),因为您没有检查目录是否存在。您可以通过为 -d 添加此条件来实现此目的,如下所示:-d 条件与 -f 的 [OR] 一起表示:if
%{DOCUMENT_ROOT}/public%{REQUEST_URI}
is常规文件或目录。 (请参阅 RewriteCond 文档)现在介绍第二条规则,以及这看起来有点令人困惑,因为我们必须处理第一条规则条件的否定。如果第一条规则通过并且 URI 被重写,则会发生 2 件事:
因此,我们有 2 个条件处理这个问题。如果第一条规则重写了 URI,我们不想再次触及它。这解决了我在第一段中提到的问题。此外,我们不希望 URI 被重新重写,从而导致重写循环。因此,如果第二条规则已经应用,我们需要添加一个条件来停止重写,这意味着 URI 现在是
/public/index.php
。以下是这些条件的组合:Ok, this is going to be a little confusing to explain. The problem you are having is that when mod_rewrite rewrites something, without the [R] or [P], it redirects internally, and all the rewrite rules get applied again. This keeps happening until the rewritten uri is the same as the un-rewritten uri. So the first rule you have is getting rewritten by the second rule. You need to prevent that from happening.
First, let's look at the first rule. What you had is totally fine, except you need to add a condition for the caveat
site.com/public/flower.jpg rerouted to public/index.php
. This means if the request itself has a/public/
in it, it will not serve the request (and let the 2nd rule handle things). An additional caveat here is if you have a directory "public" inside "/public", as inDOCUMENT_ROOT/public/public/
, it will be inaccessible.Here we've done the extra check for a request starting with something like
GET /public/flower.jpg
, if it matches, we skip this rule entirely. Also, this rule will break if you try to access a directory in/public/
. For example, if you have a directory "stuff" inside "/public" and try to access it via the requestsite.com/stuff/
, this rule will not allow you to see the contents (even if there is an index.html file in /stuff/) because you are not checking if directories exist. You can do that by adding this condition for -d, like so:The -d condition along with the [OR] of the -f says: if
%{DOCUMENT_ROOT}/public%{REQUEST_URI}
is a regular file OR a directory. (See the RewriteCond docs)Now for the second rule, and this is going to look a bit confusing because we have to handle the negation of the first rule's conditions. If the first rule passes and the URI is rewritten, 2 things happen:
So we'll have 2 conditions to deal with that. If the first rule rewrote the URI, we don't want to touch it again. This solves the problem that I mentioned in the first paragraph. Additionally, we don't want to URI to get re-rewritten, causing a rewrite loop. So we need to add a condition to stop rewriting if the 2nd rule has already been applied, which means the URI is now
/public/index.php
. Here are the combination of those conditions:这可能有效:
优化版本也可能有效,但我不确定:
顺便说一句,你的逻辑很奇怪:以下规则:
意思是:“如果请求不是文件,则重写为
public/index.php
”。问题来了:如果是一个文件,那是怎么回事?没有什么。RewriteRule
被忽略。这是不安全的,想象一下如果这是一个您可能不希望用户访问的文件?去掉这个规则就行了,没啥用,没有它就更安全了(从我的角度来看)。请问优化后的版本是否有效?
请尝试使用
RewriteLog
指令:它可以帮助您追踪此类问题:告诉我它是否有效。
This may work:
The optimized version may work too but I'm not sure:
By the way your logic is weird: the following rule:
Means: "if the request is not a file, rewrite to
public/index.php
". The problem is here: if it's a file, what's going on? Nothing. TheRewriteRule
is ignored. This is not safe, imagine if it's a file that you may not want the user to access? Just remove this rule, it's useless, and without it, it's safer (from my point of view).May I ask you to tell me if the optimized version worked?
Please try to use the
RewriteLog
directive: it helps you to track down such problems:Tell me if it works.
我对你的第一组规则有点困惑,因为如果我没有弄错的话 %{REQUEST_URI} 将是 /public/flower.jpg 。我会这样做:
我不确定 %{REQUEST_FILENAME} 为空时的行为,但基本上规则说:
如果文件名公开存在,则将所有 URI 重写到该文件,如果它不重写到索引.php
这对你有用吗?
I'm a bit confused with your first set of rules, since %{REQUEST_URI} would be /public/flower.jpg if I'm not mistaking. I would have done it this way :
I'm not sure of the behaviour if %{REQUEST_FILENAME} is empty but basically the rules says:
If the filename exists in public, rewrite all URI to that file, if it does not rewrite to index.php
Would that work for you?
您是否考虑过以编程方式创建 .htaccess 文件,以将不在您用于创建它的任何文件中设置的白名单中的任何内容列入黑名单?如果你问我,你会发现再简单不过了。
Have you considered programmatically creating your .htaccess file to blacklist anything that isn't on a whitelist that you set in whatever file you use to create it? If you ask me, you can't get much simpler.