简体   繁体   English

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

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

I'm currently working on a web project for a gaming group, and I'm trying to test myself and expand my general knowledge and toolbox. 我目前正在为一个游戏团队开发一个Web项目,并且正在尝试测试自己并扩展我的常识和工具箱。 One of the features I'm working on requires me to "clean" the URL of a requested file. 我正在使用的功能之一要求我“清除”所请求文件的URL。 I have the system in place, and it works beautifully. 我已经安装了该系统,并且运行良好。 Now, in order for my website to work 'flawlessly', I need to deny access to certain folders, and all files within, through htaccess. 现在,为了使我的网站“完美无瑕”地工作,我需要通过htaccess拒绝访问某些文件夹以及其中的所有文件。 But there's a slight catch... It needs to operate without the user even realising. 但是有一个小问题……它需要在用户没有意识到的情况下进行操作。

My file structure, preferred access method, path, and status, are as follows: 我的文件结构,首选访问方法,路径和状态如下:

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}

The above list is actually an example of how I'd like the website to operate... The index.php file controls the requested 'files' and displays them accordingly. 上面的列表实际上是我希望网站运行的一个示例... index.php文件控制所请求的“文件”并相应地显示它们。 For example, %domain%/get-in-touch/ would display the contact.php file within the %domain%/includes/ directory. 例如, %domain%/get-in-touch/将在%domain%/includes/目录中显示contact.php文件。 Calling index.php directly displays the same as %domain%/dashboard/ . 调用index.php直接显示与%domain%/dashboard/

The following code is from my .htaccess file. 以下代码来自我的.htaccess文件。 Please, ignore the # NN snippets. 请忽略# NN片段。 These are for your benefit to reduce the need for me to add code snippets. 这些都是为了您的利益,从而减少了我添加代码段的需要。 I suppose it would help you, too... You could directly reference the line numbers should you need to. 我想它也会对您有帮助...如果需要,您可以直接引用行号。

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

I commented out line 06 as the system didn't seem to need it, but I kept it for my own reference and in the off chance that I may need it later. 我注释了第06行,因为该系统似乎不需要它,但我保留了该行以供我自己参考,并且不太可能以后再使用它。 Lines 08 through 10 control redirection to index.php when a file or a directory doesn't exist, and then the responding file controls the rest. 当文件或目录不存在时,第0810行控制重定向到index.php ,然后由响应文件控制其余文件。 The folders css/js/fonts/image/includes/php all exist, so lines 12 and 13 prevent access to them and redirect to index.php where the array of 'directories' can be located and the relevant files can be selected for inclusion. 文件夹css/js/fonts/image/includes/php都存在,因此第12行和第13行阻止对其进行访问,并重定向到index.php ,可以在其中定位“目录”数组,并且可以选择要包含的相关文件。 An example of the array is included at the bottom of the post. 数组的示例包含在帖子的底部。 Lines 12 and 13 can be moved above line 08 and there would be no change, but removing them from the file causes a default 403 Forbidden error to fire. 12行和第13行可以移至第08行上方,并且没有任何变化,但是将它们从文件中删除会引发默认的403 Forbidden错误。

Removing lines 08 , 09 , 12 and 13 causes the page to break by loading the files located in the image , files and fonts directories as MIME type text/html , however, the files within any other directories are seemingly unaffected. 删除线08,09,1213时页面通过加载位于中的文件,打破imagefilesfonts目录, MIME type text/html ,但是,任何其他目录中的文件看似不受影响。

Lines 12 and 13 perform as required, until you request access to a file within those directories. 1213行根据需要执行,直到您请求访问这些目录中的文件为止。 The file is displayed, and all of my secrecy is exposed to the world. 文件被显示,我所有的秘密都暴露给了世界。 As a result, certain files that have a great deal of importance are shown to the users. 结果,某些非常重要的文件会显示给用户。 I want to prevent anybody from accessing the files within any folder I choose to deny access to, and have it redirect them to index.php and show a 403 . 我想阻止任何人访问我选择拒绝访问的任何文件夹中的文件,并将其重定向到index.php并显示403 I've attempted it by adding RewriteCond %{REQUEST_FILENAME} -f between lines 12 and 13 , but the page shows as broken and fails to load resources. 我通过在第12行和第13行之间添加RewriteCond %{REQUEST_FILENAME} -f进行了尝试,但该页面显示为已损坏,无法加载资源。

NOTE : I had to remove -Indexes from line 02 as file requests from JavaScript files would be denied, doing this has also prevented lines 12 and 13 from doing as they were intended, regardless of their placement. 注意 :我不得不从第02行中删除-Indexes ,因为来自JavaScript文件的文件请求将被拒绝,这样做还阻止了第1213行按预期的方式进行,无论它们位于何处。

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

