[Ruby on Rails]보안 기초

Jinsu Kim·2021년 3월 4일
0

rails

목록 보기
2/7
post-thumbnail

보안에 기초가 되는 점을 Rails관점에서 공부하여보았습니다.
번역하고 공부한 사이트는 아래의 사이트 입니다(일본어)

레일즈 가이드

1.XSS

2. CSRF : 사이트 간 요청 위조 (Cross-site request forgery)

✏️ CSRF 개요

✏️ 이 공격방법은 유저에 따라 인증을 종료했다고 생각되어져 Web 어플리케이션의 페이지에 악의가 있는 코드나 링크를 속에 넣는 것을 말한다.

이 Web어플리케이션에 섹션이 타임아웃해 있지 않으면 공격자는 본래인증되어져있지 않을 것이다라는 Command가 실행가능해져버립니다.

Rails의 특이한점은 많은 Rails 어플리케이션이 cookie베이스의 섹션을 사용하고 있는 것이다. 이 때 센션 ID를 cookie에 보존하여 서버쪽에 섹션해쉬를 갖게 하는 방법 또는 모든 섹션 해쉬를 클라이언트(브라우저)쪽에 갖도록 합니다. 어느쪽이라도 브라우저는 리퀘스트를 보낼 때마다 cookie를 자동적으로 도메인에 송신한다(도메인에 이용가능한 cookie가 있을 경우). 여기서 문제가 되는 것은 다른 도메인에 소속해있는 사이트에서 리퀘스트가 있던 경우에도 브라우저가 cookie를 송신해버리는 점입니다.

아래에 예시로 확인하여 보자

  1. Bob는 게시판을 브라우저에서 바라보고 있었던 이라고 하는 해커에 의한
    글을 보게 된다. 그 글에는 속임수가 있는 HTML image요소가 포함되어 있다. 그 요소가 실제로 참조하고 있는 것은 영상파일이 아닌 Bob의 프로젝트 관리 어플리케이션을 목표로 한 코맨드 <img src="http://www.webapp.com/project/1/destroy">이다.

  2. Bob는 몇분동안 로그아웃하고 있지 않기 때문에 www.webapp.com에 대하여 Bob 의 섹션은 아직 기한 만료로 되어 있지 않다.

  3. 해커에 의한 글이 브라우저에 표시되어지면 브라우저는 image태그를 발견한다. 그렇게 브라우저는 www.webapp.com에서 의심스러운 영상을 읽기시작하려고 한다. 이전에 설명했던 것처럼 이 때의 유현한 섹션 id를 포함한 cookie도 함께 송신 되어진다.

  4. www.webapp.com의 Web 어플리케이션은 리퀘스트에 대응하여 섹션 해쉬에 포함되어있는 유저 정보가 유효가 되었다는 것을 인정하고 지시에 따라서 ID 1의 프로젝트를 삭제한다. 브라우저는 결과페이지를 표시하여 무언가의 문제가 일어났다는 것을 표시한다. 영상은 표시되어지지 않는다.

  5. Bob는 공격에 눈치채지 못하고 있습니다. 그러나 몇일 후에는 프로젝트 No.1가 삭제되어졌다는 것을 눈치 챕니다.

여기서 중요한 것은 속임수가 있는 영상이나 링크를 두는 장소는 Web 어플리케이션의 도메인에 한정되어 있지 않다는 것입니다. 포럼, 블보그, email 등 어디에도 놀 수 있다.

CSRF는 CVE(Common Vulnerabilities and Exposures)에 보고 되어진 것은 거의 없지만(2006년에도 0.1% 이하) 그래도 Grossman가 말하는 「잘자고 있는 거인」이고 위험한 것에는 틀림없다. 저자는 다른 보안 전문가에 의해 보안 간련의 실적에 등록하고 있는것은 거의 없지만 CSRF는 매우 중요한 보안 문제인 것임을 틀림없다고 강하게 인식하고 있다고 말하고 싶습니다.

🎇 CSRF에의 대응책

  1. 첫번째로는 W3C가 요구하고 있는 것 처럼 GET과 POST를 적절히 사용합시다!
  2. 두번째로는 GET이외의 리퀘스트에 보안토큰을 추가하는 것으로 Web 어플리케이션을 CSRF에을 막는 것이 가능합니다.

