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:
- An Rcs Ubuntu 20.04 server.
- A sudo user.
- A LAMP Stack. You can also follow this tutorial on a Rcs One-Click LAMP server.
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.confFind 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 rewriteRestart the Apache web server to refresh the settings that you've changed.
$ sudo systemctl restart apache2Create 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.phpEnter 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/.htaccessAdd the line below into the file.
SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1Save 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.phpYou 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.phpWith 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.phpYou 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.