I have to write a little social network for a case study at university. I have done some simple webapps before but nothing that required authentication, login and so on. So I wrote this little application after reading several guides on how this is usually implemented.
What I came up with is a simple login.html
file in my server's public directory that contains a form with username and password. After submitting, the server looks up the username in a HashMap
and checks firstly if the user exists and secondly if the password is correct. If both criteria are met, I set a cookie via response that contains a random UUID that the server keeps track of in an ArrayList
. Lastly, the before
filter checks if any protected page is being accessed, and if so, checks the requests cookies and looks them up in the ArrayList
. If the cookie is known to the server I grant access to whatever is under /protected/
.
Apart from encryption, is this code doing what I want? For me it looks like it works: login sets the cookie, logout removes it, /protected/
path can't be accessed without a known cookie. But I don't exactly know if I am missing something critical that might allow people to access pages without my knowledge.
package co.selim;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import spark.Spark;
public class Router {
public static void main(String[] args) {
Map<String, String> credentials = new HashMap<>();
credentials.put("root", "passw0rd");
List<String> tokens = new ArrayList<>();
Spark.staticFileLocation("public");
Spark.exception(Exception.class, (exception, request, response) -> {
exception.printStackTrace();
});
Spark.before("/protected/*", (req, res) -> {
if (!tokens.contains(req.cookie("token")))
Spark.halt(401, "You can't access this page.");
});
Spark.post("/login", (req, res) -> {
String username = req.queryParams("user");
String password = req.queryParams("password");
String storedPassword = credentials.get(username);
if (storedPassword != null && password.equals(storedPassword)) {
String token = UUID.randomUUID().toString();
tokens.add(token);
res.cookie("token", token, 3600);
} else
return "Username or password wrong.";
return "";
});
Spark.get("/logout", (req, res) -> {
String token = req.cookie("token");
if (tokens.remove(token)) {
res.removeCookie("token");
return "Successfully logged out.";
} else
return "You were not logged in.";
});
Spark.get("/protected/controlPanel", (req, res) -> {
return "<button>Launch nukes</button>";
});
}
}
1 Answer 1
I'm not familiar with Spark. It's obvious that this code isn't thread-safe (ArrayList
isn't thread-safe, and tokens
is accessed without any explicitly locking), but I don't know whether Spark provides the thread safety.
The cookie handling is missing two important parameters:
res.cookie("token", token, 3600);
There are various overloads which take boolean secured, boolean httpOnly
, and ideally you would set both of those to true
. If you can't set secured
because you're using a cheap web host which doesn't support HTTPS, at least set httpOnly
.
if (!tokens.contains(req.cookie("token"))) { ... }
. So no, you can not just create a cookie called "token" and get access. However how would I be protected against Session Fixation? And is an attack really practical? \$\endgroup\$