HTTP 프로토콜은 2개의 기본적인 리퀘스트인 GET과 POST(DELETE, PUT, PATCH는 POST와 똑같이 사용하여야 한다)를 제공하고 있습니다. World Wide Web Consortium(W3C)는 HTTP의 GET이나 POST를 선택할 때에 체크리스트를 제공하고 있습니다.

이하의 경우 GET을 사용하자

  • 주고받음이 기본적인 조회(문의)인 경우(쿼리, 데이터를 읽는 동작, 검색과 같은 안전한 동작)

이하의 경우는 POST를 사용하자

  • 주고 받음이 기본적으로 명령인 경우
  • 주고 받음에 의해 유저에게 이해할 수 있는 형태의 리소스의 상태가 변하는 경우(서비스에 신청) 등
  • 주고 받음에 의해 일어나는 결과를 유저가 파학 가능한 경우

Web 어플리케이션이 RESTful이라면 PATCH, PUT, DELETE등의 HTTP메소드가 사요오디어지고 있을 것입니다. 그러나 일부의 브라우저는 이런 메소드를 서포트하고 있지 않습니다. 확실히 서프트 되어지고 있는 것은 GET과 POST만이다. Rails에서는 _method라고 하는 숨겨져있는 필드를 사용하여 이런 HTTP 메소드를 서포트 하고 있습니다.

Post 리퀘스트도 자동적으로 전송되어지는 경우가 있을 수 도 있습니다. 브라우저는 스테이터스 Bar에 www.harmless.com이라고 하는 Web 사이트에 링크가 표시되어저 있다고 합시다. 이 링크에 속임수가 있고 POST 리퀘스트를 몰래 송신하는 새로운 폼을 동적으로 작성하도록 되어 있다고 합시다.

<a href="http://www.harmless.com/" onclick="
  var f = document.createElement('form');
  f.style.display = 'none';
  this.parentNode.appendChild(f);
  f.method = 'POST';
  f.action = 'http://www.example.com/account/destroy';
  f.submit();
  return false;">To the harmless survey</a>

또는 공격자가 이 코드를 영상에 onmouseover 이벤트 핸들러에 넣어났다고 생각해봅시다.

<img src="http://www.harmless.com/img" width="400" height="400" onmouseover="..." />

script태그를 사용하여 JSON이나 JavaScript의 응답을 따라 특정의 URL에 크로스사이드 리퀘스트를 작성하는 등 공격방법을 다종다양하다. 이 리스폰스(응답)은 공격자가 실행방법을 찾아낸 코드이고 기밀 데이터를 추출해낼수 있는 가능성이 있습니다. 이런 데이터 유출을 방지하기 위해서는 스로스 사이트의 <script> 태그를 무효로 합니다. 단지 Ajax 리퀘스트는 브라우저의 동일-출처 정책에 따라서 동작하기 때문에(XmlHttpRequest를 개시 가능한 것은 자기의 자기의 사이트 뿐이다)
JavaScript 응답을 반환하는 것을 안전하게 허가 하는 것이 가능합니다.

Note: <script> 태그의 origin이 같은 사이트가 악의가 있는 사이트인지는 구별 할 방법이 없습니다. 이 때문에 <script> 태그는, 예를들어 실제로는 자신의 사이트에서 동일한 origin 스크립트라고 해도 전면적으로 차단해야합니다. 이런 경우 <script>를 대상으로 JavaScript를 사용하는 작업에 대해서는 명시 적으로 CSRF 보호를 건너 뛰십시오.

이런 다양한 종류의 리퀘스트를 모두 방지하기 위해서는 필수 보안트큰을 보입합니다. 이 토큰은 자신의 사이트 만이 알고 있어 다른 사이트는 모릅니다. 리퀘스트에는 이런 보안 토큰을 포함하고, 서버 쪽에 이것을 검증합니다. 이하의 1줄 코드는 어플리케이션의 콘트롤러에 추가하는 것이고 Rails에 신규작성한 어플리케이션에는 이 코드가 디폴트로 포함되어 있습니다.

protect_from_forgery with: :exception

이 코드가 있으면 Rails에서 생성된 모든 폼과 Ajax 리퀘스트에 보안 토큰이 자동적으로 포함되어 집니다. 보안토큰이 매치 하지 않은 경우네느 예외가 던져 집니다.

