0

I have a simple php web application with this directory structure

.
├── custom_403.html
├── custom_404.html
├── home
│   └── home.php
├── .htaccess
├── index.php
├── login
│   └── login.php
├── public
│   ├── bootstrap
│   ├── css
│   ├── error_pages
│   ├── fontawesome
│   ├── .htaccess
│   └── js
├── signup
│   └── signup.php
├── test.php

I made an .htacess file that basically looks like this

ErrorDocument 404 /public/error_pages/404.php
ErrorDocument 500 /public/error_pages/500.php
ErrorDocument 403 /index.php

When I type something like http://localhost/login/ it should give me a 403 Forbidden error because the directory login exists but doesn't have the file index.php. According to my .htacess it should serve the default index.php file.I take advantage of this on the index.php to load the file in the specified path from the adress bar using jquery. I search for window.location.pathname to load the corresponding file from that path with a resolveUrl() function.

Here's an example

<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>

  <!-- Boostrap 4.3 -->
  <link rel="stylesheet" href="/public/bootstrap/css/bootstrap.css">
  <!-- Fontawesome 5 Free -->
  <link rel="stylesheet" href="/public/fontawesome/css/all.css">
  <link rel="stylesheet" href="/public/css/style.css">
  <script src="/public/js/jquery.js"></script>
  <script src="/public/bootstrap/js/bootstrap.js"></script>
</head>

<body>
  <nav class="navbar navbar-expand-lg r-shadow">
    <a class="navbar-brand" href="#"><img width="50" class="d-inline-block align-top" src="https://seeklogo.com/images/M/marvel-comics-logo-31D9B4C7FB-seeklogo.com.png" alt="" srcset=""></a>
    <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarText" aria-controls="navbarText" aria-expanded="false" aria-label="Toggle navigation">
      <span class="navbar-toggler-icon"></span>
    </button>
    <div class="collapse navbar-collapse justify-content-end" id="navbarText">
      <ul class="navbar-nav">
        <li class="nav-item active">
          <a class="nav-link" href="home">Home</span></a>
        </li>
        <li class="nav-item">
          <a class="nav-link" href="login">Login</a>
        </li>
        <li class="nav-item">
          <a class="nav-link" href="signup">Sign Up</a>
        </li>
      </ul>
  </nav>
  <div class="root">
    <div class="container mt-3 rounded" style="background-color: rgba(255, 255, 255, 0.1); ">dcfdcf</div>
    <div id="loader">
      <div class="spinner"><i class="fas fa-spinner fa-spin"></i></div>
    </div>
  </div>
