getOutputStream
호출 후 getWriter()
를 호출하거나 그 반대로 수행하는 경우도 잘못입니다.HttpServletRequest
을 전달하거나, HttpServletResponse
를 리디렉션하거나 서블릿의 출력 스트림 버퍼를 플러시하면 연결된 스트림이 커밋됩니다. 추가 플러시 또는 리디렉션 등, 이어지는 버퍼 재설정 또는 스트림 커밋은 IllegalStateException
으로 이어질 수 있습니다.ServletOutputStream
또는 PrintWriter
중 하나를 사용하여(두 가지 모두는 안 됨) 응답 스트림에 데이터를 쓸 수 있습니다. getOutputStream()
을 호출한 후 getWriter()
를 호출하거나 또는 그 반대의 경우에도 IllegalStateException
이 발생합니다.IllegalStateException
은 응답 핸들러가 실행 완료되는 것을 막아 응답을 취소합니다. 이로 인해 서버 불안정이 발생할 수 있는데, 이는 서블릿이 잘못 구현되었다는 의미입니다.예제 2: 반대로 다음 코드는 요청이 전달되고 나면
public class RedirectServlet extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
...
OutputStream out = res.getOutputStream();
...
// flushes, and thereby commits, the output stream
out.flush();
out.close(); // redirecting the response causes an IllegalStateException
res.sendRedirect("http://www.acme.com");
}
}
PrintWriter
의 버퍼에 쓰거나 플러시를 시도합니다.
public class FlushServlet extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
...
// forwards the request, implicitly committing the stream
getServletConfig().getServletContext().getRequestDispatcher("/jsp/boom.jsp").forward(req, res);
...
// IllegalStateException; cannot redirect after forwarding
res.sendRedirect("http://www.acme.com/jsp/boomboom.jsp");
PrintWriter out = res.getWriter();
// writing to an already-committed stream will not cause an exception,
// but will not apply these changes to the final output, either
out.print("Writing here does nothing");
// IllegalStateException; cannot flush a response's buffer after forwarding the request
out.flush();
out.close();
}
}
Content-Length
헤더가 음수로 설정됩니다.Content-Length
헤더를 설정한다는 것은 개발자가0
또는Content-Length
를 설정합니다.
URL url = new URL("http://www.example.com");
HttpURLConnection huc = (HttpURLConnection)url.openConnection();
huc.setRequestProperty("Content-Length", "-1000");
Content-Length
헤더가 음수로 설정됩니다.Content-Length
헤더를 설정한다는 것은 개발자가0
또는Content-Length
헤더를 음수로 잘못 설정합니다.
xhr.setRequestHeader("Content-Length", "-1000");
java.io.Serializable
을 구현하는 inner class를 사용하면 outer class에서 문제가 발생하고 정보가 누출될 수 있습니다.
...
class User implements Serializable {
private int accessLevel;
class Registrator implements Serializable {
...
}
}
Example 1
에서 inner class Registrator
를 serialize하면 outer class User
의 accessLevel
필드도 serialize됩니다.synchronized
메서드를 선언했고 여러 스레드가 동일한 인스턴스에 접근할 때 올바른 동작을 보장합니다. 또한 모든 오버라이드 메서드는 synchronized
로 선언되어야 합니다. 그렇지 않으면 예기치 못한 동작이 발생할 수도 있습니다.Foo
클래스는 Bar
클래스를 오버라이드하지만 synchronizedMethod
메서드를 synchronized
로 선언하지 않습니다.
public class Bar {
public synchronized void synchronizedMethod() {
for (int i=0; i<10; i++) System.out.print(i);
System.out.println();
}
}
public class Foo extends Bar {
public void synchronizedMethod() {
for (int i=0; i<10; i++) System.out.print(i);
System.out.println();
}
}
Foo
의 인스턴스가 Bar
유형으로 배정될 수 있습니다. 같은 인스턴스를 서로 다른 두 스레드에 지정하고 synchronizedMethod
를 반복 실행하면 동작을 예측할 수 없게 됩니다.obj.Equals(null)
는 항상 false가 됩니다.Equals()
메서드를 사용하여 개체를 null
과 비교합니다. Equals()
메서드의 약정이 항상 false로 반환되도록 비교해야 합니다.obj.equals(null)
는 항상 false가 됩니다.equals()
메서드를 사용하여 개체를 null
과 비교합니다. 이 비교는 개체가 null
이 아니기 때문에 항상 false를 반환합니다. (개체가 null
이면 프로그램에 NullPointerException
이 발생합니다).readObject()
메서드는 재정의할 수 있는 함수를 호출합니다.readObject()
는 생성자로 사용되므로 이 함수가 종료될 때까지는 개체 초기화가 완료되지 않습니다. 따라서 Serializable
클래스의 readObject()
함수가 오버라이드 가능 함수를 호출하면 개체가 완전히 초기화되기 전에 오버라이드 메서드가 개체 상태에 접근할 수 있습니다.readObject()
함수는 오버라이드할 수 있는 메서드를 호출합니다.
...
private void readObject(final ObjectInputStream ois) throws IOException, ClassNotFoundException {
checkStream(ois);
ois.defaultReadObject();
}
public void checkStream(ObjectInputStream stream){
...
}
checkStream()
함수와 이 함수를 포함하는 클래스는 final
및 public이 아니므로 함수를 오버라이드할 수 있습니다. 이로 인해 공격자는 checkStream()
함수를 오버라이드하여 역직렬화 중에 개체에 접근할 수 있습니다.
Marker child = MarkerManager.getMarker("child");
Marker parent = MarkerManager.getMarker("parent");
child.addParents(parent);
parent.addParents(child);
String toInfinity = child.toString();
toString()
을 호출할 때 스택 오버플로 예외(스택 소진)를 트리거합니다. 이 예외는 자식과 부모 간의 순환 링크로 인해 발생합니다.