Skip to content

Instantly share code, notes, and snippets.

@kimsuelim
Last active September 18, 2018 01:21
Show Gist options
  • Save kimsuelim/97408160f97b469c4be4ec3e615aaef6 to your computer and use it in GitHub Desktop.
Save kimsuelim/97408160f97b469c4be4ec3e615aaef6 to your computer and use it in GitHub Desktop.
Rails 앱에 audit log 활성화하기

Rails 앱에 audit log 활성화하기

서비스를 운영할 때 admin tool은 필수입니다. 운영진은 사용자의 개인정보에 접근하거나 사용자의 요청에 따른 수정, 환불과 같은 이슈를 해결해야 합니다. 개발자에게 요청하여 수동으로 처리하는 경우가 많은가요? 개발자의 시간과 집중력은 소중하기에 수동으로 처리해야 하는 일은 최소로 줄여야 합니다.

그렇다면 운영진이 중요한 정보에 접근하거나 수정하는 것을 어떻게 기록할까요? 이럴 경우 저는 쉽고 빠르게 적용 가능한 audit log를 사용합니다. audit log에 기록하는 내용:

  • 접근한 페이지의 controller와 params
  • 접근한 사용자(운영진)의 이름, 이메일, 아이디
  • 접근한 디바이스의 IP 주소, useragent

Rails는 controller에서 before_action을 이용하면 쉽게 구현할 수 있습니다. 아래는 제가 실서버 환경에서 구현한 코드 일부입니다.

audit log 구현

# Gemfile
gem 'geoip'
# app/controllers/concerns/audit_log.rb
module AuditLog
  extend ActiveSupport::Concern

  included do
    before_action :audit_log
  end

  def audit_log
    logs = {
      controller: params[:controller],
      action: params[:action],
      params: filtered_params,
      user_id: current_user&.id,
      user_email: current_user&.email,
      user_name: current_user&.name,
      ip_address: request.remote_ip,
      geo_ip: geoip.city(request.remote_ip).to_h,
      user_agent: request.user_agent
    }

    logger.tagged('audit_log') { logger.info logs }
  end

  private

  def geoip
    GeoIP.new(File.join(Rails.root, '/lib/GeoLiteCity.dat'))
  end

  def filtered_params
    params.except(:controller, :action, :utf8, :authenticity_token, :commit)
  end
end
# app/controllers/admin_controller.rb
class AdminController < ActionController::Base
  include AuditLog
end

admin 페이지에서의 로그인, 수정 등 모든 요청은 log 파일에 아래와 같이 빠짐없이 기록됩니다.

[audit_log] {"controller":"users/sessions","action":"create",
"params":{"user":{"email":"kimsuelim@gmail.com","password":"hvgE9WBV7UnNLmSDBKQi"}},
"user_id":"1","user_email":"kimsuelim@gmail.com","user_name":"lim",
"ip_address":"127.0.0.1", "geo_ip":{"request":"127.0.0.1","ip":"127.0.0.1","country_code2":"KR","country_code3":"KOR","country_name":"Korea, Republic of","continent_code":"AS","region_name":"11","city_name":"Seoul","postal_code":"","latitude":37.5985,"longitude":126.97829999999999,"dma_code":null,"area_code":null,"timezone":"Asia/Seoul","real_region_name":"Seoul-t'ukpyolsi"},
"user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.113 Safari/537.36"}

한가지 문제점

위 로그를 자세히 보면 운영진의 비밀번호가 기록됩니다.

"password":"hvgE9WBV7UnNLmSDBKQi"

운영진의 로그인 비밀번호까지 기록해야 할까요? 친절하게도 Rails는 이미 로그 파일에서 민감한 정보를 필터링하고 있습니다. config/initializers/filter_parameter_logging.rb에서 필터링이 필요한 parameter를 설정할 수 있습니다.

# Configure sensitive parameters which will be filtered from the log file.
Rails.application.config.filter_parameters += [:password]

그러나 custom logging에는 설정이 적용되지 않아 로그인 시, 비밀번호가 로그에 기록되는 문제가 있습니다. 이럴 경우 Rails에서 제공하는 ActionDispatch::Http::ParameterFilter API를 이용하여 수동으로 parameter를 필터링해주면 됩니다.

module AuditLog
  ...

  private

  def filtered_params
    rails_params = params.except(:controller, :action, :utf8, :authenticity_token, :commit).to_unsafe_h
    parameter_filter = ActionDispatch::Http::ParameterFilter.new(Rails.application.config.filter_parameters)
    parameter_filter.filter(rails_params)
  end
end

수정한 코드를 적용하면 password 파라미터만 필터된 후에[FILTERED] 기록됩니다. cheers! 🍻

[audit_log] {"controller":"users/sessions","action":"create",
"params":{"user":{"email":"kimsuelim@gmail.com","password":"[FILTERED]"}},
...

추가

audit log에 기록만 한다고 되는 것은 아닙니다. 기록된 로그를 어떻게 저장하고 분석하는 문제가 남아있습니다. 이는 또 다른 topic이기에 여기에서 자세히 다루기는 어렵습니다. 직접 ELK(Elasticsearch + Logstash + Kibana) 환경을 setup 하거나 Papertrail와 같은 SaaS를 이용할 수 있습니다. 개인적으로는 실서버 환경에서 Papertrail 를 사용하고 있으며 아주 만족하고 있습니다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment