重写 Apache 条件不会阻止重写的应用

重写 Apache 条件不会阻止重写的应用

我尝试仅允许请求标头以 开头的传入请求/multiContent-Type而对其他、或端点的multipart/form-data请求必须始终以 开头。POSTPUTDELETEapplication/json Content-Type

在我的 Web 根文件中执行以下操作时.htaccess(无根访问权限):

# Enforce multipart/form-data content type for /multi routes
RewriteCond %{REQUEST_URI} ^/multi [NC]
RewriteCond %{HTTP:Content-Type} !^multipart/form-data; [NC]
RewriteRule ^ - [R=400,L,END]

# Enforce JSON content type for all other POST, PUT, and DELETE endpoints
RewriteCond %{REQUEST_METHOD} ^(POST|PUT|DELETE)$
RewriteCond %{HTTP:Content-Type} !^application/json [NC]
RewriteCond %{REQUEST_URI} !^/multi [NC]
RewriteRule ^ - [R=400,L,END]

...然后发送一个POST /multiwith Content-Type: multipart/form-data,我得到了 Apache 的400 Bad Request响应。当我删除第二个重写块时,请求就通过了。为什么在发送POST /multiwith时会应用第二个重写块Content-Type: multipart/form-data,尽管我在其中有条件RewriteCond %{REQUEST_URI} !^/multi [NC],这应该会阻止它被应用?

当我在启用第二个重写块的情况下发送请求时检查 Apache 错误日志时,我根本没有错误日志。当我检查 Apache Dom 日志时,我只是有一个传入请求产生了/multi响应400。因此,这确实似乎是由于应用了第二个重写规则,这对我来说毫无意义。

完整的 .htaccess 文件

# Prevent files whose name starts with ".ht" from frontend access
<FilesMatch "^\.ht">
    Require all denied
</FilesMatch>

# Set global Rules for document root directory
Options Indexes FollowSymLinks
Require all granted

# Specify rewrite rules
RewriteEngine On

# Authorization Header seems to be stripped by default, hence explicitly enable it
# Doing it via the rewrite rule is not the native way of doing it, and would need an improved rule to avoid that an
# Authorization HTTP header with an empty value is still passed to PHP
# RewriteCond %{HTTP:Authorization} ^(.*)
# RewriteRule .* - [e=HTTP_AUTHORIZATION:%1]
CGIPassAuth On

# Enforce multipart/form-data content type for /multi route
RewriteCond %{REQUEST_URI} ^/multi [NC]
RewriteCond %{HTTP:Content-Type} !^multipart/form-data; [NC]
RewriteRule ^ - [R=400,L,END]

# Enforce JSON content type for all other POST, PUT, and DELETE endpoints (return a 400 Bad Request otherwise)
RewriteCond %{REQUEST_METHOD} ^(POST|PUT|DELETE)$
RewriteCond %{HTTP:Content-Type} !^application/json [NC]
RewriteCond %{REQUEST_URI} !^/multi [NC]
RewriteRule ^ - [R=400,L,END]

# Route all incoming requests through index.php, except if it's for existing files
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule . /index.php [L]

# Ensure that png images, javascript and css files are all delivered with correct mime type. mod_mime must be enabled.
AddType application/javascript .js
AddType image/png .png
AddType text/css .css

# Add an 'Expires' HTTP header for all accessed js, css and png image files. mod_expires must be enabled.
ExpiresActive On
ExpiresByType image/png "access plus 1 year"
ExpiresByType application/javascript "access plus 1 year"
ExpiresByType text/css "access plus 1 year"

# Also add a Cache-Control HTTP header for the same cache lifetime of 1 year, to cover all browsers. mod_headers needed.
<FilesMatch "\.(js|css|png)$">
  Header set Cache-Control "public, max-age=31536000"
</FilesMatch>

# Define Image Uploads directory, to be able to access img uploads while uploads are kept outside from document root
RewriteRule ^images/(.*)$ /uploads/imgs/$1 [L]

# Ensure that all icons are delivered with the image/jpeg MIME type
RewriteRule ^icons/ - [E=CONTENT_TYPE:image/png]

