Added basic spam protection.

This commit is contained in:
Andrew Lalis 2023-07-23 19:59:21 -04:00
parent 59e60885a8
commit 7af0708c40
1 changed files with 67 additions and 19 deletions

View File

@ -41,19 +41,43 @@ void main() {
} }
void handleVisitorLog(ref HttpRequestContext ctx) { void handleVisitorLog(ref HttpRequestContext ctx) {
infoF!"Got visitor log from %s"(ctx.request.remoteAddress);
JSONValue logBody = ctx.request.readBodyAsJson(); JSONValue logBody = ctx.request.readBodyAsJson();
string name = logBody.object["name"].str; string name = logBody.object["name"].str;
if (name.length > 32) {
ctx.response.setStatus(HttpStatus.BAD_REQUEST);
ctx.response.writeBodyString("Name is too long.");
return;
}
string message = logBody.object["message"].str; string message = logBody.object["message"].str;
string remoteAddress = "UNKNOWN"; if (message.length > 255) {
if (ctx.request.remoteAddress !is null) { ctx.response.setStatus(HttpStatus.BAD_REQUEST);
remoteAddress = ctx.request.remoteAddress.toString(); ctx.response.writeBodyString("Message is too long.");
return;
}
if (!ctx.request.hasHeader("X-Forwarded-For")) {
ctx.response.setStatus(HttpStatus.FORBIDDEN);
ctx.response.writeBodyString("Missing remote IP");
return;
}
string remoteAddress = ctx.request.getHeader("X-Forwarded-For");
LogEntry[] recentLogsByThisAddress = getRecentLogEntriesByRemoteAddress(remoteAddress);
SysTime now = Clock.currTime();
if (recentLogsByThisAddress.length > 0 && now - recentLogsByThisAddress[0].createdAt < minutes(1)) {
ctx.response.setStatus(HttpStatus.TOO_MANY_REQUESTS);
return;
} }
insertLogEntry(remoteAddress, name, message); insertLogEntry(remoteAddress, name, message);
} }
void handleLogbookRequest(ref HttpRequestContext ctx) { void handleLogbookRequest(ref HttpRequestContext ctx) {
LogEntry[] entries = getRecentLogEntries(); uint limit = ctx.request.getParamAs!uint("limit", 5);
if (limit > 100) {
ctx.response.setStatus(HttpStatus.BAD_REQUEST);
ctx.response.writeBodyString("Limit is too large.");
return;
}
uint offset = ctx.request.getParamAs!uint("offset", 0);
LogEntry[] entries = getRecentLogEntries(limit, offset);
JSONValue entriesJson = JSONValue(JSONValue[].init); JSONValue entriesJson = JSONValue(JSONValue[].init);
foreach (LogEntry entry; entries) { foreach (LogEntry entry; entries) {
entriesJson.array ~= entry.toJson(); entriesJson.array ~= entry.toJson();
@ -79,6 +103,19 @@ struct LogEntry {
obj["message"] = JSONValue(message); obj["message"] = JSONValue(message);
return obj; return obj;
} }
static LogEntry fromDbRow(ref Row row) {
LogEntry entry;
entry.id = row.peek!ulong(0);
string createdAtStr = row.peek!string(1);
string isoCreatedAt = createdAtStr[0 .. 4] ~ createdAtStr[5 .. 7] ~ createdAtStr[8 .. 10] ~ 'T' ~
createdAtStr[11 .. 13] ~ createdAtStr[14 .. 16] ~ createdAtStr[17 .. $];
entry.createdAt = SysTime.fromISOString(isoCreatedAt);
entry.remoteAddress = row.peek!string(2);
entry.name = row.peek!string(3);
entry.message = row.peek!string(4);
return entry;
}
} }
void initDb() { void initDb() {
@ -111,21 +148,32 @@ SQL");
infoF!"Added log entry for %s @ %s"(name, remoteAddress); infoF!"Added log entry for %s @ %s"(name, remoteAddress);
} }
LogEntry[] getRecentLogEntries() { LogEntry[] findAllByQuery(string query) {
import std.array : Appender, appender;
Database db = Database("logbook.sqlite"); Database db = Database("logbook.sqlite");
ResultRange results = db.execute("SELECT * FROM log_entry ORDER BY created_at DESC LIMIT 5"); ResultRange results = db.execute(query);
LogEntry[] entries; Appender!(LogEntry[]) app = appender!(LogEntry[])();
foreach (Row row; results) { foreach (Row row; results) {
LogEntry entry; app ~= LogEntry.fromDbRow(row);
entry.id = row.peek!ulong(0);
string createdAtStr = row.peek!string(1);
string isoCreatedAt = createdAtStr[0 .. 4] ~ createdAtStr[5 .. 7] ~ createdAtStr[8 .. 10] ~ 'T' ~
createdAtStr[11 .. 13] ~ createdAtStr[14 .. 16] ~ createdAtStr[17 .. $];
entry.createdAt = SysTime.fromISOString(isoCreatedAt);
entry.remoteAddress = row.peek!string(2);
entry.name = row.peek!string(3);
entry.message = row.peek!string(4);
entries ~= entry;
} }
return entries; return app.data();
}
LogEntry[] getRecentLogEntries(uint limit, uint offset) {
import std.format;
string query = format!"SELECT * FROM log_entry ORDER BY created_at DESC LIMIT %d OFFSET %d"(limit, offset);
return findAllByQuery(query);
}
LogEntry[] getRecentLogEntriesByRemoteAddress(string remoteAddress) {
import std.array : Appender, appender;
Database db = Database("logbook.sqlite");
Statement stmt = db.prepare("SELECT * FROM log_entry WHERE remote_address = :addr ORDER BY created_at DESC LIMIT 10");
stmt.bind(0, remoteAddress);
ResultRange results = stmt.execute();
Appender!(LogEntry[]) app = appender!(LogEntry[])();
foreach (Row row; results) {
app ~= LogEntry.fromDbRow(row);
}
return app.data();
} }