Fortify Audit Workbench 笔记 Race Condition: Singleton Member Field 竞争条件:单例的成员字段

Race Condition: Singleton Member Field 竞争条件:单例的成员字段

Abstract

Servlet 成员字段可能允许一个用户查看其他用户的数据。

Explanation

许多 Servlet 开发人员都不了解 Servlet 为单例模式。 Servlet 只有一个实例,并通过使用和重复使用该单个实例来处理需要由不同线程同时处理的多个请求。 这种误解的共同后果是,开发者使用 Servlet 成员字段的这种方式会导致某个用户可能在无意中看到其他用户的数据。 换言之,即把用户数据存储在 Servlet 成员字段中会引发数据访问的 race condition。
例 1: 以下 Servlet 把请求参数值存储在成员字段中,然后将参数值返回给响应输出流。

public class GuestBook extends HttpServlet {
String name;
protected void doPost (HttpServletRequest req,
HttpServletResponse res) {
name = req.getParameter("name");
...
out.println(name + ", thanks for visiting!");
}
}

当该代码在单一用户环境中正常运行时,如果有两个用户几乎同时访问 Servlet,可能会导致这两个请求以如下方式处理线程的插入:
线程 1: assign "Dick" to name
线程 2: assign "Jane" to name
线程 1: print "Jane, thanks for visiting!"
线程 2: print "Jane, thanks for visiting!"
因此会向第一个用户显示第二个用户的用户名。

Recommendation

不要为任何参数(常量除外)使用 Servlet 成员字段。 (例如,确保所有成员字段都是 static final)。当开发者需要把代码内某一部分中的数据传输到另一部分时,他们经常使用 Servlet 成员字段存储用户数据。 如果您也是这么做的,可以考虑声明一个单独的类,并仅使用 Servlet “封装”这个新类。
例 2: 上述例子中的 bug 可以利用以下方式进行修正:

public class GuestBook extends HttpServlet {
protected void doPost (HttpServletRequest req,
HttpServletResponse res) {
GBRequestHandler handler = new GBRequestHandler();
handler.handle(req, res);
}
}
public class GBRequestHandler {
String name;
public void handle(HttpServletRequest req,
HttpServletResponse res) {
name = req.getParameter("name");
...
out.println(name + ", thanks for visiting!");
}
}

此外, Servlet 也可以利用同步代码块来访问 servlet 实例变量。但是,使用同步代码块可能会导致严重的性能问题。

posted @ 2020-05-10 12:58  马洪彪  阅读(5031)  评论(0编辑  收藏  举报