答案1

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

稍后在文件中有一个前端控制器模式.htaccess,它将请求重写为/index.php。由于此规则仅具有L标志(而不是END),因此重写引擎会从重写的 URL 重新开始。(在.htaccess上下文中,重写引擎会循环,直到它未经更改地通过)。

RewriteCond %{REQUEST_URI} !^/multi [NC]

在重写引擎第二次传递过程中,URL 现在是/index.php(即。不是 /multi),因此上述条件成功,第二条规则被触发,发送 400 响应。(REQUEST_URI服务器变量在重写引擎的传递之间更新。)

重写引擎的这种“循环”是使用 mod_rewrite 时的一个额外的复杂性(和“功能”)。目录/.htaccess上下文。默认情况下,在服务器(或者虚拟主机) 上下文(除非您使用标志明确触发此行为PT)。

RewriteCond %{HTTP:Content-Type} !^multipart/form-data; [NC]

在旁边:看起来该请求将被第一条规则阻止,因为您;在正则表达式的末尾似乎有一个错误的(?)(即,Content-Type: multipart/form-data仅满足上述要求的请求否定健康)状况。)

您需要:

  • END在稍后的重写/index.php(前端控制器)中改用该标志,这将停止重写引擎的所有处理。例如:

    :
    RewriteRule . index.php [END]
    

    (在内部重写的情况下,不需要在替换字符串上使用斜杠前缀。但RewriteBase无论如何您都有一个指令,这在这里也不是必需的。)

或者

  • 不要使用REQUEST_URI第二条规则(由重写引擎更新)来测试请求的 URL,THE_REQUEST而要使用包含请求标头的第一行且不会更新的。THE_REQUEST将包含格式为 的字符串POST /multi/foo HTTP/1.1。例如,不要REQUEST_URI使用条件,而要使用类似以下内容:

    # Check the URL as requested by the client, not the rewritten URL
    RewriteCond %{THE_REQUEST} !\s/multi
    

或者

  • 确保第二条规则仅适用于客户的初始请求,而不适用于书面请求(后一条规则)。为此,您可以添加额外的状况(首先)并检查REDIRECT_STATUS环境变量。这是空的在初始请求时,并200在第一次成功重写后设置为(如“200 OK”HTTP 状态)。例如:

    # Only apply this rule to direct requests, not rewritten requests
    RewriteCond %{ENV:REDIRECT_STATUS} ^$
    :
    

其他说明:

RewriteRule ^ - [R=400,L,END]

您无需同时指定LENDEND其作用与L和 相同。 在.htaccess上下文中L,停止当前通过重写引擎的操作,然后重新开始。 而END(Apache 2.4 中的新增功能)停止当前通过重写引擎的操作,并停止重写引擎的任何进一步处理。

但是,这里不需要使用和L。当指定3xx 范围之外的代码时,处理无论如何都会停止。ENDR

RewriteCond %{REQUEST_URI} ^/multi [NC]
RewriteCond %{HTTP:Content-Type} !^multipart/form-data; [NC]
RewriteRule ^ - [R=400,L,END]

您不需要检查REQUEST_URI服务器变量的条件(在第一个规则中),因为可以在以下情况下更有效地执行此检查:RewriteRule 图案本身。例如:

RewriteCond %{HTTP:Content-Type} !^multipart/form-data [NC]
RewriteRule ^multi - [NC,R=400]

(假设该.htaccess文件位于文档根目录中,注释似乎证实了这一点。)

注意:这仅适用于第一条规则,不适用于第二条规则,第二条规则使用否定表达。

# Define Image Uploads directory, to be able to access img uploads while uploads are kept outside from document root
RewriteRule ^images/(.*)$ /uploads/imgs/$1 [L]

看起来这条规则(靠近文件末尾.htaccess)放错了地方?我假设表单的请求/images/<image>不会直接映射到物理文件,因此这条规则应该前端控制器模式,否则它永远不会被处理。

(尽管评论这么说,但这里没有什么是“在文档根目录之外”的。)

相关内容