Secure PHP Resource with HTTP Authentication

Updated on February 20, 2021
Secure PHP Resource with HTTP Authentication header image

Introduction

HTTP authentication is the process of identifying and verifying users' credentials to make sure they are permitted to access a web resource. To prevent unauthorized access to your PHP web application, you can protect sensitive files or endpoints using a username and a password. One common way of achieving this is by using basic HTTP authentication. In this security design pattern, every user making a request to a resource on your server must submit an Authorization header containing Base64 encoded credentials.

The Base64 algorithm used in this context is for encoding non-HTTP compatible characters submitted by users to accepted values, and you should not use it as an encryption alternative. You would force your users to pass their credentials via secure HTTPS in a production environment.

In this guide, you'll create a password-protected PHP web resource on Ubuntu 20.04 server. This endpoint will return data for the available products in a JSON (JavaScript Object Notation) format when the user is authenticated. However, users submitting empty or invalid credentials will get an error.

Prerequisites

To follow along with this tutorial, make sure you have the following:

Edit the Apache Configuration File

SSH to your server and start by editing the Apache configuration file. For HTTP authentication to work, Apache should allow the .htaccess file to override main settings.

Open the /etc/apache2/apache2.conf file.

$ sudo nano /etc/apache2/apache2.conf

Find the content below.

<Directory /var/www/>
        Options Indexes FollowSymLinks
        AllowOverride None
        Require all granted
</Directory>

Change the AllowOverride None directive to AllowOverride All as shown below.

<Directory /var/www/>
        Options Indexes FollowSymLinks
        AllowOverride All
        Require all granted
</Directory>

Save and close the file.

Enable Apache mod_rewrite Module

Enable Apache mod_rewrite module. This is a rule-based engine that rewrites incoming requests to the server. You'll require this module to retrieve the values of the HTTP_AUTHORIZATION header in your PHP files.

$ sudo a2enmod rewrite

Restart the Apache web server to refresh the settings that you've changed.

$ sudo systemctl restart apache2

Create a Password Protected PHP File

The next step is creating a PHP web resource that displays data in JSON format for authenticated users. Open a new /var/www/html/sample.php file at the root directory of your web server.

$ sudo nano /var/www/html/sample.php

Enter the information below into the file. Remember to replace EXAMPLE_PASSWORD with a strong value.

<?php

header('Content-Type: application/json');

list($username, $password) = explode(':', base64_decode(substr($_SERVER['HTTP_AUTHORIZATION'], 6)));

if ($username != 'john' || $password != 'EXAMPLE_PASSWORD') {

    header("HTTP/1.1 401 Unauthorized");

    $error = [
             'title'      => 'Authentication failed',
             'message'    => 'Invalid username or password',
             'error_code' => '401',
             ];
    echo json_encode($error , JSON_PRETTY_PRINT);

} else {

    $products = [];

    $products[] = [
                  'product_id'   => 1,
                  'product_name' => 'WIRELESS KEYBOARD',
                  'retail_price' => '44.80',
                  ];

    $products[] = [
                  'product_id'   => 2,
                  'product_name' => 'LAPTOP BAG',
                  'retail_price' => '28.70',
                  ];

    $products[] = [
                  'product_id'   => 3,
                  'product_name' => 'MOUSE PAD',
                  'retail_price' => '5.67',
                  ];

    echo json_encode($products, JSON_PRETTY_PRINT);
}

Save and close the file.

The PHP code explained:

header('Content-Type: application/json');

The above line sends a header to a browser to format the data in the expected JSON format.

list($username, $password) = explode(':', base64_decode(substr($_SERVER['HTTP_AUTHORIZATION'], 6)));

The code snippet above decodes the Base64 encoded credentials submitted by users to two variables($username and $password).

...
if ($username != 'john' || $password != 'EXAMPLE_PASSWORD') {
    header("HTTP/1.1 401 Unauthorized");
    $error = [
             'title'      => 'Authentication failed',
             'message'    => 'Invalid username or password',
             'error_code' => '401',
             ];
    echo json_encode($error , JSON_PRETTY_PRINT);

} else {
...
}
...

