# Atom 고갈은 실수가 아니다. 우리 CVE의 3분의 1이다

> Clean Markdown view of GeekNews topic #29948. Use the original source for factual precision when an external source URL is present.

## Metadata

- GeekNews HTML: [https://news.hada.io/topic?id=29948](https://news.hada.io/topic?id=29948)
- GeekNews Markdown: [https://news.hada.io/topic/29948.md](https://news.hada.io/topic/29948.md)
- Type: GN+
- Author: [neo](https://news.hada.io/@neo)
- Published: 2026-05-28T12:02:27+09:00
- Updated: 2026-05-28T12:02:27+09:00
- Original source: [erlef.org](https://erlef.org/blog/security/atom-exhaustion)
- Points: 1
- Comments: 1

## Topic Body

- **EEF CNA**가 공개한 CVE의 35.8%는 제어되지 않은 리소스 소비이며, BEAM 생태계에서는 반복적인 atom 고갈이 큰 비중을 차지함
- **Atom 고갈**은 서비스 거부 취약점으로, atom은 가비지 컬렉션되지 않고 전역 테이블에 쌓이며 테이블이 가득 차면 VM이 크래시됨
- **사용자 입력**처럼 가능한 값의 집합이 유한하다고 보장되지 않는 데이터에서 atom을 만들면 DoS 위험이 생기며, URI scheme도 예외가 아님
- 위험은 `binary_to_atom/1`, `String.to_atom/1` 같은 명시적 호출뿐 아니라 **JSON 키 atom 디코딩**과 문자열 보간 기반 동적 생성에도 존재함
- 안전한 처리는 런타임 새 atom 생성을 피하고, 알려진 값은 **명시적 조회 테이블**이나 `to_existing_atom` 계열로 제한하며 린터로 점검해야 함

---

### Atom 고갈이 만드는 서비스 거부 취약점
- **EEF CNA**가 공개한 CVE 중 **35.8%** 는 제어되지 않은 리소스 소비이며, BEAM 생태계에서는 반복적인 atom 고갈 문제가 큰 비중을 차지함 {p:36}
- 현재 분포는 EEF CNA의 [Common Weaknesses](https://cna.erlef.org/common-weaknesses) 페이지에서 확인 가능함
- **Atom 고갈**은 서비스 거부(DoS) 취약점임
  - Atom은 가비지 컬렉션되지 않음
  - 전역 atom 테이블에 저장됨
  - 테이블이 가득 차면 VM이 크래시됨
- 유한하지 않은 값, 특히 **사용자 입력**에서 atom을 만들면 잠재적인 DoS가 됨
- 위험은 명백한 호출에만 한정되지 않음
  - Erlang의 `binary_to_atom/1`, `list_to_atom/1`
  - Elixir의 `String.to_atom/1`, `List.to_atom/1`
- 덜 눈에 띄는 위험 패턴도 존재함
  - Erlang에서 보간을 통한 동적 atom 생성:
    ```erlang
    % Erlang: 보간을 통한 동적 atom 생성
    list_to_atom("field_" ++ UserInput)
    ```
  - Elixir에서 JSON 키를 atom으로 디코딩:
    ```elixir
    
    
    
    # Elixir: JSON을 atom 키로 디코딩
    Jason.decode(json, keys: :atoms)
    ```
  - Elixir에서 보간을 통한 동적 atom 생성:
    ```elixir
    
    
    
    # Elixir: 보간을 통한 동적 atom 생성
    :"field_#{user_input}"
    ```

### 안전한 처리 방식과 점검 대상
- Atom 고갈 취약점은 단순한 부주의가 아니라, 입력이 통제되거나 유한하다고 가정한 코드에서 자주 생김
- **URI scheme**은 대표적인 예시임
  - 처리할 scheme이 몇 개뿐이라고 느껴질 수 있음
  - 값이 외부 입력에서 오면 가능한 집합이 더 이상 유한하다고 보장되지 않음
- 입력에서 atom을 만드는 코드는 가능한 값의 집합이 **유한하고, 알려져 있으며, 강제되는 경우**가 아니면 안전하지 않음
- 가장 안전한 접근은 런타임에 새 atom을 만들지 않는 것임
- 허용 값이 알려져 있다면 **명시적 조회 테이블**을 쓰는 편이 안전함
  ```erlang
  % Erlang
  case Scheme of
      <<"http">> -> http;
      <<"https">> -> https;
      _ -> error
  end
  ```
- 조회 테이블이 실용적이지 않을 때는 새 atom을 만들지 않고 기존 atom만 사용하는 변형을 써야 함
  - 이 함수들은 새 atom을 생성하지 않고 오류를 발생시킴
  ```erlang
  % Erlang
  binary_to_existing_atom(Value)
  list_to_existing_atom(Value)
  ```
  ```elixir
  
  
  
  # Elixir
  String.to_existing_atom(value)
  List.to_existing_atom(value)
  ```
- 린터는 취약점으로 이어지기 전 위험 패턴을 잡는 데 도움이 됨
  - Elixir 프로젝트에서는 Credo의 [Credo.Check.Warning.UnsafeToAtom](https://hexdocs.pm/credo/Credo.Check.Warning.UnsafeToAtom.html) 활성화를 고려할 수 있음
  - 이 검사는 `String.to_atom/1`, `List.to_atom/1`, `Module.concat/1,2`, `keys: :atoms`를 사용하는 `Jason.decode/2`의 안전하지 않은 호출을 표시함
  - 해당 검사는 기본적으로 비활성화되어 있음
- Erlang 또는 Elixir 프로젝트 유지보수자는 바이너리, 문자열, JSON 키, URI 구성요소, 헤더, 설정 값에서 atom을 생성하는 코드를 검색해야 함
- 이 취약점 범주는 CVE가 되기 전에 고치기 쉬운 유형 중 하나임
- 더 자세한 지침은 EEF Security Working Group의 [atom 고갈 방지 가이드](https://security.erlef.org/secure_coding_and_deployment_hardening/atom_exhaustion)에 정리되어 있음

## Comments



### Comment 58434

- Author: neo
- Created: 2026-05-28T12:02:28+09:00
- Points: 1

###### [Lobste.rs 의견들](https://lobste.rs/s/lfusbg/atom_exhaustion_is_not_footgun_it_s_one) 
- Ruby에서 `Symbol`이 **가비지 컬렉션 대상**이 되기 전 상황과 비슷하게 들림

- 제목이 이해가 안 감. 이건 확실히 **footgun**처럼 보임
  - 제목의 요지는 atom 고갈을 “그냥 footgun”이라고 부르면 문제의 심각성이 과소평가된다는 뜻 같음
  - 기억이 맞다면, Erlang을 매일 쓰진 않지만 **atom은 가비지 컬렉션되지 않음**  
    “Ruby에도 Erlang atom 같은 symbol이 있지 않나?”라고 생각하면 맞지만, Ruby는 symbol을 가비지 컬렉션함  
    게다가 기본적으로 Erlang atom이 저장되는 조회 테이블은 최대 **1,048,576개**만 허용함  
    폼 같은 사용자 입력으로 atom을 동적으로 생성하면 매우 위험하고, 소프트웨어가 서비스 거부 공격에 노출됨
  - “그냥” 단순한 footgun보다 더 큰 문제라는 뜻으로 이해했음  
    다만 내 경험상 “footgun” 자체가 꽤 넓은 표현이라, 어느 쪽이든 제목 문구는 어색함
  - 맞음, 그것도 엄청나게 큰 footgun처럼 보임

- 뭔가 핵심적인 부분의 **설계나 구현이 나쁜** 것처럼 들려서 놀라움. 인터넷에서 계속 칭찬받던 언어라 더 의외임
  - BEAM atom은 본질적으로 **인터닝된 문자열**이고, 전역 바이트↔정수 테이블을 가짐  
    그 테이블에 참조 카운트를 추가하면 비용이 크고, 수십 년간 존재해 온 코드의 확장 특성이 바뀜  
    atom 최대 개수는 기본값이 100만 개이며, VM 시작 시점에 정해짐  
    함정이긴 하지만 피하기 어렵지는 않음. 오래전부터 권장사항은 “사용자 입력으로 atom을 만들지 말라”였음  
    예를 들어 JSON을 파싱한다면 보통 키를 atom으로 변환하지 않거나, 이미 존재하는 atom일 때만 변환함. 이렇게 하면 atom 키로 패턴 매칭할 수 있고, 코드 로딩으로 해당 atom들이 이미 만들어져 있으며, 포괄 절은 atom 대신 문자열을 받을 수 있음
  - Erlang은 특수한 경우에 전문 프로그래머들이 쓰던 **니치 언어**였다는 점을 감안해야 함  
    Elixir는 훨씬 더 대중적이라, Erlang 개발자는 이를 알고 있을 가능성이 크지만 Elixir 개발자는 모를 가능성이 있음

- 개인적으로는 atom을 그런 식으로 쓰는 것 자체가 이상하게 느껴짐. Erlang의 atom을 대략 C의 **enum 타입**과 비슷하게 이해하고 있기 때문임  
  특정 방식으로 단어를 입력하면 내부적으로 enum이 되는 편의 기능이라고 봄  
  글에서는 사용자 입력을 언급하지만, 애초에 사용자 입력으로 새 enum 타입을 만들고 싶을 만한 사용 사례가 왜 있는지 모르겠음. 쓰임새가 극히 좁아 보임  
  옆 댓글들은 파싱을 말하지만, 이상적으로는 미리 알려진 자료 구조를 파싱하는 것 아닌가? 뭔가 놓치고 있는 느낌임
  - 질문과는 조금 빗나가지만, [K](https://en.wikipedia.org/wiki/K_(programming_language))에서는 symbol이 전역으로 인터닝되고, Erlang처럼 K 프로세스에서 **symbol 테이블**을 고갈시켜 죽일 수 있음  
    그 언어에서 symbol을 단순히 빠른 동등성 비교 이상의 별도 타입으로 두는 장점은 최소 두 가지임. symbol은 atom, 즉 문자들의 리스트형 시퀀스가 아니라 원자적 단위라 여러 연산자가 다르게 취급하고, symbol은 **벡터화**되어 단일 타입 리스트에 조밀하게 저장될 수 있음  
    K와 Q에서는 데이터베이스 테이블의 열을 벡터화된 타입으로 표현하는 것이 매우 바람직함. 지역성이 좋고, 메모리를 더 효율적으로 쓰며, 여러 연산자에 빠른 경로가 많기 때문임. 하지만 symbol 테이블 제약 때문에, 카디널리티가 높은 열에 symbol을 쓸 때는 조심해야 함  
    알려진 스키마의 JSON을 파싱한다면 symbol은 딕셔너리 키로 훌륭하고, k2/k3에서는 사실상 필수임. 하지만 정체를 모르는 JSON이라면 사용자 입력에서 오면 안 됨  
    일부 K 방언에서는 symbol 길이를 짧게 제한해서 64비트 값으로 포장하고 옮길 수 있게 함. 일반성을 포기하는 대신 symbol 테이블 자체가 필요 없어짐

- “통제된 입력”과 “통제되지 않은 입력”의 구분은 보안에서 `null` 여부 같은 것처럼 느껴짐  
  `webpack-plugin-less-css`가 신뢰할 수 없는 CSS 파일을 받으면 서비스 거부가 난다는 식의 항목을 보면 **CVE 피로감**이 꽤 큼  
  그래도 여기서 더 나은 경계 표시가 있으면 좋겠음. 예를 들어 문자열 연결을 거치면 어떤 안전 속성이 보존되는지 같은 조합 규칙도 잘 다룰 수 있으면 좋음  
  그리고 HTTP POST에서 받은 것들을 잔뜩 `SafeString` 처리했다면, 그건 어느 정도 본인 책임
