프레임워크별
Ruby on Rails 보안 — 프로덕션 하드닝 실무 레퍼런스
Ruby on Rails 프로덕션 하드닝 실무 레퍼런스. 우선순위 체크리스트에 더해 비밀과 credentials(master key/secret_key_base)·프로덕션 설정·gem CVE·Strong Parameters·인가·인젝션·SSRF까지. 방어 중심, 공격 절차 없음.
대상: Ruby on Rails로 앱을 운영하는 사람. 여기서는 공격 절차를 다루지 않고, 프로덕션을 굳히기 위한 실무 레퍼런스(우선순위 체크리스트·영역별 대책·자기 검증)를 제시합니다. 프레임워크 전체의 관점은 프레임워크별 보안의 입구도 참조하세요.
우선순위 하드닝 체크리스트
먼저 이 표를 위에서부터 실시합니다. P0은 전제, P1이 가장 빈번한 사고원, P2는 지속적인 운영 위생입니다.
P0 ── 전제(먼저 이것)
비밀과 credentials의 안전 관리 / 프로덕션 설정(예외 비노출·force_ssl) / gem CVE 즉시 패치
P1 ── 가장 빈번한 사고원
Strong Parameters/Mass Assignment 제어 / 인가(소유자 스코프)
P2 ── 운영 위생
인젝션과 위험한 메서드 / 세션·CSRF / SSRF·업로드
| 우선순위 | 대책 | 구체(Rails) |
|---|---|---|
| P0 | 비밀과 credentials | 암호화 credentials+master key를 분리. master key를 커밋하지 않기. secret_key_base 누출은 로테이션 |
| P0 | 프로덕션 설정 | 예외 상세를 외부에 노출하지 않기·config.force_ssl·filter_parameters로 비밀을 로그에서 배제 |
| P0 | gem(의존성) CVE | bundler-audit / osv-scanner로 모니터링·실가동 버전으로 판정해 즉시 패치 |
| P1 | Strong Parameters | permit를 필요 최소한으로. permit!를 쓰지 않기. 권한 필드를 대입시키지 않기 |
| P1 | 인가를 명시 | Pundit / CanCanCan+authorize+current_user 스코프로 소유자 확인 |
| P2 | 인젝션/위험한 메서드 | where는 바인드. send/constantize에 입력을 넘기지 않기. 신뢰할 수 없는 YAML/Marshal을 읽지 않기 |
| P2 | 세션/CSRF | protect_from_forgery(기본값)·Cookie의 secure/httponly/samesite·로그인 시 재생성 |
| P2 | SSRF/업로드 | 서버 측 취득은 허용 리스트로 한정+내부 IP 차단. 업로드를 검증하고 public/ 밖에 저장 |
1. 비밀과 credentials (P0)
- Rails의 암호화 credentials를 사용하고, master key(복호화 키)를 리포지토리에 커밋하지 않습니다(환경 변수 등으로 주입).
- **
secret_key_base**는 서명/암호화 Cookie와 세션의 토대입니다. 누출되면 신속히 로테이션합니다(기존의 서명/암호화가 무효화되는 점에 주의). .env나 덤프·백업을 공개 디렉터리에 두지 않습니다(→ 공개 디렉터리에 비밀을 두지 않기).
2. 프로덕션 설정 (P0)
- 프로덕션에서는 예외의 상세를 외부에 노출하지 않습니다(
consider_all_requests_local을 프로덕션에서 활성화하지 않기 등). 내부 구조의 노출을 줄입니다. - **
config.force_ssl**로 HTTPS를 강제하고, Cookie를 secure로 표시합니다. 개발용 web console 등을 프로덕션에서 활성화하지 않습니다. - 로그에 비밀이 남지 않도록 **
filter_parameters**로 비밀번호 등을 필터링합니다.
3. 의존성(gem)의 CVE (P0)
- bundler-audit나 osv-scanner로 gem의 알려진 CVE를 기계 모니터링하고, 실가동 버전(Gemfile.lock)으로 판정해 즉시 패치합니다(→ 의존성 CVE 모니터링 · 취약점 대응 실무).
- Rails와 Ruby를 지원 대상 버전으로 유지하고, EOL 버전을 방치하지 않습니다.
4. Strong Parameters와 Mass Assignment (P1)
permit하는 항목을 필요 최소한으로 하고,permit!(전부 허용)를 피합니다.is_admin같은 권한 필드를 사용자 입력에서 대입시키지 않습니다. "번거로우니 넓게 허용"을 그만둡니다.
5. 인가 (P1 — 가장 빈번한 사고원)
자주 하는(위험한)
- 인가를 쓰지 않고 "로그인=열람/갱신 가능"
- 라우트 모델 바인딩으로 남의 ID도 취득 가능
- 숨기기만 한 라우트/추측하기 어려운 ID에 의존
- 일부 액션에서
authorize를 통과시키는 것을 잊음
올바른
- Pundit / CanCanCan으로 방침을 명시+각 액션에서
authorize - 취득도
current_user의 스코프로 소유자에 한정 - 취득·갱신·삭제의 모든 경로에서 확인
- 인가는 명시적으로 구성하고, 기본값에 맡기지 않기
용어는 IDOR란. 인증과 인가는 별개이며, 인가는 데이터에 가까운 곳에서 확인합니다.
6. 인젝션과 위험한 동적 메서드 (P2)
- SQL:
where는 플레이스홀더로 바인드하고, 문자열 결합으로 쿼리를 만들지 않습니다(where("... #{params}")를 피함)(→ SQL 인젝션이란). - 위험한 동적 메서드:
send/public_send/constantize에 사용자 입력을 넘기지 않습니다. 넘겨야 한다면 허용 리스트로 엄격히 한정합니다. - 역직렬화: 신뢰할 수 없는
YAML/Marshal의 로드는 조건에 따라 코드 실행으로 이어질 수 있으므로 피합니다.
7. 세션·Cookie·CSRF (P2)
- **CSRF 보호(
protect_from_forgery)**를 전체 비활성화하지 않습니다. 폼의 토큰을 살립니다(→ CSRF란). - Cookie/세션에 secure/httponly/samesite를 설정하고, 로그인 시 세션을 재생성합니다.
8. SSRF·업로드·헤더 (P2)
- 서버가 사용자 지정 URL을 취득하는 처리는 허용 리스트로 대상을 한정하고, 내부 IP/메타데이터로의 도달을 차단합니다(→ SSRF란).
- 업로드는 종류·크기를 검증하고,
public/밖에 저장해 실행 권한을 주지 않습니다. - 보안 헤더를 부여합니다(본 사이트는 보안 헤더 진단으로 확인).
검증: 내 Rails는 정말 굳혀졌는가
만들면 끝이 아니라, 점검해야 비로소 완료입니다. 다음은 자신의 환경에 대한 방어적 자기 점검입니다.
비밀이 노출/커밋되지 않았는가
master.key가 리포지토리에 포함되지 않았는지, .env나 덤프가 URL로 취득되지 않는지 확인.프로덕션에서 예외 상세가 나오지 않는가
인가가 작동하는가
의존성과 헤더
bundler-audit/osv가 클린인지, 헤더 진단으로 HTTPS/HSTS 등이 붙는지 확인.본 사이트의 관점: 규약에 지켜져도 '인가와 의존성'은 자신의 책임
Rails의 좋은 기본값은 많은 위험을 지워주지만, '누가·무엇을·해도 되는가'라는 인가와 의존성의 신선도만은, 앱 고유·운영 고유이므로 프레임워크가 자동으로 지킬 수 없습니다. 본 사이트가 반복해서 보는 사고도, 정교한 공격보다 "인증은 있는데 소유자 확인이 없는" 유형이 많습니다. 그래서 방어의 무게 중심은, 위의 표를 위에서부터 처리하며 Strong Parameters를 죄고, 인가를 명시하고, gem을 CVE 모니터링하는 소박한 철저함에 있습니다.
다음으로 읽기
- 입구: 프레임워크별 보안(입구) · Laravel 보안(같은 MVC·인가의 형태가 가깝다)
- 실무: 취약점 대응 실무 · 의존성 CVE 모니터링 · 공개 디렉터리에 비밀을 두지 않기
- 용어: IDOR란 · SQL 인젝션 · CSRF · SSRF
- 도구: 보안 헤더 진단
FAQ
QRails 보안, 무엇부터 해야 하나요?
우선순위 P0의 3가지입니다. (1) 비밀을 안전하게 다루기(암호화된 credentials와 master key를 분리하고, master key를 커밋하지 않기. secret_key_base는 서명/암호화 Cookie의 토대이므로 누출되면 로테이션), (2) 프로덕션 설정을 굳히기(예외 상세를 외부에 노출하지 않기·force_ssl), (3) gem(의존성)의 CVE를 기계 모니터링하고 실가동 버전으로 판정해 즉시 패치. 다음으로 Strong Parameters와 인가로 넘어갑니다.
QStrong Parameters에서 주의할 점은?
받아들일 필드를 명시적으로 허용(permit)해 Mass Assignment(예상 밖 필드의 일괄 대입)를 막습니다. 위험한 것은 번거롭다고 넓게 허용하는 것, 그리고 permit!로 전부 허용하는 것입니다. permit하는 항목을 필요 최소한으로 좁히고, is_admin 같은 권한 필드는 절대 사용자 입력에서 대입되게 하지 마세요.
Q인가는 어떻게 구현해야 안전한가요?
로그인(인증)에 더해, 대상이 정말 그 사용자의 것인지라는 소유자 확인을 구현합니다. Pundit / CanCanCan 등으로 방침을 명시하고, 각 액션에서 authorize를 통과시키며, current_user의 스코프로 리소스를 좁힙니다. 숨기기만 한 라우트나 추측하기 어려운 ID에 의존하지 말고, 취득·갱신·삭제의 각 경로에서 명시적으로 확인하는 것이 안전합니다.
Qgem(의존성)의 취약점은 어떻게 관리하나요?
bundler-audit나 osv-scanner로 알려진 CVE를 기계 모니터링하고, '실가동 버전'으로 판정해 신속히 패치합니다(Gemfile.lock의 실제 버전으로 판단). Rails 본체와 Ruby를 지원 대상 버전으로 유지하고, EOL 버전을 방치하지 마세요. 의존성의 신선도는 정교한 공격보다 사고를 가르는 현실적인 요인입니다.
Q위험한 동적 메서드란 무엇인가요?
send / public_send / constantize 등에 사용자 입력을 넘기면, 의도하지 않은 메서드 호출이나 클래스 해결로 이어질 수 있습니다. 또 Marshal이나 신뢰할 수 없는 YAML의 로드(객체 복원)는 조건에 따라 코드 실행으로 이어질 수 있습니다. 이들에 사용자 유래 데이터를 넘기지 말고, 넘겨야 한다면 허용 리스트로 엄격히 한정하세요.