Ruby 보안 트랩 및 이를 피하는 방법
날카로운 칼
루비는 처음부터 개발자의 행복감에 집중하는 특성으로 유명했는데, 이는 전적으로 옳았다.루비에 대한 명언Rails doctrine은 약간 이렇다.
Ruby includes a lot of sharp knives in its drawer of features. There’s nothing programmatically in Ruby to stop you using its sharp knives to cut ties with reason.
이것은 상당히 정확한 평가이다.네가 날카로운 칼을 제공할 때, 조만간 누군가가 중상을 입을 것이다. 이것은 시간문제일 뿐이다.
Ruby와 Rails의 일반적인 방식을 살펴보겠습니다. 개발자가 몇 가지 중요한 안전 고려 요소를 소홀히 하여 큰 손해를 입었습니다.코드에 대한 작은 조정과 조정을 통해 이런 손실을 방지하는 방법도 볼 수 있다.
안전하지 않은 반서열화
사용자가 제출할 수 있는 모든 내용을 실행하는 것은 항상 나쁜 생각입니다.코드를 실행하지 않았기 때문에, 서열화와 반서열화 사용자 입력은 나쁜 생각은 아닌 것 같습니다.하지만 사실은 그렇다!사실상 안전하지 않은 반서열화는 OWASP Top Ten 중의 하나로 웹 안전의 기본 검사표이다.
Ruby에 내장된 YAML 라이브러리는 Psych를 기반으로 하여 사용자 정의 데이터 유형을 YAML로 서열화하고 반환할 수 있습니다.여기에 나열된 코드와 생성된 YAML을 참조하십시오.
# serialize.rb
require 'yaml'
require 'set'
s = Set.new([1, 2, 3])
File.open('set.yml', 'w') do |file|
YAML.dump(s, file)
end
-------- !ruby/object:Set
hash:
1: true
2: true
3: true
이 YAML을 역서열화하면 원래 데이터 유형이 반환됩니다.# deserialize.rb
require 'yaml'
require 'set'
file = File.read('set.yml')
s = YAML.load(file)
p s
$ ruby deserialize.rb
#<Set: {1, 2, 3}>
보시다시피 YAML의 행-- !ruby/object:Set
은 텍스트 표현에서 객체를 인스턴스화하는 방법을 설명합니다.이 실례화가 코드를 실행할 수 있을 때 slew of attack vectors, that can escalate to RCE 창을 열 것입니다.솔루션
해결 방법은 안전 적재를 사용하는 것이다.이것은
YAML::safe_load
함수만 사용하고 YAML::load
함수만 사용하지만, 사용자 정의 클래스의 불러오는 것을 완전히 막는다.# deserialize.rb
require 'yaml'
require 'set'
file = File.read('set.yml')
s = YAML.safe_load(file)
p s
$ ruby deserialize.rb
/usr/ruby/2.7.0/psych/class_loader.rb:97:in `find':
Tried to load unspecified class: Set (Psych::DisallowedClass)
산열과 그룹 같은 표준 형식은 YAML 문서를 이전처럼 서열화하고 그 중에서 반서열화할 수 있습니다. 이것은 당신이나 대부분의 사람들이 가장 먼저 하고 싶은 일입니다.--------
hash:
1: true
2: true
3: true
$ ruby deserialize.rb
{"hash"=>{1=>true, 2=>true, 3=>true}}
목위일납치
만약 응용 프로그램이 디스크에서 사용자가 지정한 파일을 읽거나 쓰거나 사용자가 지정한 URL에 API 요청을 보내야 한다면, 이러한 코드에 익숙할 것입니다.
# test.rb
puts 'Enter file path:'
path = gets.chomp
puts 'File contents:'
open(path, 'r') do |file|
until file.eof? do
puts file.gets
end
end
이것은 보기에 상당히 무해하다, 그렇지?파일 이름을 입력하고 파일이 끝날 때까지 한 줄씩 인쇄할 것을 요구합니다.그것의 작업 원리도 네가 예상한 것과 같다.그러나 문제를 보지 못하면 문서를 충분히 읽지 못했을 수도 있습니다.위의 코드 세션에서 사용하는
Kernel::open
함수에는 프로세스를 생성하고 파이프를 출력하는 데 사용할 수 있는 추가 기능이 있습니다.파이프 ((|
문자로 시작하는 입력을 전달할 때 어떤 일이 일어나는지 보십시오.$ ruby test.rb
Enter file path:
|date
File contents:
Sun Nov 15 22:03:33 IST 2020
이것은 date
명령을 실행하고 명령의 출력을 루비로 전송하며 루비는 파일을 읽는 것처럼 명령을 읽습니다.물론 악성 사용자가 실행하는 명령은 date
보다 더 파괴적이다.이것은 기본적으로 RCE입니다. 원격 사용자가 서버에서 코드를 실행하고 웹 응용 프로그램에서 사용할 수 있는 모든 권한을 가지고 있습니다.반복적으로 들리는 위험을 무릅쓰고 신뢰할 수 없는 소스 (예를 들어 사용자) 의 코드를 영원히 실행하지 마십시오. 그러나 만약 이렇게 해야 한다면, 최소한 선택한 자원에 대한 접근을 제한할 수 있습니다.
솔루션
사용하지 마십시오
Kernel::open
.더 좋은 선택은 File::open
또는 URL::open
또는 IO::open
를 사용하는 것이다.이번에는 File::open
대신 Kernel::open
를 써서 그것의 작업 원리가 어떻게 다른지 봅시다.그것은 단지 약간 다르지만, 더욱 안전하다.셸 명령에 더 이상 접근할 수 없습니다.# test.rb
puts 'Enter file path:'
path = gets.chomp
puts 'File contents:'
File.open(path, 'r') do |file|
until file.eof? do
puts file.gets
end
end
$ ruby test.rb
Enter file path:
|date
File contents:
Traceback (most recent call last):
2: from test.rb:4:in `<main>'
1: from test.rb:4:in `open'
test.rb:4:in `initialize': No such file or directory @ rb_sysopen - |date (Errno::ENOENT)
SQL 주입
우리는 모두 SQL 주입을 들은 적이 있다.만약 네가 줄곧 암석 아래에서 생활한다면, 이것이 바로 그것의 모습이다.이는 백엔드의 SQL 쿼리에서 직접 프런트엔드의 사용자 입력을 사용하여 정리를 수행하지 않는 경우에 발생합니다.
사용자 표시줄이 있다면, 폼에 입력한 이름으로 검색하십시오.다음은 Ruby에서 이를 구현하는 방법입니다.
User.where("name = '#{name}'")
하지만 이것은 매우 매우 안전하지 않다.악성 사용자로부터의 악성 입력을 고려해 보세요.name = " ' OR '1' = '1" # Malicious input
아래 첫 번째 줄에서 보듯이 조회가 악의적인 입력으로 인해 오류가 발생했습니다. 아래 두 번째 줄과 같습니다.WHERE
자문은 현재true이며, 테이블의 모든 사용자를 제공합니다.SELECT * FROM users WHERE name = '<name>' -- <name> comes from the user
SELECT * FROM users WHERE name = ' ' OR '1' = '1' -- Faulty query
보시다시피 이것은 상당히 심각한 오류이자 OWASP의 10대 오류 중 하나입니다.솔루션
해결 방안도 마찬가지로 매우 간단하다.다음 두 가지 방법 중 하나를 사용합니다.
User.where(["name = ?", name])
User.where({ name: name })
이 두 가지 방법의 목적은 모두 조회를 생성하기 전에 값을 정리하는 것이기 때문에 이런 공격을 받지 않는다.ID 자동 증가
Rails 모델을 생성하면 해당 필드
id
가 생성됩니다.그것은 통상적으로 자동으로 증가하는 정수 필드이다.대다수 상황에서 이것은 충분한 실현이다.정수 ID의 장점은 단순하며 증가함에 따라 충돌이 발생하지 않는다는 것입니다.그러나 단순성 면에서 제공하는 것들은 안전성 면에서 손해를 본다.만약 많은 서버가 서로 독립적으로 운행한다면, 그들은 최종적으로 같은 번호를 다른 자원에 분배할 가능성이 매우 높다.
경쟁사들은 자신의 ID를 등록하고 보기만 하면 얼마나 많은 고객이 있는지 알 수 있을 뿐만 아니라 성장률과 같은 업무 지표도 예측할 수 있다.
이론적으로 악의적인 사용자는 전체 목록을 하나하나 열거하고 교체하며 제한되지 않은 모든 자원을 얻을 수 있다.
링크 소유자만 볼 수 있는 목록에 없는 자원은 이 ID의 가용성에 의존하고, 이 ID가 정수일 경우 중단됩니다.
솔루션
2020년에 웹 응용 프로그램을 구축하려면 UUID를 사용하십시오.Rails는 이런 방식을 너무 간단하게 한다.이 변경 사항에 따라 모델은 모두 UUID를 주 키로 사용합니다.
# config/application.rb
config.generators do |g|
g.orm :active_record, primary_key_type: :uuid
end
또는 정수를 사용하고 싶다면 무작위로 선택한 정수를 사용할 수 있다.유튜브 영상을 살펴보고, 더 구체적으로 유튜브 영상의 URL 구조를 살펴본다.이것이 바로 유튜브 동영상 URL의 모습이다.https://www.youtube.com/watch?v=dQw4w9WgXcQ
끝에 있는 랜덤으로 보이는 알파벳 한 줄입니다. 이것이 바로 비디오 ID입니다. 기본적으로 0달러와 64^{11}$사이의 랜덤수입니다. (네, 73개의 5분의 1이 넘는 숫자입니다.)Base64 인코딩을 사용합니다.전체 UUID는 아니지만 자동으로 증가하는 정수 필드는 아닙니다.왜 유튜브는 1, 2, 3의 단순 정수 ID를 사용하지 않는지...잠깐만요?이제 왜 그런지 알겠어?토마토를 썰다
예, Ruby의 표현 능력은 보안 문제를 도입하는 데 쉬워졌지만, 해결 방안은 이러한 기능을 끄거나 다른 언어로 옮기는 것이 아닙니다.이렇게 하면 앞서 설명한 Rails 원칙으로 돌아갑니다.이 말의 다음 말은 이렇다.
We enforce such good senses by convention, by nudges, and through education. Not by banning sharp knives from the kitchen and insisting everyone use spoons to slice tomatoes.
모든 이 예들의 관건은 사용자를 영원히 믿지 않는 것이다.사용자가 제공한 입력은 서열화, 반서열화, 값을 구하거나 직접적으로 보여서는 안 된다.안전을 확보하는 유일한 정확한 방법은 당신이 쓴 내용을 조심하고 당신이 쓴 내용을 철저히 심사하는 것이다.
정적 코드 분석기
Static Code Analysis tools 예를 들어 문미와 빈틈 스캐너는 공개 공격을 하기 전에 많은 문제를 발견하는 데 도움을 줄 수 있다.예를 들어 RuboCop 을 찾을 수 있고 security problems 안전하지 않은 반서열화와 IO 납치, 가능한 복구에 대한 조언을 제공할 수 있습니다.Brakeman 유행하는 빈틈 스캐너는 일련의 다른 가능한 안전 빈틈에서 SQL 주입을 찾을 수 있다.이 두 가지 도구는 모두 무료 소스 오픈 도구입니다. 개발과 CI 작업 흐름에서 사용해야 합니다.
심원
또한 DeepSource와 같은 소프트웨어를 사용하여 전체 감사와 심사 과정을 자동화하는 것도 고려해야 한다. 이 소프트웨어는 이러한 필터와 보안 분석기를 통해 매번 PR을 제출하고 매번 코드를 자동으로 스캔하며 대량의 문제를 자동으로 복구할 수 있다.DeepSource는 또한 대부분의 언어를 위해 자신의 분석기를 맞춤형으로 제작했는데, 이러한 분석기는 끊임없이 개선되고 최신식을 유지한다.
저는 개인적으로 DeepSource의 아주 멋진 기능을 매우 좋아합니다. 그것은 바로 그것의 설정이 매우 간단합니다!DeepSource는 저장소의 루트 디렉토리에
.deepsource.toml
파일을 추가하기만 하면 선택합니다.version = 1
[[analyzers]]
name = "ruby"
enabled = true
[[transformers]]
name = "rubocop"
enabled = true
이렇게 쉬울 줄 누가 알았겠어?토마토를 자르는 즐거움을 누리세요. 토마토를 자르는 과정에서 손가락을 다치지 않도록 조심하세요!
Reference
이 문제에 관하여(Ruby 보안 트랩 및 이를 피하는 방법), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/deepsource/ruby-security-pitfalls-and-how-to-avoid-them-1odm텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)