Computer >> 컴퓨터 >  >> 프로그래밍 >> Ruby

환경 변수 보안 완벽 가이드 — API 키와 시크릿을 안전하게 지키는 실전 전략

지난 글 '루비스트를 위한 환경 변수 가이드'에서는 환경 변수 시스템이 어떻게 작동하는지 설명하고, 몇 가지 흔한 오해도 바로잡아 드렸습니다. 하지만 한 독자께서 지적해 주셨듯이, 보안에 대해서는 깊이 다루지 못했죠.

요즘은 비밀 API 키나 기타 중요한 정보를 환경 변수에 저장하는 것이 일반화되었기 때문에, 그 보안상 의미를 제대로 이해하는 것이 무엇보다 중요합니다. 지금부터 하나씩 살펴보겠습니다.

최악의 시나리오

해커가 서버에 루트(root) 권한으로, 혹은 웹 애플리케이션을 소유한 사용자 계정으로 침입했다고 상상해 보세요. 그 순간 환경 변수는 물론, 강력하게 암호화되지 않은 모든 것이 손에 넘어갑니다.

이 문제의 해결책은 의외로 단순합니다. 아무도 서버의 루트 권한을 얻지 못하도록 하는 것입니다. :)

트로이 목마

부두 골목에서 만난 어떤 사람이 특별히 성능을 끌어올린 버전의 ImageMagick을 건네며 "기본 제공 버전보다 두 배 빠르게 실행된다"고 주장한다고 가정해 봅시다.

...그러면 누구나 그렇듯 그대로 운영 환경에 설치하게 됩니다. 문제는, 그 프로그램이 환경 변수를 훔치도록 설계되어 있다는 점입니다.

