hiyoko-programingの日記

プログラミングを勉強したてのひよっ子。   エンジニア目指して勉強中。

Basic認証

Basic認証とは

Basic認証

Basic認証とは、HTTP通信の規格に備え付けられているユーザー認証の仕組み。
サーバーと通信が可能なユーザーとパスワードをあらかじめ設定しておき、それを知っているユーザーのみがWebアプリーションを利用できるようにすることができる。

https://tech-master.s3.amazonaws.com/uploads/curriculums//fe1298b646df1d855ec219dc6cc15ed6.png

Ruby on Railsには、Basic認証を導入するためのメソッドが用意されており、簡単に実装することができる。

 authenticate_or_request_with_http_basic

Ruby on RailsBasic認証を実装するためのメソッド。
ブロックを開き、ブロック内部でusernamepasswordを設定することでBasic認証を利用できる。

サンプル
1
2
3
4
# 'admin'というユーザー名と、'password'というパスワードでBasic認証できるように設定
authenticate_or_request_with_http_basic do |username, password|
  username == 'admin' && password == 'password'
end

Basic認証Railsアプリケーションへの導入

authenticate_or_request_with_http_basicメソッドを利用して、開発中のRailsアプリケーションにBasic認証を導入する。

RailsアプリケーションにBasic認証を導入

Basic認証によるログインの要求は、全てのコントローラで行いたい。そこで、Basic認証の処理をapplication_controller.rbにて、private以下にメソッドとして定義し、before_actionで呼び出すように実装する。

app/controllers/application_controller.rb
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class ApplicationController < ActionController::Base
  before_action :basic_auth

  private

  def basic_auth
    authenticate_or_request_with_http_basic do |username, password|
      username == 'admin' && password == '2222'
    end
  end
end

これで、どのページにアクセスしてもBasic認証が要求されるようになる。今回は、adminというユーザー名と2222というパスワードでBasic認証ができるように設定している。

Basic認証の動作を確認する

application_controller.rbの記述が完了したら、実際にアプリケーションを起動し、Basic認証の動作が期待したものになっているか確認。

'rails s'をした上で、適当なページにアクセスすると、ユーザー名とパスワードの入力を求めるポップアップウインドウが表示される。間違ったユーザー名とパスワードではBasic認証できないことを確かめるために、あえて適当な文字を入力。以下のGifでは、ユーザー名にhogehoge、パスワードに1111と入力している。

https://tech-master.s3.amazonaws.com/uploads/curriculums//e4a8d405172ee191703b043e8430b9b9.gif

正しく実装できていれば、もう一度Basic認証用のポップアップウインドウが表示される。 間違った情報ではログインできないことを確認できたので、今度は正しい情報を入力してログインできるかどうかを確かめてみる。ユーザー名にadmin、パスワードに2222と入力して、もう一度ログインを試みる。

https://tech-master.s3.amazonaws.com/uploads/curriculums//aa57e8b021d2031211cb01379bcdc523.gif

ルーティングに対応したページが表示されれば、Basic認証に成功。

Basic認証のコードを改良する

ここまでの実装で、Basic認証Railsアプリケーションに導入することができた。しかし、現状のコードには次の2つの問題点がある。

(1)ユーザー名・パスワードがコードに記述されている

Basic認証をするための、adminというユーザー名と、そのパスワード2222がコードに記述されている。もしもGithub上の公開リポジトリソースコードを管理している場合、コードを読める何者かに不正にBasic認証を突破されてしまう。対策として、コードに直接ユーザー名とパスワードを記述するのではなく、環境変数を利用する実装に切り替えることが考えられる。

(2)全ての環境でBasic認証を要求してしまう

Basic認証を使ってアクセスを制限したいのは、アプリケーションがデプロイされている本番環境でだけのはずだが、現在のコードでは、basic_authメソッドを使いたい環境を特に指定していないため、本番環境・テスト環境・開発環境の全てでBasic認証が働いてしまう。開発中にいちいちBasic認証をするのは煩わしいので、本番環境のみでBasic認証をするようにコードを書き換える。

ユーザー名・パスワードを環境変数にする

basic_authメソッド内で直接記述しているユーザー名とパスワードを環境変数に格納する。

ローカル環境の設定

ターミナル
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
$ vim ~/.bash_profile

# .bash_profileを開いたら、「i」とタイプしてインサートモードに移行

# .bash_profileの内部に次の記述を追加
export BASIC_AUTH_USER='admin'
export BASIC_AUTH_PASSWORD='2222'
# 記述を追加したら、escキーを押してインサートモードを抜け、「:wq」と入力して保存して終了する

# .bash_profileを再読み込みし、定義した環境変数を有効にする
$ source ~/.bash_profile

本番環境の設定

ターミナル
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 本番環境
$ ssh -i [ダウンロードした鍵の名前].pem ec2-user@[作成したEC2インスタンスと紐付けたElastic IP]
(ダウンロードした鍵を用いて、ec2-userとしてログイン)
$ sudo vim /etc/environment
# iを押してインサートモードに移行し、下記を追記する。既存の記述は消去しない。