Rails에 Defalt로 포함된 unobtrusive scripting adapter이 추가 하는 X-CSRF-Token이라는 헤더에는 GET 이외의 모든 Ajax 호출에서 보안 토큰이 포함됩니다. 이 헤더가 없으면 Rails는 GET 이외의 Ajax 요청을 받지 못하게 됩니다. 아약스 호출에 다른 라이브러리를 사용하는 경우 해당 라이브러리의 아약스 호출의 디폴트의 헤더에 보안토큰을 추가할 필요가 있습니다. 이 토큰을 얻으려면 어플리케이션의 뷰에 <%= csrf_meta_tags %>에서 출력되는 태그를 봐주세요.

일정한 cookie에 유저 정보를 보존하는 것은 자주 있는 일입니다. 이런 경우 cookie는 소거 되지 않는다는 것 이전의 보호기관의 밖에는 CSRF에서의 보호를 받지 못한 다는 것에 주의하여 주세요. 무언가에 이유로 이런 정보를 섹션이외의 cookie 스토어에 보관(보존)하고 있는 경우에는 필요한 조작을 개발자 자신이 행하지 않으면 안됩니다.

rescue_from ActionController::InvalidAuthenticityToken do |exception|
  sign_out_user # 유저의 cookie를 삭제하는 메소드의 예
end

위의 메소드는 ApplicationController에 두는 것이 가능합니다. 그렇게 비 GEET 리퀘스트에는 CSRF 토큰이 없는 경우나 토큰이 무효한 경우에 이 메소드가 불려집니다.

신경써야할 점은 크로스 사이드 스크립팅(XSS) 취약점은 모든 CSRF보호를 우회해버리고 만다는 점입니다. XSS 취약성이 존재하면 공격자는 Web 페이지의 모든 요소에 접근할 수 있게 되어집니다. 때문에 폼에서 CSRF 보안 토큰을 읽어들여서 폼을 직접전송하는것이 가능해져버리고 맙니다.
뒤에 나오는 글에는 XSS 상세도 읽어 봐 주세요

4 리다이렉트와 파일(Open redirect)

웹 애플리케이션의 또 다른 보안 취약성은 리다이렉트 및 파일과 관련이 있습니다.

4.1 리다이렉트

웹 애플리케이션에서의 리다이렉트는 과소평가되기 쉬운 크래킹 툴입니다.공격자가 이것을 사용하면, 사용자를 위험한 Web 사이트로 유도할 수도, Web 사이트 자체에 함정을 둘 수도 있게 됩니다.

리다이렉트용의 URL(의 일부)을 유저가 입력할 수 있도록 하면, 잠재적인 취약성이 됩니다.
가장 노골적인 공격 방법으로는
사용자를 진짜와 꼭 닮은 가짜 웹 사이트로 리다이렉트하는 것
을 생각할 수 있습니다.

이것은 흔히 '피싱(phishing)'이나 '낚시' 등으로 불리는 공격 기법입니다.

구체적 예시로는 무해를 가장한 링크를 포함한 메일을 사용자에게 보내고, 그 링크를 XSS로 웹 애플리케이션에 주입하거나 링크를 외부 사이트에 배치합니다.이 링크의 첫 부분은 해당 웹 애플리케이션의 URL이기 때문에 언뜻 보기에 무해해 보입니다.
위험한 사이트로 이끄는 URL은
http://www.example.com/site/redirect?to=www.attacker.com
과 같이 리다이렉트 파라미터에 숨겨져 있습니다.여기서는legacy액션을 예시합니다.

def legacy
  redirect_to (params.update (action: 'main'))
end

이 코드는 legacy 액션에 대한 액세스가 있으면 사용자를 메인 액션으로 리디렉션합니다.
이 코드의 원래 의도는 기존 액션에 대한 URL 파라미터를 보호하고 그것을 메인 액션에 전달하는 것입니다.

그러나, 이 URL에 이하와 같은 호스트 키가 포함되어 있으면, 공격자에게 악용될 가능성이 있습니다.

http://www.example.com/site/legacy?
param1=xy&param2=23&host=www.attacker.com

