0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Rails上のAPIセキュリティについてメモ【初学者による調査】

Last updated at Posted at 2023-03-24

導入

人生で初めてAPI設計におけるセキュリティについて考えましたので、最終的な実装内容にも簡単に触れながら考えを整理します。

各種環境について

サーバー・クライアント関係はAPI側がRails、クライアント側がReact(axiosによる送信)となる。
Rails側のエンドポイント設計はgrapeというgemによるもの。

サーバ: localhost:3000
クライアント: localhost:3001

マシンはMacBook Pro (13インチ, M1, 2020)を使用。

Railsプロジェクト
% ruby -v
ruby 3.2.1
% rails -v
Rails 7.0.4.2

grapeのバージョンは1.7.0。

React側は react@18.2.0axios@1.3.4

実装手順と説明

protect_from_forgeryはデフォルトのまま

app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception
  ...

rails newの時から触っていません。

これを「何かしらエラーが起きてしまう」からと protect_from_forgery with: :null_session にしてしまうと、アプリケーション全体でcsrf-tokenがなくともリクエストを通すようになります(で、認識が合っているかと…)。

検証そのものを行わなくするには config.action_controller.allow_forgery_protection = false というように設定するそうです。

forgery_protection_origin_checkの設定

config/application.rb
module App
  class Application < Rails::Application
    # ~ 中略 ~
    config.action_controller.forgery_protection_origin_check = false
  end
end

CSRFの追加対策として、HTTPのOriginヘッダーがサイトのoriginと一致することをチェックすべきかどうかを設定します。

今回はrack-corsでオリジン関係の設定を行うため、Rails側の設定を false にします。

rack-corsの導入

Gemfile
gem 'rack-cors'

インストールを行い、設定を書きます。

config/initializers/cors.rb
Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins 'localhost:3001'
    resource '*',
              headers: :any,
              methods: [:get, :post],
              expose: ['X-CSRF-Token']
  end
end

今回はトークンをヘッダーに仕込みます。

トークンの受け渡しについて

サーバ上のユーザ認証、トークン生成時

クライアント側の情報を元にユーザ認証が成功したとします。
grapeのドキュメントにもある方法で、レスポンスヘッダーにトークンを乗せます。

grapeによるAPI設計時に記述
post :endpoint_name do
  # 処理が成功
  header 'X-CSRF-Token', token
  # body内容の記述
end

クライアント側によるトークンの受け取り

参考サイトを参考に、トークンをaxios上の設定として保持します。

axiosによるリクエスト成功時の処理
.then(response => {
  axios.defaults.headers.common['X-CSRF-Token'] = response.headers['x-csrf-token']

以後、リクエスト発行時にヘッダーを毎回設定します。

サーバ上のリクエスト処理

リクエストを受け取る際に、Rails上での参照は下記で成功しました。

grapeによるAPI設計時に記述
env['X-CSRF-Token']

ちなみに今回はトークン参照後、サーバ側の自作メソッドでトークンを検証するようにしています。

セキュリティ上の要件を検証

セルフチェック

こちらの記事で、CORS設定を行う上でのCSRF対策要件として掲げている、

  • 重要な操作を行う場合は X-CSRFToken のようなヘッダを付けてリクエスト送信を行うような仕様にする

  • このヘッダのチェックはサーバー側でも正しく行う

  • サーバーでは正しく Access-Control-Allow-Origin ヘッダを出力する

はオールクリアしているはずかと思います。

暗号化に対する認識

手元に有用な参考文献がなくて恥ずかしいのですが、HTTPヘッダーはTLS通信時に暗号化される、という認識で合っていると思っています。

ですので、現在はブラウザ上にて簡単にトークンを確認できてしまいますが、追々HTTPSへ対応する際にこの問題は解消されるはずです。

追記: 暗号化されないそうです…。あくまでもHTTPSだと暗号化された通信路が開設されるというだけですね。

セキュリティ面における本実装の不安

より安全なセキュリティ要件に基づいて設計を行う場合、諸々についてはcookieによる受け渡しで行うのが良いかと思います。

というのも、今回行っているaxiosの設定情報の活用について、それがどれだけ安全なものなのかが分からなかったです。もし今の状態では、攻撃者がJavascriptによってトークンを簡単に奪えてしまうとしたら、やはりHTTPSを前提にcookieによる受け渡しによって設計を行うと安全そうです。

メモ代わりに補足しますが、cookieを用いた設計へと変更する場合、

  1. HTTPS対応以降、cookieのSameSite属性をNone、Secure属性にチェックを入れる
  2. CORS設定上で credentials をtrueにする
  3. Rails上にてレスポンス内容に cookie[:token] と書いて、set-cookieを行う

となるかと思われます。

トークンの保存場所としてcookieを使うべきか否か、という議論は今回は不問とさせてください。

参考サイト

実装はこちらのコードをおおいに参考しました。

0
0
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?