【问题标题】:Deny directory and file access with both htaccess and PHP拒绝使用 htaccess 和 PHP 访问目录和文件
【发布时间】:2015-01-26 08:54:01
【问题描述】:

我目前正在为一个游戏团队开发一个网络项目,我正在尝试测试自己并扩展我的一般知识和工具箱。我正在处理的一项功能要求我“清理”所请求文件的 URL。我有这个系统,它工作得很好。现在,为了让我的网站“完美”运行,我需要通过 htaccess 拒绝访问某些文件夹以及其中的所有文件。但是有一个小问题......它需要在用户甚至没有意识到的情况下运行。

我的文件结构、首选访问方式、路径、状态如下:

rogue                   {allow}  {direct}   {%domain%/dashboard/}  {200}
    css                 {deny}   {direct}   {%domain%/css/}        {403}
    js                  {deny}   {direct}   {%domain%/js/}         {403}
    fonts               {deny}   {direct}   {%domain%/fonts/}      {403}
    files               {deny}   {direct}   {%domain%/files/}      {403}
    image               {deny}   {direct}   {%domain%/image/}      {403}
    includes            {deny}   {direct}   {%domain%/includes/}   {403}
    php                 {deny}   {direct}   {%domain%/php/}        {403}
    index.php           {allow}  {direct}   {%domain%/dashboard/}  {200}
    html_footer.php     {deny}   {direct}   {%domain%/dashboard/}  {404}
    html_header.php     {deny}   {direct}   {%domain%/dashboard/}  {404}
    .htaccess           {deny}   {direct}   {%domain%/dashboard/}  {403}

上面的列表实际上是我希望网站如何运行的一个示例...index.php 文件控制请求的“文件”并相应地显示它们。例如,%domain%/get-in-touch/ 将在%domain%/includes/ 目录中显示contact.php 文件。直接调用index.php显示和%domain%/dashboard/一样。

以下代码来自我的.htaccess 文件。 请忽略# NN sn-ps。这些是为了您的利益,以减少我添加代码 sn-ps 的需要。我想它也会对您有所帮助...如果需要,您可以直接引用行号。

DirectoryIndex index.php                                                        # 01
Options All +FollowSymlinks -Indexes                                            # 02
                                                                                # 03
<IfModule mod_rewrite.c>                                                        # 04
    RewriteEngine On                                                            # 05
    #RewriteBase /rogue/                                                        # 06
                                                                                # 07
    RewriteCond %{REQUEST_FILENAME} !-d                                         # 08
    RewriteCond %{REQUEST_FILENAME} !-f                                         # 09
    RewriteRule ^(.*?)$ index.php [QSA,L]                                       # 10
                                                                                # 11
    RewriteCond %{REQUEST_FILENAME} -d                                          # 12
    RewriteRule ^(.*?)(js|image|css|includes|php|files)(.*)$ index.php [QSA,L]  # 13
</IfModule>                                                                     # 14

我注释掉了第 06 行,因为系统似乎不需要它,但我保留它以供我自己参考,以防以后可能需要它。当文件或目录不存在时,0810 行控制重定向到index.php,然后响应文件控制其余部分。文件夹css/js/fonts/image/includes/php 都存在,所以行 1213 阻止访问它们并重定向到 index.php 可以找到“目录”数组和相关的可以选择包含文件。该数组的示例包含在帖子的底部。行 1213 可以移动到行 08 之上并且不会有任何变化,但是从文件中删除它们会导致默认 403 禁止错误触发。

删除行 08091213 会通过加载位于在imagefilesfonts 目录中为MIME type text/html,但是,任何其他目录中的文件似乎都不受影响。

1213 行按要求执行,直到您请求访问这些目录中的文件。文件显示出来,我所有的秘密都暴露在世人面前。结果,向用户显示了某些非常重要的文件。我想阻止任何人访问我选择拒绝访问的任何文件夹中的文件,并将其重定向到index.php 并显示403。我尝试在 1213 行之间添加 RewriteCond %{REQUEST_FILENAME} -f,但页面显示为损坏且无法加载资源。

注意:我不得不从 02 行中删除 -Indexes,因为来自 JavaScript 文件的文件请求将被拒绝,这样做也阻止了 12 和 13 不按预期行事,无论其位置如何。

目录数组 [PHP]

