Oj는 Ruby 생태계에서 ‘가장 빠른 JSON 파서 및 객체 직렬 변환기’로 알려져 있으며, 많은 프로젝트에서 성능 향상을 위해 사용됩니다. 그러나 Oj의 ‘객체 직렬화’ 기능은 보안 관점에서 심각한 문제를 야기할 수 있습니다. 기본적으로 Oj는 콜론으로 시작하는 모든 문자열을 Ruby 심볼로 자동 변환하는데, 이는 Oj.load('{"name":":xyzzydeadbeef"}')
와 같은 간단한 코드를 통해 확인할 수 있습니다. 이러한 기본 동작은 공격자가 임의의 심볼을 주입하여 애플리케이션의 동작을 조작할 수 있는 길을 열어줍니다.
필자는 이러한 ‘기본적으로 안전하지 않은(Insecure By Default)’ 소프트웨어 설계 관행을 강력히 비판합니다. 소프트웨어 개발자는 사용자가 올바르고 안전한 방식으로 소프트웨어를 사용하도록 유도할 책임이 있으며, 위험한 기능은 명확하게 경고하거나 unsafe_load
와 같이 명시적인 이름을 부여해야 한다고 주장합니다. 현재 Oj의 문서화는 이러한 위험성을 충분히 강조하지 않아, 개발자들이 무심코 취약한 코드를 작성할 수 있게 합니다.
이 취약점의 악용 사례는 다양합니다. 예를 들어, 공격자가 {"name":":admin_notes"}
와 같은 JSON을 전송하면, Oj는 이를 {"name"=>:admin_notes}
로 변환합니다. 만약 애플리케이션이 이 값을 사용하여 SQL 쿼리를 구성한다면, UPDATE users SET name=admin_notes WHERE id=42
와 같은 쿼리가 실행되어 admin_notes
컬럼의 내용이 name
컬럼으로 복사될 수 있습니다. 이로 인해 공격자는 일반적으로 접근할 수 없는 민감한 정보를 읽을 수 있게 됩니다. 더욱 심각하게는, Oj의 객체 직렬화 기능을 통해 공격자가 임의의 Ruby 객체를 생성하고, 이를 통해 원격 코드 실행(RCE)을 유발할 가능성도 있습니다. 이는 공격자가 시스템에 대한 완전한 제어권을 얻을 수 있음을 의미합니다.
이러한 위험을 완화하기 위한 몇 가지 전략이 제시됩니다. 첫째, 프로젝트의 Gemfile.lock
을 확인하여 oj
gem이 사용되고 있는지 확인해야 합니다. 둘째, 가능하다면 oj
대신 JSON.parse
와 같이 안전한 json
gem을 사용하는 것이 좋습니다. 셋째, oj
를 반드시 사용해야 한다면, 모든 Oj.load
호출을 Oj.safe_load
로 변경해야 합니다. 넷째, 객체 역직렬화를 위해 oj
를 사용하는 것은 매우 위험하므로, Psych.load
의 permitted_classes
와 같이 안전 기능이 있는 다른 라이브러리로 전환하는 것을 강력히 권장합니다. 마지막으로, 종속성 내에서 안전하지 않은 Oj.load
사용을 방지하기 위해 애플리케이션 초기화 코드에 Oj.default_options = { mode: :strict }
를 설정하여 기본 파싱 모드를 엄격하게 제한하는 것이 중요합니다.