Keeping your web application secure is a continuous effort. One basic yet effective technique is controlling access based on IP addresses. Whether you need to block malicious actors or restrict access to a specific set of users, managing IP addresses can be crucial.
In this article, we'll explore how to implement IP blocking and whitelisting in your PHP applications using Object-Oriented Programming (OOP). This approach provides a clean, reusable, and easy-to-understand way to manage access control.
The IPBlocker
Class: Denying Access
Let's start by creating an IPBlocker
class. This class will read a list of IPs from a text file and block any incoming requests from those IPs.
class IPBlocker {
private $blockedIPs = [];
private $blockedIPFile;
public function __construct(string $blockedIPFile) {
$this->blockedIPFile = $blockedIPFile;
$this->loadBlockedIPs();
}
private function loadBlockedIPs(): void {
if (!file_exists($this->blockedIPFile) || !is_readable($this->blockedIPFile)) {
error_log("Error: Could not read blocked IP file: " . $this->blockedIPFile);
return;
}
$ips = file($this->blockedIPFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
if ($ips !== false) {
$this->blockedIPs = array_map('trim', $ips);
} else {
error_log("Error: Failed to read lines from blocked IP file: " . $this->blockedIPFile);
}
}
private function getClientIP(): ?string {
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
$ip = $_SERVER['HTTP_CLIENT_IP'];
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
} else {
$ip = $_SERVER['REMOTE_ADDR'];
}
return filter_var($ip, FILTER_VALIDATE_IP);
}
private function isIPBlocked(string $ip): bool {
return in_array($ip, $this->blockedIPs, true);
}
public function blockRequest(): void {
$clientIP = $this->getClientIP();
if ($clientIP && $this->isIPBlocked($clientIP)) {
header('HTTP/1.1 403 Forbidden');
echo "Your IP address has been blocked from accessing this website.";
exit;
}
}
}
// Example usage:
$blockedIPFile = 'blocked_ips.txt';
$ipBlocker = new IPBlocker($blockedIPFile);
$ipBlocker->blockRequest();
// The rest of your application code goes here
?>
How it works:
- The
IPBlocker
class takes the path to a text file (blocked_ips.txt
) in its constructor. This file should contain one IP address per line. - The
loadBlockedIPs()
method reads the IPs from the file into the$blockedIPs
array. - The
getClientIP()
method attempts to retrieve the visitor's IP address, handling cases where the client might be behind a proxy. - The
isIPBlocked()
method checks if the client's IP exists in the$blockedIPs
array. - The
blockRequest()
method orchestrates the process: it gets the client's IP and, if it's in the blocked list, sends a403 Forbidden
header and a message before terminating the script.
To use this:
- Create a text file named
blocked_ips.txt
in the same directory as your PHP script. - Add the IP addresses you want to block to this file, one per line.
- Include and instantiate the
IPBlocker
class at the very top of your PHP files, calling theblockRequest()
method.
The IPAllowlist
Class: Granting Access
Now, let's create an IPAllowlist
class to allow only specific IPs to access your website.
class IPAllowlist {
private $allowedIPs = [];
private $allowedIPFile;
private $blockMessage = "Your IP address is not allowed to access this website.";
private $blockStatusCode = 403;
public function __construct(string $allowedIPFile) {
$this->allowedIPFile = $allowedIPFile;
$this->loadAllowedIPs();
}
public function setBlockMessage(string $message): void {
$this->blockMessage = $message;
}
public function setBlockStatusCode(int $statusCode): void {
$this->blockStatusCode = $statusCode;
}
private function loadAllowedIPs(): void {
if (!file_exists($this->allowedIPFile) || !is_readable($this->allowedIPFile)) {
error_log("Error: Could not read allowed IP file: " . $this->allowedIPFile);
return;
}
$ips = file($this->allowedIPFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
if ($ips !== false) {
$this->allowedIPs = array_map('trim', $ips);
} else {
error_log("Error: Failed to read lines from allowed IP file: " . $this->allowedIPFile);
}
}
private function getClientIP(): ?string {
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
$ip = $_SERVER['HTTP_CLIENT_IP'];
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
} else {
$ip = $_SERVER['REMOTE_ADDR'];
}
return filter_var($ip, FILTER_VALIDATE_IP);
}
private function isIPAllowed(): bool {
$clientIP = $this->getClientIP();
return ($clientIP && in_array($clientIP, $this->allowedIPs, true));
}
public function checkRequest(): void {
if (!$this->isIPAllowed()) {
header("HTTP/1.1 " . $this->blockStatusCode . " Forbidden");
echo $this->blockMessage;
exit;
}
}
}
// Example usage:
$allowedIPFile = 'allowed_ips.txt';
$ipAllowlist = new IPAllowlist($allowedIPFile);
$ipAllowlist->checkRequest();
// The rest of your application code goes here
?>
How it works:
The IPAllowlist
class functions similarly to the IPBlocker
:
- It takes the path to an
allowed_ips.txt
file in its constructor. -
loadAllowedIPs()
reads the allowed IPs from the file. -
getClientIP()
retrieves the visitor's IP. -
isIPAllowed()
checks if the client's IP is in the allowed list. -
checkRequest()
verifies if the IP is allowed. If not, it sends a "403 Forbidden" response and stops execution.
To use this:
- Create a text file named
allowed_ips.txt
. - Add the IP addresses you want to allow to this file, one per line.
- Include and instantiate the
IPAllowlist
class at the beginning of your PHP scripts, calling thecheckRequest()
method.
Combining Blocking and Allowing
You might need both blocking and allowing logic in your application. You can achieve this by instantiating both classes. However, you'll need to decide on the order of execution and how to handle cases where an IP might be in both lists (though this is generally not a recommended setup).
A more robust solution for complex scenarios might involve a single class that manages both blocked and allowed IPs, potentially with a priority setting (e.g., always block if in the blacklist, even if in the whitelist).
Further Enhancements
Here are some ideas for extending these classes:
- Database Storage: Instead of text files, store the blocked and allowed IPs in a database for easier management and scalability.
- IP Range Blocking/Allowing: Implement logic to handle IP address ranges using techniques like CIDR notation.
- Logging: Log blocked access attempts for security auditing.
- Configuration: Load file paths and messages from a configuration file.
- Error Handling: Implement more sophisticated error handling, perhaps throwing exceptions instead of just logging errors.
Conclusion
These simple PHP OOP classes provide a foundation for implementing IP-based access control in your web applications. By reading IP lists from external files, you can easily manage who can and cannot access your site. Remember to always consider the specific security needs of your application and explore more advanced techniques as required.
By leveraging the power of OOP, we've created reusable components that can be easily integrated into your PHP projects to enhance their security posture. Happy coding!