-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathddos_protection.php
More file actions
232 lines (208 loc) · 9.31 KB
/
ddos_protection.php
File metadata and controls
232 lines (208 loc) · 9.31 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
<?php
// ddos_protection.php
// Author: SourceCode347 (with fix for ban file creation to avoid first-visit error)
// === Configuration ===
$ddos_config = [
'limit' => 10, // Maximum number of requests
'window' => 60, // Time window (in seconds)
'ban_attempts' => 30, // Number of failed attempts before banning
'ban_duration' => 86400, // Ban duration (24 hours = 86400 seconds)
'base_dir' => dirname(__FILE__) . '/', // Directory of this script
'ban_file' => dirname(__FILE__) . '/banned_ips.log', // Banned IPs file
];
// Enable error logging
ini_set('log_errors', 1);
ini_set('error_log', dirname(__FILE__) . '/php_errors.log');
// Get user's IP address
function ddos_get_client_ip() {
$ip = isset($_SERVER['REMOTE_ADDR']) ? filter_var($_SERVER['REMOTE_ADDR'], FILTER_VALIDATE_IP) : '';
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$forwarded_ips = array_map('trim', explode(',', filter_var($_SERVER['HTTP_X_FORWARDED_FOR'], FILTER_SANITIZE_STRING)));
$ip = filter_var($forwarded_ips[0], FILTER_VALIDATE_IP) ?: $ip;
} elseif (isset($_SERVER['HTTP_CF_CONNECTING_IP'])) {
$ip = filter_var($_SERVER['HTTP_CF_CONNECTING_IP'], FILTER_VALIDATE_IP) ?: $ip;
}
if (empty($ip)) {
error_log('DDoS Protection: No valid IP detected. REMOTE_ADDR: ' . ($_SERVER['REMOTE_ADDR'] ?? 'none') . ', X_FORWARDED_FOR: ' . ($_SERVER['HTTP_X_FORWARDED_FOR'] ?? 'none') . ', CF_CONNECTING_IP: ' . ($_SERVER['HTTP_CF_CONNECTING_IP'] ?? 'none'));
}
return $ip;
}
// Check if the base directory is writable
function ddos_ensure_base_dir($base_dir) {
if (!is_writable($base_dir)) {
error_log('DDoS Protection: Directory not writable: ' . $base_dir . ' (Permissions: ' . substr(sprintf('%o', fileperms($base_dir)), -4) . ', Owner: ' . posix_getpwuid(fileowner($base_dir))['name'] . ')');
return false;
}
return true;
}
// Ensure ban file exists and is writable (use touch for reliable creation)
function ddos_ensure_ban_file($ban_file) {
if (!file_exists($ban_file)) {
// Use touch to create the file
if (!touch($ban_file)) {
error_log('DDoS Protection: Failed to touch ban file: ' . $ban_file . ' (Error: ' . (error_get_last()['message'] ?? 'Unknown') . ')');
// Fallback: Try file_put_contents with an empty line
if (!file_put_contents($ban_file, "\n", LOCK_EX)) {
error_log('DDoS Protection: Fallback failed to create ban file: ' . $ban_file . ' (Error: ' . (error_get_last()['message'] ?? 'Unknown') . ')');
return false; // Don't exit, allow script to continue
}
error_log('DDoS Protection: Ban file created via fallback: ' . $ban_file);
} else {
error_log('DDoS Protection: Ban file created via touch: ' . $ban_file);
}
// Set permissions
chmod($ban_file, 0664);
}
if (!is_writable($ban_file)) {
error_log('DDoS Protection: Ban file not writable: ' . $ban_file . ' (Permissions: ' . substr(sprintf('%o', fileperms($ban_file)), -4) . ')');
return false;
}
return true;
}
// Check if the IP is banned
function is_ip_banned($ip, $ban_file, $ban_duration) {
if (!file_exists($ban_file)) {
return false; // No ban file, no bans
}
if (!is_readable($ban_file)) {
error_log('DDoS Protection: Ban file not readable: ' . $ban_file);
return false;
}
$banned = file($ban_file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
if ($banned === false) {
error_log('DDoS Protection: Failed to read ban file: ' . $ban_file);
return false;
}
foreach ($banned as $line) {
$parts = explode('|', $line);
if (count($parts) !== 2) {
error_log('DDoS Protection: Malformed line in ban file: ' . $line);
continue;
}
list($banned_ip, $ban_time) = $parts;
if ($banned_ip === $ip && ((int)$ban_time + $ban_duration) > time()) {
return true;
}
}
return false;
}
// Clean up expired bans
function clean_expired_bans($ban_file, $ban_duration) {
if (!file_exists($ban_file)) {
return; // No ban file, nothing to clean
}
if (!ddos_ensure_ban_file($ban_file)) {
error_log('DDoS Protection: Cannot clean ban file due to creation/write issues: ' . $ban_file);
return;
}
$banned = file($ban_file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
if ($banned === false) {
error_log('DDoS Protection: Failed to read ban file for cleanup: ' . $ban_file);
return;
}
$new_banned = [];
$now = time();
foreach ($banned as $line) {
$parts = explode('|', $line);
if (count($parts) !== 2) {
error_log('DDoS Protection: Malformed line in ban file during cleanup: ' . $line);
continue;
}
list($banned_ip, $ban_time) = $parts;
if (((int)$ban_time + $ban_duration) > $now) {
$new_banned[] = $line;
}
}
if (!file_put_contents($ban_file, implode("\n", $new_banned) . "\n", LOCK_EX)) {
error_log('DDoS Protection: Failed to write to ban file during cleanup: ' . $ban_file);
} else {
error_log('DDoS Protection: Ban file cleanup completed: ' . $ban_file);
}
}
// Get user's IP address
$ip = ddos_get_client_ip();
if (empty($ip)) {
error_log('DDoS Protection: Exiting due to invalid IP');
exit('Invalid IP address');
}
$now = time();
$file = $ddos_config['base_dir'] . 'dp_' . md5($ip) . '.txt';
// Ensure base directory is writable
if (!ddos_ensure_base_dir($ddos_config['base_dir'])) {
error_log('DDoS Protection: Exiting due to directory issues');
exit('Server configuration error');
}
// Ensure ban file exists (but don't exit if it fails)
if (!ddos_ensure_ban_file($ddos_config['ban_file'])) {
error_log('DDoS Protection: Ban file setup failed, continuing without ban file');
// Proceed without exiting, as ban file isn't needed until banning
}
// If the IP is banned, reject the request
if (is_ip_banned($ip, $ddos_config['ban_file'], $ddos_config['ban_duration'])) {
error_log('DDoS Protection: Banned IP detected - IP: ' . $ip);
header('HTTP/1.1 403 Forbidden');
exit('Your IP is banned for 24 hours due to excessive requests.');
}
// Clean up expired bans
clean_expired_bans($ddos_config['ban_file'], $ddos_config['ban_duration']);
// If no file exists for the IP, create one
if (!file_exists($file)) {
if (!file_put_contents($file, "$now|1|0", LOCK_EX)) {
error_log('DDoS Protection: Failed to create file for IP: ' . $file);
exit('Server configuration error');
}
} else {
$data = file_get_contents($file);
if ($data === false) {
error_log('DDoS Protection: Failed to read file: ' . $file);
exit('Server configuration error');
}
$parts = explode('|', $data);
if (count($parts) !== 3) {
error_log('DDoS Protection: Malformed data in file: ' . $file);
file_put_contents($file, "$now|1|0", LOCK_EX); // Reset corrupted file
} else {
list($start, $count, $failures) = $parts;
if (($now - (int)$start) > $ddos_config['window']) {
// Time window expired, reset counter
if (!file_put_contents($file, "$now|1|0", LOCK_EX)) {
error_log('DDoS Protection: Failed to reset file: ' . $file);
exit('Server configuration error');
}
} else {
$count = (int)$count + 1;
if ($count > $ddos_config['limit']) {
$failures = (int)$failures + 1;
// Persist the updated count and failures before handling response
if (!file_put_contents($file, "$start|$count|$failures", LOCK_EX)) {
error_log('DDoS Protection: Failed to update file during excess: ' . $file);
exit('Server configuration error');
}
// If failure limit is reached, add to ban list
if ($failures >= $ddos_config['ban_attempts']) {
if (file_exists($ddos_config['ban_file']) && !file_put_contents($ddos_config['ban_file'], "$ip|$now\n", FILE_APPEND | LOCK_EX)) {
error_log('DDoS Protection: Failed to write to ban file: ' . $ddos_config['ban_file']);
} else {
error_log('DDoS Protection: IP banned - IP: ' . $ip);
// Clean up the temp file after banning
@unlink($file);
header('HTTP/1.1 403 Forbidden');
exit('Your IP has been banned for 24 hours due to excessive requests.');
}
}
// Exceeded request limit - reject with 429
error_log('DDoS Protection: Rate limit exceeded - IP: ' . $ip);
header('HTTP/1.1 429 Too Many Requests');
header('Retry-After: ' . ($ddos_config['window'] - ($now - (int)$start)));
exit('Too many requests. Please slow down.');
} else {
// Update request counter
if (!file_put_contents($file, "$start|$count|$failures", LOCK_EX)) {
error_log('DDoS Protection: Failed to update file: ' . $file);
exit('Server configuration error');
}
}
}
}
}
?>