URL 말미에 있는 호스트 키는 알아채기 어렵고 사용자는 attacker.com 호스트로 리다이렉트되어 버립니다.일반적으로 사용자 입력을 그대로 redirect_to 메서드에 전달하는 것은 위험하다고 생각됩니다.

심플한 대응책으로는 이 legacy 액션에는 지정된 파라미터만을 포함시키는 방법이 있습니다(이것은 허가 리스트적 접근법으로 상정되지 않은 파라미터를 제외하는 방법과는 정반대입니다).

URL을 리다이렉트하는 경우는 허가 리스트 또는 정규 표현으로 체크해 주세요.

4.1.1 자기완결형 XSS

Firefox나 Opera에서는, 데이터 프로토콜을 사용해 다른 타입의 리다이렉션이나 자기 완결형 XSS 공격을 실행할 수 있습니다.

데이터 프로토콜은, 그 내용을 브라우저에 직접 표시할 수 있어 HTML이나 Java Script나 화상 전체 등 무엇이든 포함할 수 있습니다.

Data: text/html;base 64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K

위의 예에서는 Base64로 인코딩된 자바스크립트를 사용하고 있습니다. 이 자바스크립트는 단순히 메시지 상자를 표시합니다.

리다이렉션 URL 공격에서는 공격자가 이러한 악의적인 코드를 포함하는 URL로 리다이렉트시킵니다.

이 공격에 대한 대응책은 리다이렉트할 URL(의 일부)을 사용자가 입력할 수 없도록 하는 것 입니다.

4.2 파일 업로드

파일이 업로드되었을 때 중요한 것

  • 파일이 덮어쓰이지 않도록 하는 것
  • 미디어 파일은 비동기로 처리할 것

많은 웹 애플리케이션에서는 사용자의 파일 업로드를 허용하고 있습니다.

사용자가 선택하고 입력할 수 있는 파일명(또는 그 일부)은 반드시 필터해 주세요.

공격자가 위험한 파일 이름을 일부러 사용하여 서버의 파일을 덮어쓰려고 할 가능성이 있기 때문입니다.

파일이 /var/www/uploads 디렉토리에 업로드 되고, 그 때 파일명이 "../../../etc/passwd"라고 입력되어 있으면 중요한 파일이 덮어쓰여질 수 있습니다.

말할 필요도 없이, Ruby 인터프리터에 그만큼의 실행 권한이 주어지지 않으면 그러한 덮어쓰기는 실행할 수 없습니다. 웹 서버, 데이터베이스 서버 등의 프로그램은 비교적 권한이 작은 유닉스 사용자로 실행되는 것이 보통입니다.

또 한 가지 주의가 있습니다.

사용자가 입력한 파일명을 필터할 때에는 파일명으로부터 위험한 부분을 제거하는 「금지 리스트」적 접근 방식을 사용하면 안 됩니다.

Web 어플리케이션이 파일명으로부터 「../」라고 하는 문자를 제거하는 데 성공해도, 이번에는 공격자가 「....//」와 같은 그 이면을 쓰는 패턴을 사용하면 「../」라고 하는 상대 패스가 지나가 버리는 식으로, 금지 리스트적인 수법에서는 아무래도 누락이 남아 버립니다.

가장 좋은 방법은 '허가 목록'에 의한 접근법입니다. 이것은 파일명이 유효한지 여부(지정된 문자만 사용되고 있는지 여부)를 체크하는 것입니다(이것은 이용이 허용되지 않는 문자를 제거하는 「금지 리스트」와 반대의 접근법입니다).

파일 이름이 비활성화된 경우는 거부하거나 비활성 문자를 (삭제가 아닌) 대체하는 것 중 하나로 합니다. 아래의 예는 attachment_fu 플러그인에서 발췌한 파일명 써니타이저입니다.

def sanitize_filename(filename)
  filename.strip.tap do|name|
    # 메모: File.basename은 Unix상에서의 Windows 경로에 대해서는 정상적으로 작동하지 않습니다.
    # 전체 경로가 아닌 파일 이름만 가져옵니다.
    name.sub!/\A.*(\|\/)/, '
    
    # 최종적으로 비영수 문자를 언더스코어 또는
    # 마침표와 언더 스코어로 대체
    name.gsub!/[^\w\.\-]/, '_'
  end
end

