glenn-syj / more-effective-java

이펙티브 자바를 읽으며 자바를 더 효율적으로 공부합니다
4 stars 5 forks source link

Docker 사례를 통한 TOCTOU에 대한 추가적인 이해와 ConcurrentHashMap의 동시성 관리에 대해 #209

Open undeadtimo opened 3 months ago

undeadtimo commented 3 months ago

based on: item 50 by @glenn-syj

멀티스레딩 환경에서 발생할 수 있는 TOCTOU 공격과 해당 TOCTOU 공격을 방지하기 위한 방법에 대한 글 잘 보았습니다.

덕분에 SQL을 공부하면서 들었던 '동시성 문제'를 Java에서는 어떻게 해결하는지 알 수 있게 되었습니다.

다만 '동시성 문제'에 대한 정확한 개념이 기억나지 않으며 또한 상태 검사와 검사 결과 이용 사이의 경쟁상태에 대하여 정확히 이해하지 못해 TOCTOU에 대해 조사해보았습니다.

추가적으로 이해를 돕기 위해 glenn-syj님께서 언급하신 2019년 Aleska Sarai가 도커가 갖고 있던 TOCTOU 공격에 대한 취약점을 발견한 글을 찾아보았습니다.


먼저 TOCTOU가 발생하는 과정에 대해 제가 이해한 바를 작성해보겠습니다.

멀티 스레딩 환경에서 Process A와 Process B가 존재한다고 할 때, 두 프로세스가 접근할 수 있는 특정한 파일이 있다고 가정하겠습니다.

  1. 프로세스 A가 먼저 특정 파일을 읽기 위해 파일이 존재하는지 검사 후 CPU에 제어권을 반납하게 됩니다.
  2. 프로세스 A로부터 넘겨받은 CPU 제어권을 프로세스 B가 받아서 존재하던 File을 삭제(또는 변경) 후 다시 CPU 제어권을 반납합니다.
  3. CPU 제어권을 되찾은 프로세스 A가 검사했던 File을 사용하려고 하지만 File이 존재하지 않거나 기존에 검사한 파일과 다른 형태가 되어 오류가 발생하게 됩니다.

이와 같은 과정으로 인하여 발생할 수 있는 오류를 발생시키려 의도하는 것이 TOCTOU 공격이며, 이를 해결하기 위해 synchronized를 이용하거나, 아니면 일부분에 대한 락을 적용하거나, 원자적 연산을 통해 검사와 사용을 하나의 작업단위로 처리하는 방법을 사용할 수 있는 것이었습니다.


추가적으로 docker에서 발견된 TOCTOU 공격에 대한 취약점이 무엇인지 찾아보았습니다.

Reference : https://core-research-team.github.io/2021-04-01/Docker-Race-Condition-Vulnerability-CVE-2018-15664

해당 사이트에서 구체적으로 설명이 되어있고, docker로 해당 취약점을 직접 테스트 해볼 수 있는 코드도 존재합니다. 직접 테스트하여 그 과정을 설명드리고 싶었지만 아직 docker에 대해 익숙치 않아, 이론적인 부분만 정리하였습니다.


Docker에서 TOCTOU 공격에 대한 취약점을 만들어낸 것은 FollowSymlinkInScope라는 함수였습니다.

FollowSymlinkInScope 함수의 기능은 다음과 같습니다.

이 짧은 순간 동안, 접근하던 파일에 새롭게 심볼릭 링크를 걸어서 접근이 제한된 컨테이너 바깥 영역의 파일을 가리키도록 하면 루트 권한을 이용해 접근이 가능해집니다.


마지막으로 다른 TOCTOU 공격을 방지하는 추가적인 방식들에 대해서는 이해하였지만, ConcurrentHashMap이 여러 세그먼트로 나뉘어져 있다는 것이 어떻게 TOCTOU 공격을 방지하는지 이해가 되지 않아 조사하였습니다.

java 7 까지의 ConcurrentHashMap은 내부 클래스로 Segment 를 가지고 있었으며 해당 Segment는 HashEntry에 해당하는 Lock을 가지면서 동시성을 고려하고 TOCTOU 공격을 방지할 수 있었습니다.

그러나 java 8부터는 Segment 내부 클래스가 ConcurrentHashMap에서 삭제되고 다른 방법으로 동시성을 관리하게 되었습니다.

    /**
     * Stripped-down version of helper class used in previous version,
     * declared for the sake of serialization compatibility
     */
    static class Segment<K,V> extends ReentrantLock implements Serializable {
        ...
    }

open jdk 8을 보면 Segment 클래스에 대한 설명을 통해 Segment 클래스가 더이상 사용되고 있지 않다는 것을 알 수 있습니다.

open jdk 설명

java 8 부터는 ConcurrentHashMap의 동시성을 Segment 클래스로 관리하는 대신 atomic 패키지처럼 CAS 연산을 통해 관리한다고 합니다.


혹여, 제가 정리한 부분에서 올바르지 않은 내용이 있다면 지적해주시기 바랍니다!

glenn-syj commented 3 months ago

잘 정리해주셔서 감사합니다! 사실 이런 문제에 대해서 깊게 알게 되기 어렵다고 생각하는데, 덕분에 ConcurrentHashMap에 대해서도 더 알게된 것 같습니다. 관련해서는 저도 추가 조사를 해보겠습니다!!