$_INCDIRECTORY = 'includes/';
$_PATHINCLUDES = array(
    'dashboard'     => array(
        'allow'         => true,
        'path'          => $_INCDIRECTORY.'dashboard.php',
        // The following must remain FALSE; This value will be changed 'on-the-fly';
        'exists'        => false,
    ),
    'news'          => array(
        'allow'         => true,
        'path'          => $_INCDIRECTORY.'news.php',
        // The following must remain FALSE; This value will be changed 'on-the-fly';
        'exists'        => false,
    ),
    'search'        => array(
        'allow'         => true,
        'path'          => $_INCDIRECTORY.'search.php',
        // The following must remain FALSE; This value will be changed 'on-the-fly';
        'exists'        => false,
    ),
    // Excluded/Denied directories;
    'css'           => array(
        'allow'         => false,
        'path'          => 'css/',
        // The following must remain FALSE; This value will be changed 'on-the-fly';
        'exists'        => false,
    ),
    // Error pages;
    '403'           => array(
        'allow'         => true,
        'path'          => $_INCDIRECTORY.'errors/403.php',
        // The following must remain FALSE; This value will be changed 'on-the-fly';
        'exists'        => false,
    ),
    '404'           => array(
        'allow'         => true,
        'path'          => $_INCDIRECTORY.'errors/404.php',
        // The following must remain FALSE; This value will be changed 'on-the-fly';
        'exists'        => false,
    ),
);

