보안을 고려한 인증 시스템 구축기
소개
Rails 기반으로 생산관리 시스템을 재구축하면서, 가장 먼저 해결해야 했던 과제는 "누가 어떤 데이터에 접근할 수 있는가?" 였습니다.
소규모 제조업체이지만 관리자와 직원들 사이에 각자의 업무에 필요한 기능만 접근할 수 있도록 하는 것이 중요합니다. 또한 보안 측면을 고려해 작업장 내부의 태블릿에서만 시스템에 접속할 수 있도록 제한하는 기능이 필요했습니다.
이 글에서는 Devise를 기반으로 한 기본 인증부터 디바이스 기반 접근 제어, 세밀한 페이지 권한 관리, 사용자 초대 이메일, 로그인 이력 추적까지 포함한 실전 구현 사례를 공유합니다. 가능한 빠르게 실무에 적용할 수 있도록 핵심 내용을 중심으로 설명합니다 😎
진행 방법
✅ Devise로 인증 시스템 기본 세팅
왜 Devise를 썼나?
Rails에서 가장 널리 쓰이며, 다양한 인증 시나리오를 빠르게 구현할 수 있음
기본적으로 회원가입, 로그인, 비밀번호 재설정, 세션 관리, CSRF 보호 등을 제공
Trackable모듈로 로그인 횟수와 마지막 로그인 정보까지 자동 추적 가능
설치 과정
# Gemfile
gem 'devise'
# 설치 및 User 모델 생성
rails generate devise:install
rails generate devise User
사용자 모델에 커스텀 필드 추가
사용자 이름, 관리자 여부 필드를 추가
Trackable 기능도 함께 적용해 로그인 이력 확인 가능
# db/migrate/xxx_devise_create_users.rb
create_table :users do |t|
t.string :email, null: false, default: ""
t.string :encrypted_password, null: false, default: ""
# 로그인 이력
t.integer :sign_in_count, default: 0
t.datetime :current_sign_in_at
t.datetime :last_sign_in_at
t.string :current_sign_in_ip
t.string :last_sign_in_ip
# 커스텀
t.boolean :admin, default: false
t.string :name
t.timestamps
end
첫 사용자 자동 관리자 설정
# app/controllers/users/registrations_controller.rb
if User.count.zero?
params[:user][:admin] = true
end
✅ 디바이스 Fingerprint 기반 접근 제어
배경: 외부 접속 차단을 위한 보안 조치로, 작업장 내부에서만 접속 가능한 방식이 필요했습니다. 단순한 IP 제한은 불안정하므로 브라우저 지문(Fingerprint) 방식을 도입했습니다.
브라우저 fingerprint란?
브라우저, OS, 하드웨어 정보, canvas 렌더링 결과 등을 조합해 고유 해시값(SHA-256)을 생성
이 fingerprint를 디바이스 식별자로 사용
프론트엔드 구현 (요약)
// device_fingerprint.js
form.appendChild(createHiddenField('device_fingerprint', fingerprint));
디바이스 인증 로직
로그인 시 fingerprint를 함께 전송
서버에서 해당 디바이스가 승인된 이력인지 확인
unless resource.device_authorized?(fingerprint)
sign_out(resource)
redirect_to new_device_authorization_path(...)
end
디바이스 정보는 DB에 저장되어 추후 조회 및 관리 가능
✅ 이메일 기반 승인 요청 워크플로우
승인되지 않은 디바이스로 접속 시, 이메일 전송
Rails credentials와 Gmail SMTP를 활용한 보안 설정
# credentials.yml
gmail:
email: [email protected]
password: your-app-password
문제: 개발환경에서
deliver_later가 작동하지 않음해결: 개발 시에는
deliver_now로 변경, 운영환경에서는 ActiveJob을 위한 큐 어댑터 설정
# production.rb
config.active_job.queue_adapter = :solid_queue
✅ 로그인 이력 추적 시스템
LoginHistory 모델 생성
로그인 성공/실패 여부, 디바이스 fingerprint, IP, 브라우저, OS 등 저장
관리자 전용 대시보드에서 전체 이력 확인 및 통계 제공
scope :today, -> { where('attempted_at >= ?', Time.current.beginning_of_day) }
✅ 페이지별 접근 권한 관리
PagePermission모델을 만들어 15개 페이지에 대한 권한 설정관리자 여부와 상관없이 각 페이지의 접근 여부를 제어
ApplicationController에서before_action으로 권한 체크 일괄 적용
def check_page_permission
unless PagePermission.allowed?(page_key, current_user)
redirect_to root_path
end
end
✅ UI 필터링
메뉴, 대시보드 카드 등에서
page_allowed?를 활용해 권한 없는 메뉴는 숨김 처리UX와 보안을 동시에 만족시키는 구조
🐛 트러블슈팅: 체크박스 저장 문제
문제: 체크박스를 해제하면 params가 전송되지 않아 기존 권한이 유지됨
해결: 저장 전 전체 false 초기화 → 체크된 항목만 true로 설정
PagePermission.update_all(allowed_for_users: false)
params[:permissions].each do |id, allowed|
PagePermission.find(id).update(allowed_for_users: true)
end
🐛 트러블슈팅: fingerprint 누락 버그
문제: 회원가입 시 fingerprint 전송이 누락됨
원인: JS에서 form selector가
/users경로를 감지하지 못함해결: 다양한 action 패턴을 커버하도록 selector 확장
const deviseForms = document.querySelectorAll([
'form[action*="sign_in"]',
'form[action*="sign_up"]',
'form[action="/users"]'
].join(', '))
✅ 사용자 초대 이메일
관리자가 사용자 생성 시 자동으로 초대 이메일 전송
임시 비밀번호 포함, 로그인 버튼 포함한 깔끔한 템플릿 구성
결과와 배운 점
🎯 구축 완료 기능 요약
인증 시스템
Devise 기반, Trackable, 자동 관리자 지정
디바이스 기반 접근 제어
Fingerprint 인증, 승인 워크플로우, 수동 관리
보안 감사 기능
로그인 이력 저장, 관리자 대시보드
세밀한 권한 설정
페이지별 권한 제어, UI 필터링 연계
사용자 경험 향상
필요한 기능만 보이게, UX 혼란 최소화
💡 얻은 인사이트
Fingerprint 방식은 현실적인 보안 해법
단, spoofing 가능성이 있어 2단계 인증 등 추가 보안 권장
Devise는 커스터마이징이 매우 유연함
Rails의 Convention을 따르되, 실제 HTML을 반드시 검증해야 함
폼 동작(특히 checkbox 등)은 서버 처리 로직에 항상 영향을 준다