質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.77%

APIトークンの効率の良い実装方法

受付中

回答 1

投稿 編集

  • 評価
  • クリップ 5
  • VIEW 2,025

score 50

現在使っているのがRailsなので、Railsをベースに質問させていただきます

やりたいこと

Railsを使って主にスマホアプリで使うRESTful APIを開発しています。
その際、ユーザーのデバイスからの要求に基づいて新規でユーザー情報と紐付いたAPIトークンを発行し、デバイスに返しています。
ユーザー情報と紐付いて、それによって応答が変わるようになっていますが、その実装方法に疑問があります。

現状

ユーザーのデバイスから新規登録の要求が送られると、まずデータベースにユーザーを登録します。

実装としてはこんな具合です(例)

require 'securerandom'
require 'json'

def register_user
    new_user = User.new
    new_user.name = params["name"]
    new_user.user_id = params["email"] #API内部で識別に使用する名前
    new_user.rand_id = SecureRandom.hex(16) #これを使ってトークンを生成する
    new_user.save()
       ・
       ・
       ・

そして、APIトークンを生成します

    encryptor = ActiveSupport::MessageEncryptor.new(ENV["encryption_key"], cipher: 'aes-256-cbc')
    api_token = encryptor.encrypt_and_sign(new_user.rand_id) #ランダム生成したIDを暗号化する
    render json:{"status" => "OK","token" => api_token}
end

APIのリクエストに対しての認証は以下の通りです

def new_post
    begin
        encryptor = ActiveSupport::MessageEncryptor.new(ENV["encryption_key"], cipher: 'aes-256-cbc')
        target_user = User.where(:rand_id => encryptor.decrypt_and_verify(request.headers["Authorization"])
        if !target_user.present?
            raise
    rescue
        render json:{"status":"error","error_description":"Invalid Authorization"},status:403
        return


    post_data = JSON.parse(request.body.read)
    if !post_data["title"].present? or !post_data["body"].present?
        render json:{"status" => "error", "error_description" => "Invalid Paramater"},status:400
        return

    post_to_db = Post.new
    post_to_db.user_id = target_user.user_id #内部識別用のIDはトークンの生成に使われない値
                ・
                ・
                ・

疑問

1つ目に疑問なのが、いくらランダム生成した値とはいえ、APIトークンの素となる値がユーザーと同じテーブルに存在してていいのかというところです。
データベース(Postgresql)とRailsは同一の物理サーバー内で完結していて、データベースのUserテーブルとDotenvで管理している暗号鍵を入手しなければAPIから期待通りの応答を得ることは難しいと思われますし、これらを奪取されるのはまた別の次元の問題なので、Railsだけ見れば問題ないのではないかと思ってこの実装にしていますが、何とも言い難い気持ち悪さが残っています・・・

これは果たして妥当なDB設計なのでしょうか、もっと良いDB設計があればご教授頂きたいです。


2つ目にわざわざ暗号化が必要なのかというところです。
暗号化をしている理由としては、同一暗号鍵と同一平文の組み合わせでも、暗号化を実行する度に結果が変わるので推測が難しくなるとの考えがあります(もしかしたらここからして愚かなのかもしれませんが・・・)

現状、この実装でセキュリティ的にもパフォーマンス的にも不便が起きたことはないのですが、それはあくまでユーザーが圧倒的に少ないからであって、多くなるにつれて暗号化がボトルネックになってパフォーマンスが低下するということがあってほしくはありません。
もし暗号化よりもパフォーマンスの良い方法があればご教授頂きたいです。

まとめ

  • どこから間違ってるのか分からないくらい、「ホントにこれ合ってるの?」という実装がとても多いです・・・
  • APIトークンの生成に限らず、助言などをいただけると大変ありがたいです・・・

宜しくお願いしますm(_ _)m
質問などあれば、随時返信・編集しますので、言っていただければと・・・

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • ockeghem

    2019/01/16 22:33

    回答しようかなと思いつつ、元のプログラムを読み解くのが面倒なので、結局手がつかないのですよね。もう少し、解決すべき問題を端的に提示していただけないでしょうか? (回答を保証するものではありません)

    キャンセル

回答 1

0

APIトークンの素となる値がユーザーと同じテーブルに存在してていいのか

トークンで何ができるかによるかも
トークンでユーザテーブルがみられるだけならトークンを分けても片方がもれればどちらももれるので分ける意味はないかと

このトークンで他のもっとみられたくないテーブルアクセスもできるのであれば
分けることでユーザテーブルの情報はもれたけどもっと重要な情報は守られるということはあるかも

これらを奪取されるのはまた別の次元の問題

よくあるのはユーザ入力をクエリの中にいれてしまう
いわゆるSQLインジェクションでクエリの脆弱性をつかれたときにトークンが流出してしまう

あと Rails だとモデルとカラムが連携してるので検索した意図的に select しないと
モデルに自動的にトークン情報も含まれるので
めったにおきないエラーメッセージにレコードのダンプを返すようにしてそのまま忘れてたりとか

それから一部サービス開発を第3社に提供する場合にユーザ情報はみてもいいけどトークンは見られたくないみたいな場合があるかどうか
あるいはバイトや新人にテーブル操作はしてもいいけど認証情報はさわらせたくないとか

分けておいたほうがそういう細かい権限の設定やバグを回避できる場合もあるかも

わざわざ暗号化が必要なのか

暗号化というよりは環境を識別する必要があるかどうかではないですか
たとえばステージングとプロダクションがあってステージングで作ったトークンで本番環境でアクセスできてしまうのがいいかどうか
あるいは別サービスが同じデータベースを見ているとか
それがかまわないのであれば暗号化する必要はないと思います

思いつくのはこのくらいです

Rails だったら自分でトークン管理しなくても Gem いれて1行かくだけで使える認証システムがいくつかあるんじゃないかな
自分はいれたことがないので詳しくないですが

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.77%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問