feat: add honeypot and time-trap bot protection
This commit is contained in:
parent
92812bc70c
commit
6e2c6644df
2 changed files with 39 additions and 1 deletions
|
|
@ -14,6 +14,12 @@ switch ($_SERVER['REQUEST_METHOD']) {
|
||||||
$json = file_get_contents('php://input');
|
$json = file_get_contents('php://input');
|
||||||
$params = json_decode($json);
|
$params = json_decode($json);
|
||||||
|
|
||||||
|
// Honeypot check
|
||||||
|
if (!empty($params->website)) {
|
||||||
|
http_response_code(200);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
// Extract values
|
// Extract values
|
||||||
$email = $params->email;
|
$email = $params->email;
|
||||||
$name = $params->name;
|
$name = $params->name;
|
||||||
|
|
|
||||||
|
|
@ -6,14 +6,30 @@ export default function Contact() {
|
||||||
const [name, setName] = useState("");
|
const [name, setName] = useState("");
|
||||||
const [email, setEmail] = useState("");
|
const [email, setEmail] = useState("");
|
||||||
const [message, setMessage] = useState("");
|
const [message, setMessage] = useState("");
|
||||||
|
const [honeypot, setHoneypot] = useState("");
|
||||||
|
|
||||||
const [submitted, setSubmitted] = useState(false);
|
const [submitted, setSubmitted] = useState(false);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [error, setError] = useState(false);
|
const [error, setError] = useState(false);
|
||||||
|
|
||||||
|
const [formLoadTime] = useState(Date.now());
|
||||||
|
|
||||||
const handleSubmit = async (e: React.SubmitEvent<HTMLFormElement>) => {
|
const handleSubmit = async (e: React.SubmitEvent<HTMLFormElement>) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
|
// Bot check
|
||||||
|
const timeTaken = (Date.now() - formLoadTime) / 1000;
|
||||||
|
|
||||||
|
if (timeTaken < 3) {
|
||||||
|
setSubmitted(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (honeypot !== "") {
|
||||||
|
setSubmitted(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError(false);
|
setError(false);
|
||||||
|
|
||||||
|
|
@ -23,7 +39,7 @@ export default function Contact() {
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ name, email, message }),
|
body: JSON.stringify({ name, email, message, honeypot }),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
|
|
@ -31,6 +47,7 @@ export default function Contact() {
|
||||||
setName("");
|
setName("");
|
||||||
setEmail("");
|
setEmail("");
|
||||||
setMessage("");
|
setMessage("");
|
||||||
|
setHoneypot("");
|
||||||
} else {
|
} else {
|
||||||
setError(true);
|
setError(true);
|
||||||
}
|
}
|
||||||
|
|
@ -122,6 +139,21 @@ export default function Contact() {
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<form onSubmit={handleSubmit} className="space-y-4">
|
<form onSubmit={handleSubmit} className="space-y-4">
|
||||||
|
<div className="hidden" aria-hidden="true">
|
||||||
|
<label className="block text-xs mb-1.5" htmlFor="website">
|
||||||
|
Website
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="website_hp"
|
||||||
|
name="website_hp"
|
||||||
|
tabIndex={-1}
|
||||||
|
autoComplete="off"
|
||||||
|
value={honeypot}
|
||||||
|
onChange={(e) => setHoneypot(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-xs mb-1.5" htmlFor="name">
|
<label className="block text-xs mb-1.5" htmlFor="name">
|
||||||
Name
|
Name
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue