繁体   English   中英

使用htaccess和PHP拒绝目录和文件访问

[英]Deny directory and file access with both htaccess and PHP

我目前正在为一个游戏团队开发一个Web项目,并且正在尝试测试自己并扩展我的常识和工具箱。 我正在使用的功能之一要求我“清除”所请求文件的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片段。 这些都是为了您的利益,从而减少了我添加代码段的需要。 我想它也会对您有帮助...如果需要,您可以直接引用行号。

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都存在,因此第12行和第13行阻止对其进行访问,并重定向到index.php ,可以在其中定位“目录”数组,并且可以选择要包含的相关文件。 数组的示例包含在帖子的底部。 12行和第13行可以移至第08行上方,并且没有任何变化,但是将它们从文件中删除会引发默认的403 Forbidden错误。

删除线08,09,1213时页面通过加载位于中的文件,打破imagefilesfonts目录, MIME type text/html ,但是,任何其他目录中的文件看似不受影响。

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

注意 :我不得不从第02行中删除-Indexes ,因为来自JavaScript文件的文件请求将被拒绝,这样做还阻止了第1213行按预期的方式进行,无论它们位于何处。

目录数组[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,
    ),
);

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

以下代码位于网站根目录下的.htaccess文件中。 在代码段之后,对每行的功能进行了描述。

.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 ,这不是index.php ,但是如果您的Web主机将Apache的默认设置更改为使用其他默认索引,则使用此方法可能是一个好习惯。

Options +FollowSymlinks -Indexes执行以下操作: +FollowSymlinks允许Apache'遵循符号链接',并且-Indexes防止在缺少索引的情况下列出任何子文件夹。{ext}。 注意 :从以前的配置中删除All将阻止脚本调​​用包含在排除项中,从而允许所有AJAX功能。

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

RewriteEngine On启用重写引擎。 将值设置为“ Off将禁用此功能,并且所有基础代码都将无法运行。

<IfModule mod_ssl.c> ... </IfModule>允许所包含的代码运行,前提是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检查请求是否使用httphttps ,还可以选择使用www. 然后检查网址的其余部分。 然后RewriteRule ^(.*?)(js|image|css|includes|php|files)(.*)$ index.php [L]检查所请求的文件夹是否包含在RegEx中,然后重定向到index.php ,PHP代码将在其中处理棘手的问题。 注意 :请参阅此帖子以供参考。

按照与上述代码相同的过程, RewriteCond %{REQUEST_FILENAME} -f执行相同的操作。 但是处理文件请求(如果存在)。 注意 :再次,请参阅此帖子以供参考。

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

PHP魔术

此处找到该文章之后,创建您的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'定义了脚本请求的文件,其他代码段检查了该文件是否存在,然后显示为绿色。 在请求开始时, 'exists'始终为假,随后将进行更改。

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 => &$value ..允许在foreach( .. )循环中更改数组的值, unset( $value ); 从循环之后的任何操作中删除该变量。 注意 :请参阅此帖子以供参考。

为了防止任何讨厌的PHP错误,例如未设置请求的offset [N]等,请使用以下代码。 这将在尝试使用变量之前检查该变量是否确实存在,如果不存在,则使用预定义的值。

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

最终代码段控制着所包含文件的行为。

## 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页面,则脚本将显示基本错误响应。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM