In this post on stackoverflow, it is established that HttpSession
is not thread-safe. Specifically,
The Developer has the responsibility for thread-safe access to the attribute objects themselves. This will protect the attribute collection inside the
HttpSession
object from concurrent access, eliminating the opportunity for an application to cause that collection to become corrupted.
However, looking at the implementation of Catalina's CsrfPreventionFilter, I see no synchronization applied:
HttpSession session = req.getSession(false);
@SuppressWarnings("unchecked")
LruCache<String> nonceCache = (session == null) ? null
: (LruCache<String>) session.getAttribute(
Constants.CSRF_NONCE_SESSION_ATTR_NAME);
...
if (nonceCache == null) {
nonceCache = new LruCache<String>(nonceCacheSize);
if (session == null) {
session = req.getSession(true);
}
session.setAttribute(
Constants.CSRF_NONCE_SESSION_ATTR_NAME, nonceCache);
}
Could you please explain if this kind of access is safe, and why?
Update: In response to Andrew's answer below: Please check the following code. In function process()
of ThreadDemo
, while req
is a local variable, it's not thread-safe, as it holds reference to the shared object session
.
import java.util.HashMap;
import java.util.Map;
public class Main {
public static void main(String[] args) {
Session session = new Session();
int len = 10;
ThreadDemo[] t = new ThreadDemo[len];
for (int i = 0; i < len; i++) {
t[i] = new ThreadDemo(session);
t[i].start();
}
}
}
class ThreadDemo extends Thread {
private final Session session;
ThreadDemo(Session session) {
this.session = session;
}
@Override
public void run() {
Request request = new HttpRequest(session);
process(request);
}
private void process(Request request) {
HttpRequest req = (HttpRequest) request;
Session session = req.getSession();
if (session.getAttribute("TEST") == null)
session.putAttribute("TEST", new Object());
else
session.clearAttributes();
System.out.println(session.getAttribute("TEST") == null);
}
}
class Session {
private final Map<String, Object> session;
Session() {
session = new HashMap<>();
}
Object getAttribute(String attr) {
return session.get(attr);
}
void putAttribute(String attr, Object o) {
session.put(attr, o);
}
void clearAttributes() {
session.clear();
}
}
class Request {
private final Session session;
Request(Session session) {
this.session = session;
}
Session getSession() {
return session;
}
}
class HttpRequest extends Request {
HttpRequest(Session session) {
super(session);
}
}