sassy_rog
sassy_rog

Reputation: 1097

How to use apache 403 error as routing technique with htacess

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.

Upvotes: 0

Views: 460

Answers (1)

Alessandro
Alessandro

Reputation: 898

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.

Upvotes: 1

Related Questions