</body>
<script>
  function resolveUrl(url) {
    if (url) {
      var path = url.replace(/\//gi, '').trim();
      $(".container").load("/" + path + "/" + path + ".php");
      return path;
    } else {
      $(".container").load("/home/home.php");
      console.log(path)
    }
  }

  function resolveActiveTab(pathName) {
    $(".nav-item").removeClass("active");
    if (pathName) {
      $("li > a[href='" + pathName + "']").parent().addClass("active");
    } else {
      $("li > a").first().parent().addClass("active");
    }
  }

  $(document).ready(function () {
    console.log(window.location.pathname)
    var currentPath = resolveUrl(window.location.pathname);
    resolveActiveTab(currentPath)

    $(document).ajaxStart(function () {
      $("#loader").css({
        "display": "block"
      });
    })
    $(document).ajaxStop(function () {
      $("#loader").css({
        "display": "none"
      });
    })

    $('.nav-link').click(function (e) {
      e.preventDefault();
      var path = $(this).attr("href");

      $(".container").load("/" + path + "/" + path + ".php");
      resolveActiveTab(path)
      window.history.pushState("", "", "/" + path);
    })
  })
</script>

</html>

this means that if the refresh the page with a path like http://localhost/login/ the jquery will load login.php.

This works fine except I get an error on the apache log that looks like this

AH01276: Cannot serve directory /var/www/html/test/login/: No matching DirectoryIndex (index.php,index.html,index.cgi,index.pl,index.xhtml,index.htm) found, and server-generated directory index forbidden by Options directive

When I try to solve this problem by changing the filenames from login.php signup.php etc to index.php I get served those files back since they do exist, meaning I won't get the 403 error. I tried denying access to these specific files in my .htaccess but then they are denied for everything including the jquery .load() function. My question is how do what I'm doing efficiently without the apache error.

sassy_rog
  • 1,077
  • 12
  • 30

1 Answers1

1

Your .htaccess is wrong, you are using ErrorDocument 403 to load a file index.php, (ErrorDocument is used normally for custom error pages not for loading a normal resource) you wish have a Forbidden when try to load login/ because this folder doesn't have a index.php file but I suspect that you have in your VirtualHost the option Indexes, this directive loads the folder contents but doesn't serve a 403 error.

My VirtualHost for testing purpose is:

<VirtualHost localhost-test:80>
  ServerName localhost-test
  ServerAlias localhost-test
  ErrorLog "logs/localhost-test-error.log"
  TransferLog "logs/localhost-test-access.log"
  DocumentRoot "D:/Web/test"
    <Directory />
      Require all granted
      Options FollowSymLinks Includes ExecCGI
      AllowOverride All
    </Directory>
</VirtualHost>

If you remove Indexes from your VirtualHost you can see the Forbidden error custom page. You should have:

ErrorDocument 404 /public/error_pages/404.php
ErrorDocument 500 /public/error_pages/500.php
ErrorDocument 403 /public/error_pages/403.php

Now if you try to load the folder /login/ without an index.php file inside, you should have a Forbidden 403 with your custom page error but using JQuery in meanwhile you can load the login.php file instead.

I hope this helps.

Alessandro
  • 900
  • 12
  • 23
  • I have familiarity with everything you have said but my problem is I want to use folder names as paths/routes in my application. So when someone loads `http://localhost/login` it doesn't try to access the login folder but but my jquery loads the content from `login/login.php` – sassy_rog May 04 '19 at 16:17
  • See this: https://stackoverflow.com/questions/5301776/directory-index-forbidden-by-options-directive – Alessandro May 04 '19 at 16:22
  • See this: https://stackoverflow.com/questions/21346486/how-to-show-directory-index-in-apache-2-4-with-custom-document-root – Alessandro May 04 '19 at 16:25
  • Using option `Indexes` in your `VirtualHost` or `.htaccess` you can fix the error in the log but you cannot get `Forbidden 403` when you are accessing the folder structure... – Alessandro May 04 '19 at 17:30
  • Did you try to put inside the folder login a index.php with header("location: login.php"); ? – Alessandro May 04 '19 at 18:36
  • 1
    Did you try to put inside the folder `login` a file `index.php` and call it like `index.php?option=login` and `index.php?option=signup` and `index.php?option=home` ? Then using `switch` you perform some actions eg `case 'login': header("Location: login.php"); break; case 'signup': header("Location: signup.php"); break; case 'home': header("Location: ../index.php"); break;` etcetera. Mine is only and idea. – Alessandro May 04 '19 at 19:25
  • Yes, the problem of someone easily accessing the files inside the, say `login` by easily typing them out on the adress bar. – sassy_rog May 05 '19 at 08:40
  • 1
    What do you think about doing a `referer` check instead? For example, if the `login.php` file has `www/login/index.php` as referer it is ok, while if the `referer` is empty it means that the user has direct access to the `login.php` file and in this case after a check put a beautiful `exit();` `if (!isset($_SERVER['HTTP_REFERER'])) { exit("Direct Access to this File not Allowed"); }` – Alessandro May 05 '19 at 17:47
  • Thank you I managed to find a solution which turned out to be there the entire time, I'm just not familiar with apache. It's the ReWrite engine in `.htaccess`. `RewriteEngine On # Turn on the rewriting engine RewriteRule ^(login|signup|home)/?$ index.php [NC,L] ` this helped a lot. But you are right on checking with php what type of request I'm looking at. My work around was `if (isset($_SERVER['HTTP_X_REQUESTED_WITH'])) { if(!$_SERVER['HTTP_X_REQUESTED_WITH']== "XMLHttpRequest"){` – sassy_rog May 05 '19 at 18:24
  • Use `strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest'` instead... `XMLHttpRequest` name can be different... Glad to help. – Alessandro May 06 '19 at 17:57