(attachment_fu플러그인이 화상에 대해 수행하도록) 파일 업로드를 동기적으로 처리하는 경우의 중대한 문제점은 서비스 거부(DoS) 공격의 취약성이 생긴다는 것입니다.

공격자는 동기적으로 수행되는 화상 파일 업로드를 다수의 컴퓨터에서 동시에 실행함으로써 서버에 고부하를 가하고 최종적으로 서버를 충돌시키거나 동작 불능으로 만들 수 있습니다.

이를 해결하려면 미디어 파일을 비동기적으로 처리하는 것이 최선입니다.

미디어 파일을 저장한 후 데이터베이스 내에서 처리 요청을 스케줄링합니다. 파일 처리는 다른 프로세스가 백그라운드에서 수행합니다.

4.3 파일 업로드로 실행 가능한 코드 보내기

업로드된 파일에 포함된 소스 코드가 특정 디렉토리에 놓이면 소스 코드가 실행 가능하게 되어 버릴 가능성이 있습니다.
Rails의 /public 디렉토리가 Apache의 홈 디렉토리로 되어 있는 경우 여기에 업로드 파일을 두지 마십시오.

널리 사용되는 Apache Web 서버에는 DocumentRoot라는 옵션이 있습니다. 이것은 웹 사이트의 홈 디렉토리이며, 이 디렉토리 트리에 놓여 있는 것은 모두 웹 서버에 의해 전달됩니다. 거기에 놓여 있는 파일의 이름에 특정 확장자가 주어져 있으면, 그것에 대해 요청이 송신되었을 때에 실행되어 버리는 일이 있습니다(어떤 옵션을 줄 필요가 있을지도 모릅니다).

실행될 가능성이 있는 확장자는 예를 들어 PHP나 CGI 등입니다. 공격자가 "file.cgi"라는 파일을 업로드하고 그 안에 위험한 코드가 설치되어 있다고 칩시다. 이 파일을 누군가가 다운로드하면 이 코드가 실행됩니다.

Apache의 DocumentRoot가 Rails의 /public 디렉토리를 가리키는 경우 업로드 파일을 여기에 두면 안됩니다. 적어도 한 단계 위에 저장해야 합니다.

4.4파일 다운로드

사용자에게 임의 파일의 다운로드를 허용하지 않을 것.

파일 업로드 시 파일명 필터가 필요한 것과 마찬가지로 파일 다운로드 시에도 파일명을 필터해야 합니다.

아래의 send_file() 메서드는 서버에서 클라이언트로 파일을 전송합니다.파일 이름이 필터 처리되지 않으면 사용자가 원하는 파일을 다운로드할 수 있게 되어 버립니다.

send_file ('/var/www/uploads/'+params[:filename])

위의 코드에서는 예를 들어 「../../../etc/passwd」와 같은 파일명을 건네는 것만으로 서버의 로그인 정보를 다운로드할 수 있습니다. 이에 대한 간단한 대책은 요청된 파일명이 상정되어 있는 디렉토리 아래에 있는지 여부를 체크하는 것입니다.

basename= File.expand_path('../../files',_dir__)
filename= File.expand_path(File.join(basename,@file.public_filename))
raise if basename!=
  File.expand_path(File.join(File.dirname(filename), '../../'))
send_file filename, disposition : 'inline'

다른 방법은 파일명을 데이터베이스에 저장해 두고 데이터베이스의 id를 서버의 디스크상에 두는 실제 파일명 대신 사용하는 것입니다
(이는 위의 방법과 병용 가능합니다).
이 방법도 업로드 파일이 실행될 가능성을 회피하는 방법으로 좋습니다.
attachment_fu 플러그인에서도 같은 방법이 채용되고 있습니다.

5 인트트라라넷과 admin의 보안

6 유저 관리

7 인젝션

8 안전하지 않않은 쿼리 작성

9 디폴트 헤더

5, command injection

6, Directory Traversal

7. authentication과 authorization 의 차이

authentication과 authorization 의 차이를 명확히 이해하는 게 좋습니다.

그외 기초적인 지식 +

서버 접근권한 제한
로그인 비밀번호 암호화 방식

profile
Ruby와 js로 첫 커리어를 시작하였고 3년차 엔진니어입니다! vim에 관심이 많습니다!

0개의 댓글