Rails에서 Constantize를 사용하지 않는 또 다른 이유


배경 이야기
최근 한 친구는 후속 코드 사용 결과가 없어도 사용자가 입력할 때 constantize으로 전화하는 것이 위험하냐고 물었다.
예를 들어, 이 Rails 코드는 질의 매개 변수를 상수로 변환합니다.
params[:class].classify.constantize
Brakeman은 이 코드에 대한 "원격 코드 실행"경고를 보고합니다.
Confidence: High
Category: Remote Code Execution
Check: UnsafeReflection
Message: Unsafe reflection method `constantize` called with parameter value
Code: params[:class].classify.constantize
File: app/controllers/users_controller.rb
Line: 7
그런데 왜요?물론 문자열을 상수로 변환할 뿐입니다. (상수가 존재한다면!)위험하진 않겠지?우리는 어떻게 그것으로 코드를 집행합니까?좋아요.
공교롭게도 같은 시간에 나는 루비의 반서열화 작은 도구인 특히 this one을 연구하고 있다. 그 중에서 루비의 Digest 모듈은 모듈 이름에 따라 파일을 불러올 것이다.예를 들어 Digest::Arequire 'digest/a'을 시도합니다.
2.7.0 :001 > require 'digest'
 => true 
2.7.0 :002 > Digest::Whatever
Traceback (most recent call last):
        5: from /home/justin/.rvm/rubies/ruby-2.7.0/bin/irb:23:in `<main>'
        4: from /home/justin/.rvm/rubies/ruby-2.7.0/bin/irb:23:in `load'
        3: from /home/justin/.rvm/rubies/ruby-2.7.0/lib/ruby/gems/2.7.0/gems/irb-1.2.1/exe/irb:11:in `<top (required)>'
        2: from (irb):2
        1: from /home/justin/.rvm/rubies/ruby-2.7.0/lib/ruby/2.7.0/digest.rb:16:in `const_missing'
LoadError (library not found for class Digest::Whatever -- digest/whatever)
Digest 라이브러리는 const_missing 갈고리를 사용하여 이 기능을 실현한다.
이것은 나로 하여금 constantizeconst_missing이 연결될 수 있는지, 그리고 어떤 결과가 발생할지 궁금하게 한다.

레일 속의 영원한 불변
Rails turns a string into a constantconstantize 방법입니다.이 상수가 존재하지 않으면 NameError이 발생한다.
그러나 const_missing 방법을 정의하여 Ruby에서 상수 검색 프로세스에 연결할 수 있습니다.주어진 모듈에서 상수를 찾을 수 없고 이 모듈이 const_missing으로 정의되어 있으면 const_missing을 호출합니다.
2.7.0 :001 > module X
2.7.0 :002 >   def self.const_missing(name)
2.7.0 :003 >     puts "You tried to load #{name.inspect}"
2.7.0 :004 >   end
2.7.0 :005 > end
 => :const_missing 
2.7.0 :006 > X::Hello
You tried to load :Hello
 => nil
만약 const_missing이 상수 명칭을 기반으로 하는 행위를 통해 이루어진다면, 예를 들어 파일을 불러오거나 새로운 대상을 만들면 악의적인 행위가 발생할 수 있다.

여린 보석들
다행히도 const_missing은 자주 사용하지 않는다.만약 그렇다면, 실현은 통상적으로 이용할 수 없다.
나는 약 1300개의gem를 검색했는데 약 40개의gem만 찾았고 const_missing을 실현했다.
그중 대부분은 사용할 수 없는 것이다. 왜냐하면 예상치에 따라 상수 이름을 검사하거나 const_get을 호출하면 상수가 존재하지 않으면 이상을 일으킬 수 있기 때문이다.
보석 한 개, coderayloads files based on constant names, 도서관처럼.요약 라이브러리와 마찬가지로 이 파일들은 coderay 디렉터리에만 한정되어 있기 때문에 사용할 수 없을 것 같습니다.
다음 두 개의gem에 메모리 유출이 있습니다. 이것은 메모리 소모를 통해 서비스 공격을 거부할 수 있습니다.

활용단어참조
Templegem은 Haml, Slim 및 기타 템플릿 라이브러리에서 사용되는 기본 gem입니다.
Temple에는 다음과 같이 Temple::Mixins::GrammarDSL이라는 모듈이 있습니다.
def const_missing(name)
  const_set(name, Root.new(self, name))
end
이 방법은 주어진 const_missing을 기반으로 새 상수를 만들고 새 대상을 지정합니다.
이것은 쓰레기가 영원히 수집되지 않기 때문에 메모리 유출이다.만약 공격자가 그것을 촉발할 수 있다면, 그들은 무한한 수량의 영구 대상을 만들고 가능한 한 메모리를 사용할 수 있다.
불행히도 이 코드를 이용하는 것은 매우 쉽다.nameTemple::Grammar으로 확장되어 Temple의 핵심 과정입니다.Haml에서 로드되는지 확인합니다. Haml은 Rails에 자주 사용되는 템플릿 라이브러리입니다.
2.7.0 :001 > require 'haml'
 => true 
