Nginx 将所有流量重定向到 index.php,但不允许任意文件访问 [英] Nginx redirect all traffic to index.php, but don't allow arbitrary file access

查看:128
本文介绍了Nginx 将所有流量重定向到 index.php,但不允许任意文件访问的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个旧的 PHP 应用程序(Zend 框架),我正从 Apache 迁移到 Nginx.除了网页文件(JS、CSS、图片、字体等)之外,所有流量都需要通过项目文件夹根目录下的index.php文件.所以我需要将所有流量重定向到 index.php,除了网络文件.

有很多使用 try_files 作为包罗万象的解决方案.基本上,如果文件系统上没有找到文件,nginx 会将请求发送到 index.php.

问题在于应用程序将其配置文件存储在可通过网络访问的路径中(即在包含 index.php 的文件夹的子目录中).因此,您可以将浏览器指向配置文件路径并阅读它(例如 https://example.com/config/app.xml).由于该文件存在于文件系统中,nginx 将为其提供服务.实际上还有一些项目配置文件也可以通过这种方式访问​​.

那么,我如何将所有请求发送到 index.php,除了网络文件,并且不允许从网络读取任意文件?

是的,我知道我可以更新应用程序以访问另一个位置的配置文件,但它是遗留的,并不是非常重要.我不想花太多时间在上面,也不想破坏它.

我还可以禁止访问任何碰巧是配置文件的扩展(例如 xmlyml 等),但这是一个大项目,我不这样做不想冒险错过一些东西.

我想出了一个解决方案.我没有在网上找到任何完全符合我想要的方式的东西,所以我问这个问题是为了回答它.

解决方案

看着你的配置我有一个强烈的愿望评论它,但是这将远远超出单一评论格式,因此将其写为答案.

<块引用>

location ~* \.(js|ico|gif|jpg|png|svg|css|jpeg|wav|mp3|eot|woff|ttf)$ {}

当然,这会奏效.但在现实生活中,这会有效地阻止诸如 robots.txtsitemap.xml 之类的文件,从而阻止通过 /.well-known/进行 SSL 证书验证pki-validation/.txt

<块引用>

location ~* ^/(?!(index\.php)) {重写 ^/index.php$is_args$args;}

rewritelocation 指令都适用于规范化的 URI(不包括请求的查询部分).虽然 try_filesreturn 指令通常需要使用这个 $is_args$args 后缀,以及 rewrite 指令保留所有查询参数并遵循重写的 URI.这意味着 /users?blah=1 请求被重写为 - 惊喜,惊喜 - /index.php?blah=1?blah=1.这使得 nginx 认为最后一个 ?blah=1 是一个查询字符串,前面的 /index.php?blah=1 是给你HTTP 404"的文件名未找到"错误.写这个位置的正确方法是

location ~* ^/(?!index\.php) {最后重写^/index.php;}

它可以更简单:

location/{最后重写^/index.php;}location =/index.php { # 而不是 ~ \.php$...}

但是,如果您想将所有其他请求传递给您的 index.php 控制器,则可以以更有效的方式完成.我假设您使用的 snippets/fastcgi-php.conf 文件是某些基于 Debian 的 Linux 发行版的默认文件:

<块引用>

# regex 将 $uri 拆分为 $fastcgi_script_name 和 $fastcgi_pathfastcgi_split_path_info ^(.+\.php)(/.+)$;# 传递前检查PHP脚本是否存在try_files $fastcgi_script_name =404;# 绕过 try_files 重置 $fastcgi_path_info 的事实# 见:http://trac.nginx.org/nginx/ticket/321设置 $path_info $fastcgi_path_info;fastcgi_param PATH_INFO $path_info;fastcgi_index index.php;包括 fastcgi.conf;

您根本不需要它,因为您不需要 location ~ \.php$ { ... } 因为您要允许访问的唯一 PHP 文件是index.php.使用以下内容:

location/{# 定义默认的 FastCGI 参数包括 fastcgi.conf;# 始终使用 'index.php' 作为 FastCGI 脚本fastcgi_param SCRIPT_FILENAME $document_root/index.php;# 将请求传递给 FastCGI 后端fastcgi_pass unix:/run/php/php7.4-fpm.sock;}

整个配置将是

server {根/路径/到/www;位置 ~* \.(js|ico|gif|jpg|png|svg|css|jpeg|wav|mp3|eot|woff|ttf)$ {}地点/{包括 fastcgi.conf;fastcgi_param SCRIPT_FILENAME $document_root/index.php;fastcgi_pass unix:/run/php/php7.4-fpm.sock;}}

I have a legacy PHP app (Zend Framework) that I'm moving from Apache to Nginx. Except for things like web files (JS, CSS, images, fonts, etc.), all traffic needs to go through the index.php file in the root of the project folder. So I need to redirect all traffic to index.php, except for web files.

There are a lot of solutions to this that use try_files as a catch-all. Basically if a file is not found on the filesystem, nginx will send the request to index.php.

The problem with this is that the app stores its configuration file in a web-accessible path (i.e. in a subdirectory of the folder that holds index.php). So you could point your browser to the configuration file path and read it (e.g. https://example.com/config/app.xml). Since the file exists on the filesystem, nginx will serve it. There are actually a few more project config files that can be accessed in this way too.

So, how do I send all requests to index.php, except for web files, and also not allow arbitrary files to be read from the web?

Yes, I know I could update the app to access the config files in another location, but it's legacy and not super crucial. I don't want to spend more time on it than I have to and I don't want to break it.

I could also forbid access to any extensions that happen to be config files (e.g. xml, yml, etc.), but it's a big project and I don't want to risk missing something.

I figured out a solution. I didn't find anything that worked exactly the way I wanted online, so I'm asking this question in order to answer it.

解决方案

Looking at your configuration I have a strong desire to comment it, however this would be much beyond the single comment format so writing this as an answer.

location ~* \.(js|ico|gif|jpg|png|svg|css|jpeg|wav|mp3|eot|woff|ttf)$ {}

Of course, this would work. But in a real life this would effectively blocking the files such as robots.txt, sitemap.xml, preventing SSL certificate validation via the /.well-known/pki-validation/<signature>.txt etc.

location ~* ^/(?!(index\.php)) {
  rewrite ^ /index.php$is_args$args;
}

Both rewrite and location directives works with the normalized URI (which doesn't include the query part of the request). While the try_files or return directives usually require this $is_args$args suffix to be used, with the rewrite directive all the query arguments are preserved and follows the rewritten URI. This means the /users?blah=1 request is being rewrited to - surprise, surprise - /index.php?blah=1?blah=1. This makes nginx consider the last ?blah=1 to be a query string and the preceding /index.php?blah=1 to be the filename giving you "HTTP 404 Not found" error. The right way to write this location would be the

location ~* ^/(?!index\.php) {
  rewrite ^ /index.php last;
}

and it can be even more simple:

location / {
  rewrite ^ /index.php last;
}

location = /index.php { # instead of ~ \.php$
  ...
}

However if you want to pass all the other requests to your index.php controller it could be done in a much more effective way. I assume the snippets/fastcgi-php.conf file you are using is the default one from some Debian-based Linux distro:

# regex to split $uri to $fastcgi_script_name and $fastcgi_path
fastcgi_split_path_info ^(.+\.php)(/.+)$;

# Check that the PHP script exists before passing it
try_files $fastcgi_script_name =404;

# Bypass the fact that try_files resets $fastcgi_path_info
# see: http://trac.nginx.org/nginx/ticket/321
set $path_info $fastcgi_path_info;
fastcgi_param PATH_INFO $path_info;

fastcgi_index index.php;
include fastcgi.conf;

You don't need it at all, as you don't need the location ~ \.php$ { ... } since the only PHP file you want to allow access to is the index.php. Use the following:

location / {
  # define the default FastCGI parameters
  include fastcgi.conf;
  # always use the 'index.php' as the FastCGI script
  fastcgi_param SCRIPT_FILENAME $document_root/index.php;
  # pass the request to the FastCGI backend
  fastcgi_pass unix:/run/php/php7.4-fpm.sock;
}

The whole configuration will be the

server {
  root /path/to/www;

  location ~* \.(js|ico|gif|jpg|png|svg|css|jpeg|wav|mp3|eot|woff|ttf)$ {}

  location / {
    include fastcgi.conf;
    fastcgi_param SCRIPT_FILENAME $document_root/index.php;
    fastcgi_pass unix:/run/php/php7.4-fpm.sock;
  }
}

这篇关于Nginx 将所有流量重定向到 index.php,但不允许任意文件访问的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