BASIC_AUTH_USER='admin'
BASIC_AUTH_PASSWORD='2222'

# 編集が終わったらescapeキーを押してから:wqと入力して保存して終了
ターミナル
1
2
3
4
5
6
7
# 本番環境
# 編集した環境変数を適用するために一旦ログアウトします。
$ exit
$ ssh -i [ダウンロードした鍵の名前].pem ec2-user@[作成したEC2インスタンスと紐付けたElastic IP]
# 環境変数が適用されているか確認しましょう。
$ env | grep BASIC_AUTH_USER
$ env | grep BASIC_AUTH_PASSWORD

BASIC_AUTH_USERBASIC_AUTH_PASSWORDという名前で、それぞれユーザー名とパスワードを定義することができた。続いて、この環境変数Railsアプリケーション側で読み込むように記述変更する。

controllers/application_controller.rb
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class ApplicationController < ActionController::Base
  before_action :basic_auth
  protect_from_forgery with: :exception

  private

  def basic_auth
    authenticate_or_request_with_http_basic do |username, password|
      username == ENV["BASIC_AUTH_USER"] && password == ENV["BASIC_AUTH_PASSWORD"]
    end
  end
end

これで、環境変数を使って、Basic認証を行えるユーザー名とパスワードを定義することができた。もう一度Basic認証を試みて、環境変数として設定したユーザー名とパスワードでログインができれば、正しく実装できている。

環境変数は、環境ごとに設定する必要がある。開発環境でBasic認証をしたい場合はローカルの~/.bash_profileを、本番環境でBasic認証をしたい場合は本番サーバーにssh接続して、~/.bash_profileを編集する。

Capistranoを利用して自動デプロイを行う場合、環境変数を明示的に指定する必要がある。config/deploy.rbに追記する。

本番環境のみでBasic認証をするように実装する

まずはconfig/deploy配下のproduction.rbに追記。(すでに追加済なら作業不要)

config/deploy/production.rb
1
2
3
4
5
6
7
8
# server "db.example.com", user: "deploy", roles: %w{db}
server "(EC2のIPアドレス)", user: "ec2-user", roles: %w{app db web}

set :rails_env, "production"
set :unicorn_rack_env, "production"

# role-based syntax
# ==================

これで、unicornが現在の環境を本番環境として認識するようになる。
さらにapplication_controllerの記述を少し変更する。

app/controllers/application_controller.rb
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
class ApplicationController < ActionController::Base
  before_action :basic_auth, if: :production?
  protect_from_forgery with: :exception

  private

  def production?
    Rails.env.production?
  end

  def basic_auth
    authenticate_or_request_with_http_basic do |username, password|
      username == ENV["BASIC_AUTH_USER"] && password == ENV["BASIC_AUTH_PASSWORD"]
    end
  end
end

production?というメソッドを定義し、現在の環境が本番環境ならtrue、そうでないならfalseを返すように、Rails.env.production?と記述している。そして、before_action :basic_auth の後に、 if: :production?と記述することによって、本番環境のみでbasic_authメソッドが実行されるようになる。

Basic認証の動作確認を行う

続いて、Basic認証の動作確認をする。Basic認証を実装したブランチをマージしたら、Capistranoを用いたデプロイを行う。初めてBasic認証の動作確認をする時だけ、以下のようにデプロイをしてunicornの停止と起動も行ってから動作確認する。

開発端末上での操作
1
2
3
4
$ bundle exec cap production deploy unicorn:stop
(デプロイ完了まで待ち、完了後に以下を実行)

$ bundle exec cap production deploy unicorn:start

もしうまく動かない場合は、一度AWSのコンソールからEC2を再起動し、nginx、MySQLUnicornを手動で順番に再起動。
なおUnicornは以下のように起動する。

EC2上での操作
1
2
$ cd /var/www/アプリケーション名/current
$ bundle exec unicorn -c /var/www/アプリケーション名/current/config/unicorn.rb -E production -D

Basic認証の問題点

Basic認証は少ない手間で認証を実現できるので、便利なのだが、安全性という観点から、完全に信頼できる認証方式ではない。HTTP通信で定義されている仕様上、ユーザー名とパスワードが通信経路上にそのまま送られるため、漏洩のリスクがある。また、ログアウトの概念が定義されていないため、もし必要になる場合は自力で実装する必要があるほか、複数のサーバーを跨いだ認証が難しいといった特徴も問題になりうる。

あくまで、必要最低限の認証機能を作成中のWebアプリケーションに実装したい場合のみ、Basic認証を利用するようにする。

参考リンク

Basic認証 Wikipedia

https://ja.wikipedia.org/wiki/Basic%E8%AA%8D%E8%A8%BC

Basic認証はHTTPで定義されている認証方法。どのようなリクエスト・レスポンスを送っているのかがわかる。

Rails.env リファレンス(英語)

https://apidock.com/rails/Rails/env/class

Rails.envについて、具体的な説明はないが、コード例が載っている。