Guten Tag,
ich möchte mein eigenes Captcha verwenden und wollte fragen, ob die folgende Umsetzung in Ordnung wäre, bevor ich es online nehme, oder ob es dabei sicherheitstechnische Bedenken gäbe
Dependencies:
Nun kann man den Captcha-Server starten und
Es geht darum, dass ich einige Kontaktinfos durch ein Captcha vor bösen Robotern schützen möchte - und nur Menschen diese einsehen können sollen
Damit nicht ständig ein neues Captcha genriert werden kann, habe ich das auf 10 Sekunden pro IP begrenzt
Kann man das so online stellen oder ergeben sich hier Angriffspunkte?
Wenn Bedarf besteht, könnte ich auch noch ein https://de.wikipedia.org/wiki/Sequenzdiagramm beifügen
ich möchte mein eigenes Captcha verwenden und wollte fragen, ob die folgende Umsetzung in Ordnung wäre, bevor ich es online nehme, oder ob es dabei sicherheitstechnische Bedenken gäbe
Main.java:
Java:
import com.hellokaton.blade.Blade;
import java.awt.*;
import java.awt.image.RenderedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.Base64;
import java.util.HashMap;
import javax.imageio.ImageIO;
import net.logicsquad.nanocaptcha.content.LatinContentProducer;
import net.logicsquad.nanocaptcha.image.ImageCaptcha;
import net.logicsquad.nanocaptcha.image.backgrounds.GradiatedBackgroundProducer;
import net.logicsquad.nanocaptcha.image.noise.CurvedLineNoiseProducer;
import net.logicsquad.nanocaptcha.image.renderer.DefaultWordRenderer;
import org.json.JSONObject;
public class Main {
public static void main(String[] args) {
final HashMap<String, Long> lastAccess = new HashMap<>();
final HashMap<Integer, String> map = new HashMap<>();
Blade.create()
.get(
"/get",
ctx -> {
long now = System.currentTimeMillis();
String clientIp = ctx.address();
System.out.println("Client IP: " + clientIp + ", Request Time: " + now);
if (lastAccess.containsKey(clientIp)) {
long last = lastAccess.get(clientIp);
if (now - last <= 10_000) {
lastAccess.put(clientIp, now);
JSONObject response = new JSONObject();
response.put("ok", false);
response.put(
"message", "Please wait 10 seconds before requesting a new captcha.");
ctx.status(403);
ctx.json(response.toString());
return;
}
}
lastAccess.put(clientIp, now);
ImageCaptcha ic =
new ImageCaptcha.Builder(500, 150)
.addContent(
new LatinContentProducer(12),
new DefaultWordRenderer.Builder()
.randomColor(Color.BLACK, Color.BLUE, Color.CYAN, Color.RED)
.build())
.addBackground(new GradiatedBackgroundProducer())
.addNoise(new CurvedLineNoiseProducer())
.build();
String code = ic.getContent();
RenderedImage img = ic.getImage();
String base64Image = imgToBase64String(img, "PNG");
int hash = base64Image.hashCode();
map.put(hash, code);
JSONObject response = new JSONObject();
response.put("ok", true);
response.put("base64Image", base64Image);
ctx.json(response.toString());
System.out.println("Generated captcha with hash: " + hash + " and code: " + code);
})
.get(
"/check/:hash/:code",
ctx -> {
long now = System.currentTimeMillis();
String clientIp = ctx.address();
System.out.println("Client IP: " + clientIp + ", Request Time: " + now);
if (lastAccess.containsKey(clientIp)) {
long last = lastAccess.get(clientIp);
if (now - last <= 10_000) {
lastAccess.put(clientIp, now);
JSONObject response = new JSONObject();
response.put("ok", false);
response.put("message", "Please wait 10 seconds before checking a new captcha.");
ctx.status(403);
ctx.json(response.toString());
return;
}
}
lastAccess.put(clientIp, now);
try {
int hash = ctx.pathInt("hash");
String code = ctx.pathString("code");
System.out.println("Checking captcha with hash: " + hash + " and code: " + code);
if (map.containsKey(hash)) {
String expectedCode = map.get(hash);
if (expectedCode.equals(code)) {
JSONObject response = new JSONObject();
response.put("ok", true);
response.put("message", "Captcha is correct.");
response.put(
"secret_message",
"This message contains the secret information you requested.");
ctx.json(response.toString());
} else {
JSONObject response = new JSONObject();
response.put("ok", false);
response.put("message", "Captcha is incorrect.");
ctx.status(403);
ctx.json(response.toString());
}
} else {
JSONObject response = new JSONObject();
response.put("ok", false);
response.put("message", "Captcha not found or expired.");
ctx.status(403);
ctx.json(response.toString());
}
} catch (Exception e) {
JSONObject response = new JSONObject();
response.put("ok", false);
response.put("message", "Invalid request.");
ctx.status(400);
ctx.json(response.toString());
}
})
.get(
"/demo",
ctx -> {
long now = System.currentTimeMillis();
String clientIp = ctx.address();
System.out.println("Client IP: " + clientIp + ", Request Time: " + now);
ctx.render("demo.html");
})
.listen(80)
.start();
}
public static String imgToBase64String(final RenderedImage img, final String formatName) {
final ByteArrayOutputStream os = new ByteArrayOutputStream();
try {
ImageIO.write(img, formatName, os);
return Base64.getEncoder().encodeToString(os.toByteArray());
} catch (final IOException ioe) {
throw new UncheckedIOException(ioe);
}
}
}
templates/demo.html:
HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Demo</title>
</head>
<body>
<input type="button" value="Request a new captcha" onclick="requestCaptcha();">
<div id="captchaContainer">
<p>Captcha will be displayed here after request.</p>
</div>
<label for="captchaInput">Captcha text:</label>
<input type="text" id="captchaInput" placeholder="Enter captcha here">
<input type="button" value="Submit captcha" onclick="submitCaptcha();">
<script>
let hash = null;
function hashString(str) {
let hash = 0, l = str.length, i = 0;
if (l > 0)
while (i < l)
hash = (hash << 5) - hash + str.charCodeAt(i++) | 0;
return hash;
}
async function requestCaptcha() {
await fetch('/get')
.then(data => data.json())
.then(json => {
if (!json.ok) {
throw new Error(json.message || 'Failed to fetch captcha');
}
let captchaContainer = document.getElementById('captchaContainer');
captchaContainer.innerHTML = ''; // Clear previous content
let img = document.createElement('img');
img.src = 'data:image/png;base64,' + json.base64Image;
img.alt = 'Captcha Image';
img.style.paddingTop = '1rem';
captchaContainer.appendChild(img);
hash = hashString(json.base64Image); // Store the hash of the image
})
.catch(error => {
let captchaContainer = document.getElementById('captchaContainer');
let errorMessage = document.createElement('p');
errorMessage.textContent = 'Error fetching captcha: ' + (error.message || 'Unknown error');
captchaContainer.appendChild(errorMessage);
});
}
async function submitCaptcha() {
let captchaText = document.getElementById('captchaInput').value;
await fetch('/check/' + hash + '/' + encodeURIComponent(captchaText))
.then(data => data.json())
.then(json => {
if (!json.ok) {
throw new Error(json.message || 'Failed to fetch captcha');
}
let captchaContainer = document.getElementById('captchaContainer');
captchaContainer.innerHTML = ''; // Clear previous content
let resultMessage = document.createElement('p');
resultMessage.textContent = json.message || 'Captcha submitted successfully!';
captchaContainer.appendChild(resultMessage);
let secretMessage = document.createElement('p');
secretMessage.textContent = json.secret_message || 'No secret message provided.';
captchaContainer.appendChild(secretMessage);
hash = null; // Reset hash after submission
})
.catch(error => {
let captchaContainer = document.getElementById('captchaContainer');
let errorMessage = document.createElement('p');
errorMessage.textContent = 'Error submitting captcha: ' + (error.message || 'Unknown error');
captchaContainer.appendChild(errorMessage);
});
}
</script>
</body>
</html>
Dependencies:
Code:
dependencies {
implementation 'org.apache.commons:commons-lang3:3.18.0'
implementation 'org.json:json:20250517'
implementation 'com.hellokaton:blade-core:2.1.2.RELEASE'
implementation 'net.logicsquad:nanocaptcha:2.1'
implementation 'org.slf4j:slf4j-simple:2.0.17'
}
Nun kann man den Captcha-Server starten und
http://localhost/demo aufrufen... Möglicherweise geht das nur unter LinuxEs geht darum, dass ich einige Kontaktinfos durch ein Captcha vor bösen Robotern schützen möchte - und nur Menschen diese einsehen können sollen
Damit nicht ständig ein neues Captcha genriert werden kann, habe ich das auf 10 Sekunden pro IP begrenzt
Kann man das so online stellen oder ergeben sich hier Angriffspunkte?
Wenn Bedarf besteht, könnte ich auch noch ein https://de.wikipedia.org/wiki/Sequenzdiagramm beifügen