Secure PHP Resource with HTTP Authentication
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 Vultr Ubuntu 20.04 server.
- A sudo user.
- A LAMP Stack. You can also follow this tutorial on a Vultr 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.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.