Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file modified gradlew
100644 → 100755
Empty file.
12 changes: 11 additions & 1 deletion src/main/java/de/igslandstuhl/database/server/WebServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

import de.igslandstuhl.database.server.webserver.handlers.GetRequestHandler;
import de.igslandstuhl.database.server.webserver.handlers.PostRequestHandler;
import de.igslandstuhl.database.server.webserver.handlers.SessionValidationResult;
import de.igslandstuhl.database.server.webserver.requests.GetRequest;
import de.igslandstuhl.database.server.webserver.requests.HttpHeader;
import de.igslandstuhl.database.server.webserver.requests.PostRequest;
Expand Down Expand Up @@ -146,7 +147,16 @@ void handlePost(String headerString, InputStream in, PrintStream out) throws IOE
body = URLDecoder.decode(raw, bodyCharset.name());
}
PostRequest parsedRequest = new PostRequest(postHeader, body, clientIp, secure);
HttpResponse response = Server.getInstance().getWebServer().getSessionManager().validateSession(parsedRequest) ? PostRequestHandler.getInstance().handlePostRequest(parsedRequest) : PostResponse.forbidden("Forbidden: session manipulation or ratelimit", parsedRequest);

SessionValidationResult v = Server.getInstance().getWebServer().getSessionManager().validateSession(parsedRequest);
HttpResponse response;
switch(v) {
case OK -> response = PostRequestHandler.getInstance().handlePostRequest(parsedRequest); //OK
case RATE_LIMITED -> response = PostResponse.tooManyRequests("Too Many Requests", parsedRequest); //429
case INVALID_SESSION -> response = PostResponse.unauthorized("Invalid Session", parsedRequest); //401
default -> response = PostResponse.unauthorized("Invalid Session", parsedRequest);
}