After spending a while searching for a solution, and the usual trial and error attempts at solving it, I have come up with a solution that works exactly how I want it to. 在花了一段时间寻找解决方案并尝试了常见的试验和错误尝试之后,我提出了一个完全可以按照我想要的方式工作的解决方案。 It seemed more fitting to answer my own question for reference to anybody who should come across the same issue. 回答我自己的问题以供任何遇到相同问题的人参考似乎更合适。

The following code rests within the .htaccess file at the root directory of your website. 以下代码位于网站根目录下的.htaccess文件中。 A description of what each line does follows after the snippet. 在代码段之后,对每行的功能进行了描述。

The .htaccess Trickery .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 defines the primary 'homepage' of the website to index.php , this is not a requirement but it may be good practice to use this should your web-host change the default settings for Apache to use a different default index. DirectoryIndex index.php将网站的主要“主页”定义为index.php ,这不是index.php ,但是如果您的Web主机将Apache的默认设置更改为使用其他默认索引,则使用此方法可能是一个好习惯。

Options +FollowSymlinks -Indexes does the following: +FollowSymlinks allows Apache to 'follow symbolic links', and -Indexes prevents any subfolders from being listed should an index.{ext} be missing. Options +FollowSymlinks -Indexes执行以下操作: +FollowSymlinks允许Apache'遵循符号链接',并且-Indexes防止在缺少索引的情况下列出任何子文件夹。{ext}。 NOTE : Removing All from the previous configuration prevented script calls from being included within the exclusion, allowing all AJAX functionality. 注意 :从以前的配置中删除All将阻止脚本调​​用包含在排除项中,从而允许所有AJAX功能。

<IfModule mod_rewrite.c> ... </IfModule> allows the basis of the htaccess file to function without the Rewrite Module being enabled within your Apache config. <IfModule mod_rewrite.c> ... </IfModule>允许htaccess文件的基础运行,而无需在Apache配置中启用重写模块。

RewriteEngine On enables the Rewrite Engine. RewriteEngine On启用重写引擎。 Setting the value to Off disables this feature, and none of the underlying code will operate. 将值设置为“ Off将禁用此功能,并且所有基础代码都将无法运行。

<IfModule mod_ssl.c> ... </IfModule> allows the contained code to function, providing the Apache installation includes, and uses, the SSL Module. <IfModule mod_ssl.c> ... </IfModule>允许所包含的代码运行,前提是Apache安装包括并使用SSL模块。

RewriteCond %{HTTPS} off then checks to see if %{HTTPS} is being used by the request, and then RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R=301,NC,L] permanently redirects the user to an SSL enabled URI. RewriteCond %{HTTPS} off然后检查是否%{HTTPS}正在使用由所述请求,然后RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R=301,NC,L]将用户永久重定向到启用SSL的URI。 %{HTTP_HOST}%{REQUEST_URI} simply allows the same functionality over multiple websites without the need of a reconfiguration. %{HTTP_HOST}%{REQUEST_URI}仅允许在多个网站上使用相同的功能,而无需重新配置。

RewriteCond %{REQUEST_FILENAME} -d checks to see if the requested directory exists, RewriteCond %{HTTP_REFERER} !^((((https|http)://([w]{3}\\.)?)([a-z0-9\\.]+)/).*)$ [NC] then checks to see if the referrer was NOT requesting a full URL, basically a domain name. RewriteCond %{REQUEST_FILENAME} -d检查以查看请求的目录是否存在, RewriteCond %{HTTP_REFERER} !^((((https|http)://([w]{3}\\.)?)([a-z0-9\\.]+)/).*)$ [NC]然后检查是否引荐并没有要求一个完整的URL,基本上是一个域名。 The RegEx following RewriteCond checks to see if the request uses http or https , optionally uses www. RewriteCond的RegEx检查请求是否使用httphttps ,还可以选择使用www. and then checks the rest of the URL. 然后检查网址的其余部分。 Then RewriteRule ^(.*?)(js|image|css|includes|php|files)(.*)$ index.php [L] checks to see if any of the folders requested were included within the RegEx and then redirects to index.php where the PHP code will handle the tricky stuff. 然后RewriteRule ^(.*?)(js|image|css|includes|php|files)(.*)$ index.php [L]检查所请求的文件夹是否包含在RegEx中,然后重定向到index.php ,PHP代码将在其中处理棘手的问题。 NOTE : See this post for reference. 注意 :请参阅此帖子以供参考。

Following the same course as the above code, RewriteCond %{REQUEST_FILENAME} -f does the same. 按照与上述代码相同的过程, RewriteCond %{REQUEST_FILENAME} -f执行相同的操作。 But handles file requests, should they exist. 但是处理文件请求(如果存在)。 NOTE : Again, see this post for reference. 注意 :再次,请参阅此帖子以供参考。

RewriteCond %{REQUEST_FILENAME} !-d and RewriteCond %{REQUEST_FILENAME} !-f check to see whether the requested file were a directory or a file and whether they existed, or not, and then RewriteRule ^(.*?)$ index.php [QSA,L] redirected the entire request to the index.php where the request would be correctly handled by PHP. RewriteCond %{REQUEST_FILENAME} !-dRewriteCond %{REQUEST_FILENAME} !-f检查所请求的文件是目录还是文件,以及它们是否存在,然后检查RewriteRule ^(.*?)$ index.php [QSA,L]将整个请求重定向到index.php ,PHP将在其中正确处理该请求。 NOTE : To those who didn't already know this, the ! 注意 :对于那些还不知道的人, ! character can be used in most cases as a shorthand for not . 字符在大多数情况下可以作为not的简写。 NOTE : See this page for a mod_rewrite variables cheatsheet. 注意 :有关mod_rewrite变量备忘单,请参见此页面 And this tool for RegEx testing. 此工具用于RegEx测试。

The PHP Magic PHP魔术

Following the post, found here , to create your PHP file and then use the .htaccess config from within this answer would seemingly work perfectly, and provide the majority of the functionality that you may request. 此处找到该文章之后,创建您的PHP文件,然后在此答案中使用.htaccess配置似乎可以完美地工作,并提供您可能要求的大多数功能。 Severe modifications to the index have been made in my case, the more important ones are displayed below. 在我的情况下,已经对该索引进行了严重修改,下面显示了更重要的修改。

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;
}

The above code can be changed to create a cleaner environment and an easier to understand configuration. 可以更改以上代码以创建更干净的环境和更易于理解的配置。 Since some of the website functionality can be controlled by the requested files, changing the code to a more suitable alternative would be advised. 由于某些网站功能可以由请求的文件控制,因此建议将代码更改为更合适的替代方法。

For example: All of your requests can be added to an array that contains a few variables based on the request. 例如:您的所有请求都可以添加到包含一些基于请求的变量的数组中。

$_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,
    ),
);

