-
Notifications
You must be signed in to change notification settings - Fork 16
/
Copy pathAjaxForm.php
229 lines (200 loc) · 8.84 KB
/
AjaxForm.php
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
<?php
/**
* Secure Contact Form using PHPMailer & reCAPTCHA v3
* Includes honeypot protection and enhanced email formatting
*
* @see https://github.com/raspgot/AjaxForm-PHPMailer-reCAPTCHA
* @package PHPMailer
* @version 1.6.0
*/
declare(strict_types=1);
// Set the HTTP response content type to JSON
header('Content-Type: application/json');
// Import PHPMailer classes
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
// Load PHPMailer source files (no Composer)
require_once 'PHPMailer/PHPMailer.php';
require_once 'PHPMailer/SMTP.php';
require_once 'PHPMailer/Exception.php';
// 🛠️ Configuration constants for SMTP and reCAPTCHA
const SECRET_KEY = ''; // Google reCAPTCHA secret key
const SMTP_HOST = ''; // SMTP server hostname
const SMTP_USERNAME = ''; // SMTP username
const SMTP_PASSWORD = ''; // SMTP password
const SMTP_SECURE = 'tls'; // Encryption method: TLS or SSL
const SMTP_PORT = 587; // Port for TLS (587) or SSL (465)
const SMTP_AUTH = true; // Enable SMTP authentication
const FROM_NAME = 'Raspgot'; // Sender name
const EMAIL_SUBJECT = '[Github] New message !'; // Subject for outgoing emails
// Predefined response messages
const EMAIL_MESSAGES = [
'success' => '✉️ Your message has been sent !',
'enter_name' => '⚠️ Please enter your name.',
'enter_email' => '⚠️ Please enter a valid email.',
'enter_message' => '⚠️ Please enter your message.',
'token_error' => '⚠️ No reCAPTCHA token received.',
'domain_error' => '⚠️ The email domain is invalid.',
'method_error' => '⚠️ Method not allowed.',
'constant_error' => '⚠️ Missing configuration constants.',
'honeypot_error' => '🚫 Spam detected.',
];
// Ensure all necessary constants are set
if (empty(SECRET_KEY) || empty(SMTP_HOST) || empty(SMTP_USERNAME) || empty(SMTP_PASSWORD)) {
respond(false, EMAIL_MESSAGES['constant_error']);
}
// Allow only POST requests (reject GET or others)
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
respond(false, EMAIL_MESSAGES['method_error']);
}
// Simple bot detection based on the user-agent string
if (empty($_SERVER['HTTP_USER_AGENT']) || preg_match('/curl|bot|spider|crawler/i', $_SERVER['HTTP_USER_AGENT'])) {
respond(false, EMAIL_MESSAGES['honeypot_error']);
}
// Gather and validate user input from POST data
$date = new DateTime();
$ip = $_SERVER['REMOTE_ADDR'] ?? 'Unknown';
$email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL) ?: respond(false, EMAIL_MESSAGES['enter_email']);
$name = isset($_POST['name']) ? sanitize($_POST['name']) : respond(false, EMAIL_MESSAGES['enter_name']);
$message = isset($_POST['message']) ? sanitize($_POST['message']) : respond(false, EMAIL_MESSAGES['enter_message']);
$token = isset($_POST['recaptcha_token']) ? sanitize($_POST['recaptcha_token']) : respond(false, EMAIL_MESSAGES['token_error']);
$honeypot = isset($_POST['website']) ? trim($_POST['website']) : '';
if (!empty($honeypot)) {
respond(false, EMAIL_MESSAGES['honeypot_error']);
}
// Check if the email domain is valid (DNS records)
$domain = substr(strrchr($email, "@"), 1);
if (!checkdnsrr($domain, "MX") && !checkdnsrr($domain, "A")) {
respond(false, EMAIL_MESSAGES['domain_error']);
}
// Validate the reCAPTCHA token with Google
validateRecaptcha($token);
// Construct the HTML email content
$email_body = '<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>' . EMAIL_SUBJECT . '</title>
</head>
<body style="margin: 0; padding: 0; background-color: #f4f4f4; font-family: Arial, sans-serif">
<table width="100%" cellpadding="0" cellspacing="0" border="0" bgcolor="#f4f4f4">
<tr>
<td align="center" style="padding: 30px 15px">
<table width="600" cellpadding="0" cellspacing="0" border="0" style="background-color: #ffffff; border-radius: 8px; overflow: hidden; border: 1px solid #e0e0e0">
<tr>
<td bgcolor="#4a90e2" style="padding: 24px; text-align: center; color: #ffffff; font-size: 24px; font-weight: bold">' . EMAIL_SUBJECT . '</td>
</tr>
<tr>
<td style="padding: 24px; color: #333333; font-size: 16px; line-height: 1.5">
<p style="margin: 0 0 12px"><strong>Date</strong><br />' . $date->format('m/d/Y H:i:s') . '</p>
<p style="margin: 0 0 12px"><strong>Name</strong><br />' . $name . '</p>
<p style="margin: 0 0 12px"><strong>Email</strong><br /><a href="mailto:' . $email . '" style="color: #4a90e2; text-decoration: none">' . $email . '</a></p>
<p style="margin: 0 0 12px"><strong>Message</strong><br />' . $message . '</p>
<hr style="border: none; border-top: 1px solid #dddddd; margin: 24px 0" />
<p style="font-size: 14px; color: #888888; margin: 0"><strong>IP: </strong>' . $ip . '</p>
</td>
</tr>
<tr>
<td bgcolor="#f4f4f4" style="padding: 16px; text-align: center; font-size: 12px; color: #aaaaaa">This email was generated automatically.</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>';
// Send the email using PHPMailer
$mail = new PHPMailer(true);
try {
// SMTP configuration
$mail->isSMTP();
$mail->Host = SMTP_HOST;
$mail->SMTPAuth = SMTP_AUTH;
$mail->Username = SMTP_USERNAME;
$mail->Password = SMTP_PASSWORD;
$mail->SMTPSecure = SMTP_SECURE;
$mail->Port = SMTP_PORT;
// Set sender and recipient
$mail->setFrom(SMTP_USERNAME, FROM_NAME);
$mail->Sender = $SMTP_USERNAME;
$mail->addAddress($email, $name);
$mail->addCC(SMTP_USERNAME, 'Admin');
$mail->addReplyTo($email, $name);
// Email content
$mail->isHTML(true);
$mail->CharSet = 'UTF-8';
$mail->Subject = EMAIL_SUBJECT;
$mail->Body = $email_body;
$mail->AltBody = strip_tags($email_body);
// Attempt to send email
$mail->send();
respond(true, EMAIL_MESSAGES['success']);
} catch (Exception $e) {
// Catch errors and return the message
respond(false, '❌ ' . $e->getMessage());
}
/**
* Validate the reCAPTCHA token via Google's API
*
* @param string $token reCAPTCHA token received from frontend
*/
function validateRecaptcha(string $token): void
{
$ch = curl_init('https://www.google.com/recaptcha/api/siteverify');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query([
'secret' => SECRET_KEY,
'response' => $token
]),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
]);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curl_error = curl_error($ch);
curl_close($ch);
if ($response === false || $http_code !== 200) {
respond(false, '❌ Error during the Google reCAPTCHA request : ' . ($curl_error ?: "HTTP $http_code"));
}
$data = json_decode($response, true);
if (empty($data['success'])) {
respond(false, '❌ reCAPTCHA validation failed : ', $data['error-codes'] ?? []);
}
// Reject if score is too low (likely a bot)
if (isset($data['score']) && $data['score'] < 0.5) {
respond(false, '❌ reCAPTCHA score too low. You might be a robot 🤖');
}
}
/**
* Sanitize input to prevent header injection, XSS, and control characters
*
* @param string $data Raw input string
* @return string Sanitized and safe string
*/
function sanitize(string $data): string
{
// Remove null bytes and other control characters (except \t)
$data = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+/u', '', $data);
// Prevent header injection by replacing line breaks with spaces
$data = preg_replace('/\r|\n/', ' ', $data);
// Escape HTML entities (with strict quote handling and UTF-8 safety)
return trim(htmlspecialchars($data, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML5, 'UTF-8', true));
}
/**
* Send a JSON response and stop script execution
*
* @param bool $success Success status
* @param string $message Message to send
* @param mixed $detail Optional additional data
*/
function respond(bool $success, string $message, mixed $detail = null): void
{
echo json_encode([
'success' => $success,
'message' => $message,
'detail' => $detail,
]);
exit;
}