response.respond(out);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public enum Status {
METHOD_NOT_ALLOWED(405, "Method Not Allowed"),
I_AM_A_TEAPOT(418, "I'm a teapot"),
INTERNAL_SERVER_ERROR(500, "Internal Server Error"),
TOO_MANY_REQUESTS(429, "Too Many Requests")
;
/**
* The HTTP status code.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,19 @@ private GetRequestHandler() {}

public final HttpResponse handleRequest(GetRequest request) {
SessionManager sessionManager = Server.getInstance().getWebServer().getSessionManager();
if (!sessionManager.validateSession(request)) {
return GetResponse.forbidden(request);
} else {
String path = request.getPath();
HttpHandler<GetRequest> handler = Registry.getRequestHandlerRegistry().get(path);
return handler.handleHttpRequest(request);

SessionValidationResult v = sessionManager.validateSession(request);
if(v != SessionValidationResult.OK){
return switch (v){
case RATE_LIMITED -> GetResponse.tooManyRequests(request);
case INVALID_SESSION -> GetResponse.unauthorized("Invalid Session", request);
default -> GetResponse.unauthorized("Invalid Session", request);
};
}

String path = request.getPath();
HttpHandler<GetRequest> handler = Registry.getRequestHandlerRegistry().get(path);
return handler.handleHttpRequest(request);
}

public static GetResponse handleFileRequest(GetRequest request) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package de.igslandstuhl.database.server.webserver.handlers;

public enum SessionValidationResult {
OK, RATE_LIMITED, INVALID_SESSION
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,48 @@ public static GetResponse forbidden(HttpRequest request) {
public static GetResponse unauthorized(HttpRequest request) {
return new GetResponse(request, Status.UNAUTHORIZED, new ResourceLocation("html", "errors", "401.html"), ContentType.HTML, "", false);
}
/**
* Returns a response for a GET request the user must be logged in for.
* This overload allows specifying a custom message (e.g. "Invalid Session") without leaking details.
* @param message The message to include in the response.
* @return the GetResponse object
*/
public static GetResponse unauthorized(String message, HttpRequest request) {
return new GetResponse(
request,
Status.UNAUTHORIZED,
new ResourceLocation("html", "errors", "401.html"),
ContentType.HTML,
message,
false
);
}
/**
* Returns a response indicating that the client has sent too many requests in a given amount of time.
* This is used for rate limiting.
* @param message The message to include in the response.
* @return the GetResponse object
*/
public static GetResponse tooManyRequests(String message, HttpRequest request) {
return new GetResponse(
request,
Status.TOO_MANY_REQUESTS,
new ResourceLocation("html", "errors", "429.html"),
ContentType.HTML,
message,
false
);
}

/**
* Returns a response indicating that the client has sent too many requests in a given amount of time.
* This is used for rate limiting.
* @return the GetResponse object
*/
public static GetResponse tooManyRequests(HttpRequest request) {
return tooManyRequests("", request);
}

/**
* The HTTP status of this response
* @see Status
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,26 @@ public static PostResponse notFound(String message, PostRequest request) {
public static PostResponse forbidden(String message, PostRequest request) {
return new PostResponse(Status.FORBIDDEN, message, ContentType.TEXT_PLAIN, request);
}

/**
* Returns a response indicating that the client has sent too many requests in a given amount of time.
* This is used for rate limiting.
* @param message The error message to include in the response.
* @return A PostResponse object representing the too many requests response.
*/
public static PostResponse tooManyRequests(String message, PostRequest request) {
return new PostResponse(Status.TOO_MANY_REQUESTS, message, ContentType.TEXT_PLAIN, request);
}

/**
* Returns a response indicating that the client has sent too many requests in a given amount of time.
* This is used for rate limiting.
* @return A PostResponse object representing the too many requests response.
*/
public static PostResponse tooManyRequests(PostRequest rq) {
return tooManyRequests("Too Many Requests", rq);
}

public static PostResponse redirect(String location, PostRequest request) {
return new PostResponse(Status.FOUND, "", ContentType.TEXT_PLAIN, request, new String[] {
"Location: " + location
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import de.igslandstuhl.database.api.User;
import de.igslandstuhl.database.server.webserver.Cookie;
import de.igslandstuhl.database.server.webserver.handlers.SessionValidationResult;
import de.igslandstuhl.database.server.webserver.requests.HttpRequest;

public class SessionManager {
Expand All @@ -26,6 +27,8 @@ public class SessionManager {
private final int maximumInactivityDuration;
private final int maxRequests;



public SessionManager(int sessionExpireDuration, int maximumInactivityDuration, int maxRequests) {
this.sessionExpireDuration = sessionExpireDuration;
this.maximumInactivityDuration = maximumInactivityDuration;
Expand Down Expand Up @@ -65,7 +68,7 @@ private void cleanSecondsJob() {
}
}

public boolean validateSession(HttpRequest request) {
public SessionValidationResult validateSession(HttpRequest request) {
lastActivity.set(request, Instant.now());

Integer requests = requestCount.get(request);
Expand All @@ -74,21 +77,21 @@ public boolean validateSession(HttpRequest request) {
requestCount.set(request, count);
if (count > maxRequests && !getSessionUser(request).isAdmin()) {
System.out.println("Ratelimit!");
return false;
return SessionValidationResult.RATE_LIMITED;
}

String userAgent = request.getUserAgent();
if (!getSession(request).getUserAgent().equals(userAgent)) {
System.err.println("SEVERE WARNING: POTENTIAL ATTACK: faked session id (device changed), for user " + getSessionUser(request));
return false;
return SessionValidationResult.INVALID_SESSION;
}
String ip = request.getIP();
if (!getSession(request).getIpAddress().equals(ip)) {
System.err.println("SEVERE WARNING: POTENTIAL ATTACK: faked session id (ip address changed) for user " + getSessionUser(request));
return false;
return SessionValidationResult.INVALID_SESSION;
}

return true;
return SessionValidationResult.OK;
}

public Session getSession(UUID sessionUUID) {
Expand Down
Loading