대상: 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
- 도구: 보안 헤더 진단
출처와 확인 범위
아래 1차 자료는 위의 설정·영향 범위·대응을 뒷받침하며 링크는 2026년 10월 23일 확인했습니다. 자료의 버전·구성 조건을 확인하세요. 점검 우선순위와 교훈은 이 사이트의 정리이며 글 전체의 재검증이나 안전 보증이 아닙니다.