【问题讨论】:

    标签: php apache .htaccess mod-rewrite redirect


    【解决方案1】:

    在花了一段时间寻找解决方案,并尝试通过通常的尝试和错误来解决它之后,我想出了一个完全按照我想要的方式工作的解决方案。似乎更适合回答我自己的问题,以供遇到相同问题的任何人参考。

    以下代码位于您网站根目录的.htaccess 文件中。在 sn-p 之后对每一行的作用进行说明。

    .htaccess 诡计

    DirectoryIndex index.php
    Options +FollowSymlinks -Indexes
    
    <IfModule mod_rewrite.c>
        # Enable mod_rewrite;
        RewriteEngine On
    
        # Detect if SSL Module is enabled;
        <IfModule mod_ssl.c>
            RewriteCond %{HTTPS} off
            RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R=301,NC,L]
        </IfModule>
    
        # Check if the directory exists;
        RewriteCond %{REQUEST_FILENAME} -d
        RewriteCond %{HTTP_REFERER} !^((((https|http)://([w]{3}\.)?)([a-z0-9\.]+)/).*)$ [NC]
        RewriteRule ^(.*?)(js|image|css|includes|php|files)(.*)$ index.php [L]
    
        # Check if the file exists;
        RewriteCond %{REQUEST_FILENAME} -f
        RewriteCond %{HTTP_REFERER} !^((((https|http)://([w]{3}\.)?)([a-z0-9\.]+)/).*)$ [NC]
        RewriteRule ^(.*?)(js|image|css|includes|php|files)(.*)$ index.php [L]
    
        # Redirect to 'index.php', regardless of the request;
        RewriteCond %{REQUEST_FILENAME} !-d
        RewriteCond %{REQUEST_FILENAME} !-f
        RewriteRule ^(.*?)$ index.php [QSA,L]
    </IfModule>
    

    DirectoryIndex index.php 将网站的主要“主页”定义为index.php,这不是必需的,但如果您的网络主机更改 Apache 的默认设置以使用不同的默认设置,使用它可能是一个好习惯索引。

    Options +FollowSymlinks -Indexes 执行以下操作:+FollowSymlinks 允许 Apache '跟随符号链接',-Indexes 防止在 index.{ext} 丢失时列出任何子文件夹。 注意:从以前的配置中删除 All 会阻止脚本调​​用包含在排除项中,从而允许所有 AJAX 功能。

    &lt;IfModule mod_rewrite.c&gt; ... &lt;/IfModule&gt; 允许 htaccess 文件的基础运行,而无需在 Apache 配置中启用重写模块。

    RewriteEngine On 启用重写引擎。将值设置为Off 会禁用此功能,并且不会运行任何底层代码。

    &lt;IfModule mod_ssl.c&gt; ... &lt;/IfModule&gt; 允许包含的代码运行,前提是 Apache 安装包含并使用 SSL 模块。

    RewriteCond %{HTTPS} off 然后检查%{HTTPS} 是否被请求使用,然后RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R=301,NC,L] 将用户永久重定向到启用 SSL 的 URI。 %{HTTP_HOST}%{REQUEST_URI} 只允许在多个网站上使用相同的功能,而无需重新配置。

    RewriteCond %{REQUEST_FILENAME} -d 检查请求的目录是否存在,RewriteCond %{HTTP_REFERER} !^((((https|http)://([w]{3}\.)?)([a-z0-9\.]+)/).*)$ [NC] 然后检查引用者是否 请求完整的 URL,基本上是一个域名。 RewriteCond 后面的 RegEx 检查请求是使用 http 还是 https,可选地使用 www.,然后检查 URL 的其余部分。然后RewriteRule ^(.*?)(js|image|css|includes|php|files)(.*)$ index.php [L] 检查请求的任何文件夹是否包含在正则表达式中,然后重定向到index.php,PHP 代码将在其中处理棘手的问题。 注意:请参阅this post 以供参考。

    遵循与上述代码相同的过程,RewriteCond %{REQUEST_FILENAME} -f 执行相同的操作。但是处理文件请求,如果它们存在的话。 注意:同样,请参阅this post 以供参考。

    RewriteCond %{REQUEST_FILENAME} !-dRewriteCond %{REQUEST_FILENAME} !-f 检查请求的文件是目录还是文件以及它们是否存在,然后RewriteRule ^(.*?)$ index.php [QSA,L] 将整个请求重定向到请求所在的index.php由 PHP 正确处理。 注意:对于那些还不知道这一点的人,! 字符在大多数情况下可以用作not 的简写。 注意:请参阅 this page 获取 mod_rewrite 变量备忘单。 this tool 用于 RegEx 测试。

    PHP 魔法

    按照found here 的帖子创建您的 PHP 文件,然后使用此答案中的.htaccess 配置似乎可以完美运行,并提供您可能要求的大部分功能。在我的案例中,对索引进行了严重的修改,下面显示了更重要的修改。

    switch( $path_info['call_parts'][0] ) {
        case 'about-us':
            include 'about.php';
        break;
        case 'users':
            include 'users.php';
        break;
        case 'news':
            include 'news.php';
        break;
        case 'products':
            include 'products.php';
        break;
        default:
            include 'front.php';
        break;
    }
    

    可以更改上述代码以创建更清洁的环境和更易于理解的配置。由于某些网站功能可以由请求的文件控制,因此建议将代码更改为更合适的替代方案。

    例如:您的所有请求都可以根据请求添加到包含一些变量的数组中。

    $_INCDIRECTORY = 'includes/';
    $_PATHINCLUDES = array(
        'dashboard'     => array(
            'allow'         => true,
            'path'          => $_INCDIRECTORY.'dashboard.php',
            // The following must remain FALSE; This value will be changed 'on-the-fly';
            'exists'        => false,
        ),
        'news'          => array(
            'allow'         => true,
            'path'          => $_INCDIRECTORY.'news.php',
            // The following must remain FALSE; This value will be changed 'on-the-fly';
            'exists'        => false,
        ),
        'search'        => array(
            'allow'         => true,
            'path'          => $_INCDIRECTORY.'search.php',
            // The following must remain FALSE; This value will be changed 'on-the-fly';
            'exists'        => false,
        ),
        'register'      => array(
            'allow'         => true,
            'path'          => $_INCDIRECTORY.'register.php',
            // The following must remain FALSE; This value will be changed 'on-the-fly';
            'exists'        => false,
        ),
        ####################################
        ## DENIED PAGES/DIRECTORIES ########
        ####################################
        'css'           => array(
            'allow'         => false,
            'path'          => 'css/',
            // The following must remain FALSE; This value will be changed 'on-the-fly';
            'exists'        => false,
        ),
        'files'             => array(
            'allow'         => false,
            'path'          => 'files/',
            // The following must remain FALSE; This value will be changed 'on-the-fly';
            'exists'        => false,
        ),
        ####################################
        ## ERROR PAGES #####################
        ####################################
        '403'           => array(
            'allow'         => true,
            'path'          => $_INCDIRECTORY.'errors/403.php',
            // The following must remain FALSE; This value will be changed 'on-the-fly';
            'exists'        => false,
        ),
        '404'           => array(
            'allow'         => true,
            'path'          => $_INCDIRECTORY.'errors/404.php',
            // The following must remain FALSE; This value will be changed 'on-the-fly';
            'exists'        => false,
        ),
    );
    

    初始数组包含请求的每个文件或目录的列表。然后,每个子数组都包含特定于请求的变量,这些变量稍后会控制包含脚本。 'allow' 定义是否允许用户查看请求。 true 会显示页面,false 会显示 403 错误。 'path' 定义了脚本请求的文件,当一切都是绿色的时候,在显示之前,另一个sn-p会检查该文件是否确实存在。 'exists' 在请求开始时始终为 false,以下更改。

    foreach( $_PATHINCLUDES as $key => &$value ) {
        ## Check to see if browsing the 'directory' is allowed
        ## before checking to see if the 'directory' exists;
        if( $value['allow'] == true ) {
            ## Browsing is allowed, check if 'directory'
            ## actually exists and set 'exists' to '=1';
            if( file_exists( $value['path'] ) == true ) {
                ## Set the value 'exists' to 'true';
                $value['exists'] = true;
            };
        } else {
            ## Browsing the 'directory' is not allowed,
            ## so the response should be a '403';
        };
    };
    unset( $value );
    

    上面的代码处理数组并将'exists'相应地设置为truefalse。提供'allow' 变量是true。当文件夹/文件的存在无关紧要时,检查用户无法访问的文件夹/文件是没有意义的。 .. as $key =&gt; &amp;$value .. 允许在 foreach( .. ) 循环内更改数组的值,unset( $value ); 删除该变量在循环后不被任何东西访问。 注意:请参阅this post 以供参考。

    为了防止任何令人讨厌的 PHP 错误,例如 requested offset [N] not set 等...使用以下代码。这会在尝试使用之前检查变量是否确实存在,如果不存在,则使用预定义的值。

    $_CALL_PART = isset( $path_info['call_parts'][0] ) ? $path_info['call_parts'][0] : 'dashboard';
    $_REQUESTED = isset( $_PATHINCLUDES[ $_CALL_PART ] ) ? $_PATHINCLUDES[ $_CALL_PART ] : $_PATHINCLUDES['404'];
    

    最终代码 sn -p 控制包含文件的行为。

    ## Check to see if access to the file is
    ## allowed before doing anything else;
    if( $_REQUESTED['allow'] == true ) {
        ## The file can be browsed to, so check if it exists;
        if( $_REQUESTED['exists'] == true ) {
            ## The file exists, so include it;
            include( $_REQUESTED['path'] );
        } else {
            ## The file does not exist, so display a 404 page;
            if( $_PATHINCLUDES['404']['exists'] == true ) {
                ## Check to ensure the file exists before including it;
                ## This prevents an error;
                include( $_PATHINCLUDES['404']['path'] );
            } else {
                ## The file does not exist, so simply display a message;
                echo '<h1>404 - File Not Found: No ErrorDocument supplied</h1>';
            };
        };
    } else {
        ## The file cannot be browsed to, so display a 403;
        if( $_PATHINCLUDES['403']['exists'] == true ) {
            ## Check to ensure the file exists before including it;
            ## This prevents an error;
            include( $_PATHINCLUDES['403']['path'] );
        } else {
            ## The file does not exist, so simply display a message;
            echo '<h1>403 - Forbidden: No ErrorDocument supplied</h1>';
        };
    };
    

    if( $_REQUESTED['allow'] == true ) { ... } else { ... } 检查请求是否被允许。如果是这样,则可以执行脚本的下一部分。 if( $_REQUESTED['exists'] == true ) { ... } else { ... } 检查请求的文件是否存在。如果是这样,脚本将执行 include( $_REQUESTED['path'] ); 以显示该页面。否则,脚本将返回一个 404,随后以相同的方式对其进行检查,不包括 allow 参数,然后显示它。如果前面的if( .. ) 语句要返回true,那么如果exists 参数返回为true,就会检查并显示403 错误。如果403404 页面均未找到,则脚本将显示基本错误响应。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-07-30
      • 1970-01-01
      • 2017-03-17
      • 2011-04-17
      • 2012-08-02
      • 1970-01-01
      • 2019-04-07
      • 1970-01-01
      相关资源
      最近更新 更多