처리중입니다. 잠시만 기다려주세요.
TTJ 코딩클래스
정규반 단과 자료실 테크 뉴스 코딩 퀴즈
테크 뉴스
Hacker News 2026.04.26 31

파일 하나 여는 게 이렇게 어려운 일이었다니

Hacker News 원문 보기

도입

리눅스에서 open("/etc/passwd", O_RDONLY) 한 줄로 파일을 여는 건 우리 일상이잖아요. 그런데 시스템 프로그래머들 사이에서는 이 한 줄 뒤에 숨어 있는 복잡함이 진짜 만만치 않다는 게 오래된 농담이에요. C로 좀 깊이 들어가본 분이라면 open()이라는 시스템 콜을 써보셨을 거예요. 파일 경로랑 플래그 몇 개 던져주면 파일 디스크립터(fd, 파일을 가리키는 정수 식별자)가 돌아오죠. 보통 거기서 끝이에요.

그런데 Sebastian Wick라는 개발자가 쓴 글이, 이 단순해 보이는 동작 뒤에 얼마나 많은 함정이 도사리고 있는지 차근차근 정리해놨거든요. 컨테이너, 멀티스레드, 보안, 심볼릭 링크, 권한 검사 같은 현대적 요구사항이 쌓이면서 "파일을 안전하게 열기"가 운영체제 수준의 난제가 됐어요.

핵심 내용

경로 해석은 원자적이지 않아요

open("/a/b/c", ...)를 호출하면 커널은 루트(/)부터 한 단계씩 디렉토리를 따라 내려가면서 마지막 c를 찾아요. 문제는 이 과정 중간에 누군가 디렉토리를 갈아치울 수 있다는 거예요. 이걸 TOCTOU(Time-Of-Check to Time-Of-Use) 경합이라고 부르는데요, 권한 검사한 시점과 실제 파일을 여는 시점 사이에 공격자가 심볼릭 링크를 바꿔치기하면 의도치 않은 파일을 열게 되는 보안 취약점으로 이어져요.

이걸 방지하려고 리눅스는 openat(), openat2() 같은 시스템 콜을 추가했어요. 특히 openat2()에는 RESOLVE_NO_SYMLINKS(심볼릭 링크 따라가지 마)나 RESOLVE_BENEATH(시작 디렉토리 밑에서만 찾아) 같은 플래그를 줄 수 있어서, 공격자가 경로를 조작해도 안전하게 막을 수 있게 됐죠.

O_PATH라는 이상한 모드

리눅스에는 O_PATH라는 플래그도 있어요. 이걸 쓰면 파일을 "참조"만 할 뿐 실제로 읽거나 쓰지는 못하는, 일종의 핸들만 받아오는 거예요. 왜 이런 게 필요할까요? 디렉토리 트리를 안전하게 탐색할 때, 또는 권한 없이도 메타데이터(파일 정보)에 접근할 때 유용해요. 이런 fd를 다른 시스템 콜에 넘겨서 진짜 작업을 수행하는 식이에요.

권한 검사도 함정 투성이

전통적인 권한 비트(rwx) 검사는 단순해 보이지만, 실제로는 ACL(접근 제어 리스트), SELinux/AppArmor 같은 LSM(Linux Security Module), capabilities, 컨테이너의 user namespace까지 합쳐져서 결과가 결정돼요. 게다가 NFS 같은 네트워크 파일시스템은 서버 쪽에서 다시 검사하는 경우도 있어서, 클라이언트와 서버의 권한 모델이 다르면 예상 못한 거부가 발생할 수 있어요. "왜 이 파일을 못 여는데 권한은 분명히 맞는데?" 같은 미스터리의 90%는 여기서 옵니다.

업계 맥락

이런 복잡함은 리눅스만의 문제가 아니에요. macOS는 O_SYMLINK, getattrlist() 같은 BSD 계열 API를 두고 비슷한 고민을 하고 있고, 윈도우는 NT 커널의 NtCreateFile이 더 많은 옵션을 노출해요. Plan 9는 파일시스템 자체를 네트워크 프로토콜로 다시 설계하면서 이 문제를 다른 각도에서 풀려고 했죠.

최근에는 Rust의 std::fs 같은 라이브러리도 openat2를 활용한 안전한 경로 처리를 도입하고 있고, 컨테이너 런타임(runc, containerd)들은 컨테이너 탈출 취약점을 막기 위해 이런 저수준 API를 적극적으로 쓰고 있어요. CVE-2019-5736 같은 유명한 취약점이 바로 파일 디스크립터를 통한 호스트 침투였거든요.

한국 개발자에게 주는 시사점

대부분의 웹/앱 개발에서는 open()의 깊은 동작까지 알 필요는 없어요. 하지만 컨테이너 보안, CI/CD 파이프라인, 사용자 업로드 처리 같은 곳에서 한 번쯤 마주치게 돼요. 사용자가 업로드한 zip 파일을 풀 때 ../../../etc/passwd 같은 경로 트래버설 공격을 안전하게 막으려면 결국 openat2RESOLVE_BENEATH 같은 안전장치를 쓰는 게 답이에요.

또 백엔드 엔지니어로서 시스템 추상화가 어디서 새는지 감을 잡아두면, 이상한 버그가 났을 때 훨씬 빠르게 원인을 찾을 수 있어요. 운영체제는 어디까지나 우리가 발 딛고 있는 땅이고, 그 땅이 어떻게 생겼는지 한번 들여다보는 건 늘 가치가 있어요.

마무리

파일 하나 여는 일이 이렇게 까다로운 줄 모르셨다면, 이번 기회에 man 2 openat2를 한 번 읽어보세요. 운영체제의 깊은 곳을 들여다보는 재미가 쏠쏠합니다.

여러분은 시스템 프로그래밍에서 open()과 관련해 가장 황당했던 버그 경험이 있으세요?


🔗 출처: Hacker News

이 뉴스가 유용했나요?

이 기술을 직접 배워보세요

AI 도구, 직접 활용해보세요

AI 시대, 코딩으로 수익을 만드는 방법을 배울 수 있습니다.

AI 활용 강의 보기

"비전공 직장인인데 반년 만에 수익 파이프라인을 여러 개 만들었습니다"

실제 수강생 후기
  • 비전공자도 6개월이면 첫 수익
  • 20년 경력 개발자 직강
  • 자동화 프로그램 + 소스코드 제공

매일 AI·개발 뉴스를 받아보세요

주요 테크 뉴스를 매일 아침 이메일로 전해드립니다.

스팸 없이, 언제든 구독 취소 가능합니다.