아래 예시에서는 백틱(`) 문법을 사용해 Ruby에서 '악성' mogrify 명령어를 실행하고 있습니다. mogrify는 환경 변수를 훔친 뒤 도발적인 메시지를 출력합니다.

환경 변수 보안 완벽 가이드 — API 키와 시크릿을 안전하게 지키는 실전 전략 환경 변수는 부모 프로세스(irb)에서 자식 프로세스(mogrify)로 복사됩니다.

사실 이것은 진짜 '해킹'이 아닙니다. 그저 환경 변수가 작동하는 방식일 뿐입니다. 환경 변수는 부모 프로세스에서 자식 프로세스로 복사되며, 우연히 악성 코드를 실행하게 되더라도 그 코드는 다른 프로그램과 마찬가지로 환경 변수를 상속받습니다.

이런 일이 일어나는 경로는 크게 두 가지입니다.

  • 누군가 여러분의 툴체인에 악성 소프트웨어를 설치하도록 유도하는 경우

  • 툴체인의 '정상적인' 구성 요소에 취약점이 존재하고, 이를 악용당해 환경 변수가 노출되는 경우

근본적인 해결책은 이런 일 자체를 사전에 차단하는 것입니다. 하지만 만에 하나 환경 변수를 훔치는 악성 프로그램이 툴체인에 들어오더라도 피해를 최소화할 수 있는 예방 조치도 준비되어 있습니다.

환경 변수 정리하기(Sanitizing)

Ruby에서 ImageMagick이나 다른 외부 프로그램을 실행하기 위해 셸을 호출할 때마다, 그 프로그램은 앱의 ENV 복사본을 상속받습니다. ENV에 비밀 정보가 있다면 그것까지 함께 넘어갑니다. 미리 걷어내지 않는 한 말이죠.

새 프로세스를 생성할 때 system 메서드에 해시를 전달하면 특정 환경 변수를 재정의할 수 있습니다.

환경 변수 보안 완벽 가이드 — API 키와 시크릿을 안전하게 지키는 실전 전략 Ruby의 system 메서드에 커스텀 환경 변수를 전달하는 방법

셸 명령을 실행할 때 환경 변수를 아예 전달하지 않으려면 아래 예시처럼 처리할 수 있습니다. ENV를 그대로 복제하되 모든 값을 nil로 설정한 해시를 만드는 방식입니다. 우아하지는 않지만, 셸 명령 실행 시 모든 환경 변수를 제거할 수 있는 거의 유일한 방법입니다.

환경 변수 보안 완벽 가이드 — API 키와 시크릿을 안전하게 지키는 실전 전략 Ruby에서 셸 명령을 실행할 때 환경 변수를 전달하지 않는 방법

아기를 욕조물과 함께 버리지 마세요

상속은 환경 변수의 가장 유용한 기능 중 하나입니다.

S3에 데이터를 기록하는 명령어를 실행해야 한다고 가정해 보세요. 환경 변수 상속 덕분에 그 명령어는 Ruby 앱의 API 키를 매끄럽게 공유할 수 있습니다. 상속을 무조건 차단해버리면 이런 편의성을 잃게 됩니다.

지속성(Persistence)

모든 프로세스는 자신만의 환경 변수 집합을 가지며, 프로세스가 종료되면 함께 사라집니다. Ruby 앱에서 환경 변수를 설정할 수 있지만, 앱이 종료되는 순간 사라집니다.

재부팅 후에도 환경 변수가 유지되길 원한다면 어딘가에 저장해 두어야 합니다. 그 '어딘가'는 보통 파일 시스템입니다.

앱의 git 저장소에는 절대 넣지 마세요

GitHub의 사람들도 우리만큼 정직하다고 믿습니다. 하지만 소스 코드에 접근할 수 있는 모든 사람이 여러분의 API 키를 얻어 10,000달러짜리 AWS 청구서를 만들어내기를 바라시나요?

.bashrc나 .bash_profile에 비밀 정보를 저장하지 마세요

.bashrc 같은 파일에 비밀 정보를 저장하면, 해당 사용자로 실행되는 모든 프로그램에 환경 변수 형태로 전달됩니다. 대부분의 프로그램은 여러분의 비밀을 알 필요가 없습니다. 굳이 넘겨줄 이유가 없는 것이죠.

필요한 프로세스에게만 비밀을 공개하세요

Rails 앱이 HONEYBADGER_API_KEY를 알아야 하는 유일한 프로세스라면, 오직 그 프로세스에서만 사용할 수 있도록 만드는 것이 좋습니다.

Rails 시작 시점에 로드되는 파일에 환경 변수를 추가할 수 있는 젬(gem)이 여럿 있습니다. 이렇게 설정된 환경 변수는 Rails 프로세스와 그 자식 프로세스에서만 접근할 수 있습니다. figaro와 dotenv 젬에 대한 자세한 내용은 '루비스트를 위한 환경 변수 가이드' 하단 섹션에서 확인할 수 있습니다.

설정 파일을 안전하게 보호하세요

환경 변수를 설정 파일에서 불러온다면, 웹 앱을 실행하는 사용자만 읽을 수 있도록 파일 권한을 반드시 설정해야 합니다.

아래 예시에서는 설정 파일을 생성한 사용자(이 경우 Rails 애플리케이션을 소유한 동일 사용자)만 읽고 쓸 수 있도록 권한을 변경했습니다.

환경 변수 보안 완벽 가이드 — API 키와 시크릿을 안전하게 지키는 실전 전략 설정 파일을 읽어야 하는 사용자만 읽을 수 있도록 권한을 설정하세요.

GitHub에 커밋하지 않는 이상, SSH로 서버에 접속해 직접 수정하거나 Chef 같은 구성 관리 도구를 활용해야 합니다.

가능한 곳에 '방화벽'을 구축하세요

AWS의 훌륭한 점 중 하나는 매우 세분화된(granular) 접근 제어 정책을 만들 수 있다는 것입니다. 이와 유사한 기능을 제공하는 서비스 업체가 많습니다.

이게 무슨 의미일까요? 하나의 API 키가 S3의 특정 버킷 하나에 업로드하는 작업만 허용하도록 만들 수 있다는 뜻입니다. 활동별로 별도의 키페어(keypair)를 발급하세요. 이미지 업로드용 키 하나, 데이터베이스 백업용 키 하나, 이런 식으로 나누는 겁니다.

이렇게 운영하면 보안 사고가 발생하더라도 피해 범위를 최소한으로 제한할 수 있습니다.