The initial array contains a list of each file, or directory, of the request. 初始数组包含请求的每个文件或目录的列表。 Each sub-array then contains request-specific variables that control the inclusion script later on. 然后,每个子数组都包含特定于请求的变量,这些变量稍后将控制包含脚本。 'allow' defines whether the user is allowed to view the request. 'allow'定义是否允许用户查看请求。 true would display the page, and false would display a 403 error. true将显示页面,而false将显示403错误。 'path' defines the file that is requested by the script, the file is checked by another snippet to see if it actually exists before being displayed when everything is green. 'path'定义了脚本请求的文件,其他代码段检查了该文件是否存在,然后显示为绿色。 'exists' is always false at the beginning of the request, the following changes that. 在请求开始时, '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 );

The above code handles the array and sets 'exists' to true or false accordingly. 上面的代码处理该数组,并相应地将'exists'truefalse Providing the 'allow' variable is true . 提供'allow'变量为true There is no point in checking a folder/file the user can't access when its existence doesn't matter. 当文件夹/文件的存在无关紧要时,检查用户无法访问的文件夹/文件毫无意义。 .. as $key => &$value .. allows the value of the array to be changed within the foreach( .. ) loop, unset( $value ); .. as $key => &$value ..允许在foreach( .. )循环中更改数组的值, unset( $value ); removes the variable from being accessed by anything after the loop. 从循环之后的任何操作中删除该变量。 NOTE : See this post for reference. 注意 :请参阅此帖子以供参考。

To prevent any nasty PHP errors, such as requested offset [N] not set , etc... Use the following code. 为了防止任何讨厌的PHP错误,例如未设置请求的offset [N]等,请使用以下代码。 This checks that the variable actually exists before attempting to use it, if it doesn't exist, then it uses a predefined value. 这将在尝试使用变量之前检查该变量是否确实存在,如果不存在,则使用预定义的值。

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

The final code snippet controls the behavior of the included file. 最终代码段控制着所包含文件的行为。

## 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 { ... } checks to see whether the request is allowed. if( $_REQUESTED['allow'] == true ) { ... } else { ... }检查是否允许该请求。 If so, the next section of the script can be executed. 如果是这样,可以执行脚本的下一部分。 if( $_REQUESTED['exists'] == true ) { ... } else { ... } checks whether the requested file exists. if( $_REQUESTED['exists'] == true ) { ... } else { ... }检查请求的文件是否存在。 If so, the script executes include( $_REQUESTED['path'] ); 如果是这样,脚本将执行include( $_REQUESTED['path'] ); which displays the page. 显示页面。 Otherwise, the script then returns a 404 which is subsequently checked in the same manner, excluding the allow parameter, and then displays it. 否则,脚本然后返回404,随后以相同的方式检查此文本(不包括allow参数),然后显示它。 If the prior if( .. ) statement was to return true , then the 403 error would be checked and displayed if the exists parameter returned as true. 如果先前的if( .. )语句返回true ,则将在exists参数返回为true的情况下检查并显示403错误。 If neither the 403 and the 404 pages could be found, then the script would show a basic error response. 如果找不到403404页面,则脚本将显示基本错误响应。

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

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