2.7.0 :002 > Temple::Grammar
 => Temple::Grammar
위대하다만약 우리가 존재하지 않는 모듈을 인용하려고 시도한다면, 무슨 일이 일어날까요?
2.7.0 :003 > Temple::Grammar::DefinitelyDoesNotExist
 => #<Temple::Mixins::GrammarDSL::Root:0x000055a79b011060 @grammar=Temple::Grammar, @children=[], @name=:DefinitelyDoesNotExist> 
상수는 위의 그림에서 보듯이 새 객체와 함께 작성됩니다.
한층 더...constantize의 사용은 이 코드를 호출합니까?
Haml을 사용하여 어플리케이션에 대한 Rails 콘솔을 로드하는 테스트를 수행할 수 있습니다.
Loading development environment (Rails 6.0.3.2)
2.7.0 :001 > require 'haml'
 => false 
2.7.0 :002 > 'Temple::Grammar::DefinitelyDoesNotExist'.constantize
 => #<Temple::Mixins::GrammarDSL::Root:0x000055ba28031a50 @grammar=Temple::Grammar, @children=[], @name=:DefinitelyDoesNotExist> 
네!
Haml이나 Slim을 사용하는 모든 Ruby on Rails 응용 프로그램은 사용자가 입력할 때 Template::Mixins::GrammarDSL(예를 들어 constantize)을 호출하면 이런 방법을 통해 메모리 유출이 발생하기 쉽다.

원력을 회복하다
restforcegem에서 매우 유사한 코드 모델을 실현하였다.
ErrorCode module은 다음과 같이 params[:class].classify.constantize을 사용합니다.
module ErrorCode
  def self.const_missing(constant_name)
    const_set constant_name, Class.new(ResponseError)
  end
end
거의 마찬가지다. 단지 이것은 실제적으로 새로운 클래스를 만들었을 뿐, 일반적인 대상이 아니다.
Rails 콘솔에서 다음을 다시 확인할 수 있습니다.
Loading development environment (Rails 6.0.3.2)
2.7.0 :001 > require 'restforce'
 => false 
2.7.0 :002 > Restforce::ErrorCode::WhateverWeWant
 => Restforce::ErrorCode::WhateverWeWant 
이번에 우리는 새 수업을 하고 싶은 만큼 수업을 한다.
Restforce 5.0.0에서 수정되었습니다.

메모리 유출 찾기 및 이용
생산 프로그램에서 이렇게 공격받기 쉬운 코드를 찾는 것은 매우 어려울 것이다.어떤 파라미터가 const_missingd일 수 있는지 추측하기만 하면 됩니다.
메모리 유출을 발견했는지 확인하는 것은 좀 까다롭습니다. 위에서 설명한 두 개의 메모리 유출 생성 대상은 매우 적습니다.
Temple의 새로운 constantize 대상은 약 300바이트의 메모리를 사용하고 Restforce의 새로운 클래스는 1000바이트에 가까운 메모리를 차지할 것으로 추정됩니다.
이 점을 바탕으로 테스트한 결과 1GB 메모리만으로 100만 ~ 400만 개의 요청이 필요합니다.
웹 응용 프로그램이 정기적으로 다시 시작되고, 프로세스를 죽이고, 새로운 프로세스를 시작하는 것이 보통 대수롭지 않다는 것을 감안하면, 이것은 그다지 효과적이지 않은 것 같다.
그러나 이것은 짜증나고 작은 사이트에 해로울 수도 있다.예를 들어, 기본 Heroku 인스턴스는 512MB에 불과합니다.
메모리 유출은 보호되지 않은 Rule 호출의 최악의 결과가 아니라는 점도 주의해야 한다.원격 코드 실행을 촉발할 가능성이 더 높다.내가 여기서 탐구하고자 하는 진정한 문제는 의존항에 숨겨질 수 있는 의외의 행위이다.

결론
Rails 애플리케이션에서는 constantize을 사용하지 마십시오.만약 그것을 사용해야 한다면, constantize을 호출하기 전에 허용된 클래스 이름 집합을 검사하십시오.(단, 먼저 constantize을 쳐도 됩니다.)
Ruby 라이브러리의 classify도 마찬가지입니다.상수 이름을 사용하여 동적 작업 (파일 불러오기, 새 대상 만들기, 코드 계산 등) 을 피해야 합니다.이상적인 경우 예상된 이름 목록을 대조하여 검사하고 다른 내용을 거부합니다.
마지막으로 이것은 사용자의 입력을 불신하고 입력을 엄격하게 검증하는 안전한 기초로 귀결된다.

좋은 웹페이지 즐겨찾기