In the above code, you've used the PHP logical if (...) {...} else {...} statement to check if the user is submitting the required username and password combination. In this case, the correct user will be authenticated by the username john and EXAMPLE_PASSWORD as the password. In case those values don't match, you're throwing a JSON encoded error and a 401 header informing the user that authentication has failed.

...
if ($username != 'john' && $password != 'EXAMPLE_PASSWORD') {
...
} else {

    $products = [];

    $products[] = [
                  'product_id'   => 1,
                  'product_name' => 'WIRELESS KEYBOARD',
                  'retail_price' => '44.80',
                  ];
    ...

    echo json_encode($products, JSON_PRETTY_PRINT);
    ...
 }

In the code above, you're creating an array of products and displaying them as a JSON encoded string for the authenticated users.

Modify the .htaccess File

Modify the .htaccess file for the HTTP authentication to work. Open the /var/www/html/.htaccess file.

$ sudo nano /var/www/html/.htaccess

Add the line below into the file.

SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1

Save and close the file. The rewrite rule above instructs Apache to copy the value of $_ENV['HTTP_AUTHORIZATION'] into the $_SERVER['HTTP_AUTHORIZATION'] variable. Remember, you are retrieving this value in the /var/www/html/sample.php file to get encoded users' credentials.

Test HTTP Authentication

After you've modified the Apache settings, created a password-protected resource, and modified the .htaccess file, the next step is testing if everything is working as expected.

Use Linux curl command to request the resource as shown below. In the first attempt, don't submit any credentials to check if you'll get an error. The -i option instructs curl to include the HTTP response headers when displaying the output.

$ curl -i -H 'Accept:application/json' localhost/sample.php

You should get an output similar to the one shown below confirming that you're not authenticated.

HTTP/1.1 401 Unauthorized
Date: Tue, 26 Jan 2021 09:27:19 GMT
Server: Apache/2.4.41 (Ubuntu)
Content-Length: 121
Content-Type: application/json

{
    "title": "Unauthorized",
    "message": "You are not authorized to access this resource",
    "error_code": "401"
}

Try to access the resource with the correct credentials. The -u option converts the username and password to a Base64 encoded string under the hood when submitting the request to the server.

$ curl -u john:EXAMPLE_PASSWORD -i -H 'Accept:application/json' localhost/sample.php

With the correct credentials, you should now get a list of products, as shown below.

HTTP/1.1 200 OK
Date: Tue, 26 Jan 2021 09:30:50 GMT
Server: Apache/2.4.41 (Ubuntu)
Content-Length: 331
Content-Type: application/json

[
    {
        "product_id": 1,
        "product_name": "WIRELESS KEYBOARD",
        "retail_price": "44.80"
    },
    {
        "product_id": 2,
        "product_name": "LAPTOP BAG",
        "retail_price": "28.70"
    },
    {
        "product_id": 3,
        "product_name": "MOUSE PAD",
        "retail_price": "5.67"
    }
]

Repeat the same process with some incorrect credentials and see if you get an error.

$ curl -u john:WRONG_PASSWORD -i -H 'Accept:application/json' localhost/sample.php
$ curl -u wrong_username:EXAMPLE_PASSWORD -i -H 'Accept:application/json' localhost/sample.php

You should get an error in both cases.

HTTP/1.1 401 Unauthorized
Date: Tue, 26 Jan 2021 09:27:19 GMT
Server: Apache/2.4.41 (Ubuntu)
Content-Length: 121
Content-Type: application/json

{
    "title": "Unauthorized",
    "message": "You are not authorized to access this resource",
    "error_code": "401"
}

Conclusion

In this tutorial, you've secured a PHP web resource with HTTP Authentication on Ubuntu 20.04 server. To take your web server security even a step further, consider securing Apache with TLS/SSL Certificate.