CWE-488, Exposure of Data Element to Wrong Session, is what happens when a product does not enforce boundaries between sessions and data goes to, or is used by, the wrong one. One user sees another user's information. MITRE marks it not language specific, but the best known case is the Java servlet.
How it happens
MITRE explains that data can bleed from one session to another through member variables of singleton objects, such as servlets, and objects from a shared pool.
A servlet is a singleton. Unless it implements the SingleThreadModel interface, there is one instance, reused to handle many requests at once on different threads. A field on that instance is shared by all of them.
// Vulnerable: a field on a shared servlet instance
public class ProfileServlet extends HttpServlet {
private String currentUser; // shared across all requests
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
currentUser = req.getRemoteUser();
// another thread may overwrite currentUser here
resp.getWriter().println(loadProfile(currentUser));
}
}
If two requests overlap, the second can overwrite currentUser before the first finishes, and the first user gets the second user's profile. MITRE calls this a data access race condition.
What an attacker gains
MITRE lists one consequence: reading application data. No privilege is needed beyond making requests at the right moment. The record has no observed CVEs, and the leak is often random, which makes it easy to miss in testing and hard to reproduce.
How to find it
MITRE suggests using a static analysis tool to scan for information leakage such as the singleton member field pattern. Search servlet and controller classes for instance fields that are not constants. Any mutable field on a shared object is a candidate.
Load testing with several different users at the same time can also surface it, since the bug needs overlapping requests.
How to fix it
MITRE's guidance has three parts.
- Protect sessions from information leakage and make sure one session's data is not used or visible to others.
- In a multithreaded environment, do not store user data in servlet member fields.
- Scan with static analysis as part of testing.
public class ProfileServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
String user = req.getRemoteUser(); // local variable, per request
resp.getWriter().println(loadProfile(user));
}
}
Local variables live on each thread's own stack. Keep per user data there, or in the session object, never on the shared instance. The same rule applies to any singleton, such as a Spring bean with default scope or an object taken from a pool.
Quick check
Why does this bug appear only under load?
It needs two requests in flight at once. With one tester, each request finishes before the next begins and the shared field always holds the right value.