2
1

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.

Web APIのエンドポイントやレスポンスにIDを使用してはいけないか考える

Posted at

はじめに

皆様、こんにちは!
佐久間まゆちゃんのプロデューサーの@hiroki_tanakaです。

私は現在、RailsシステムのAPIリプレイス活動に携わっています。
その際にエンドポイントにIDを使用するかどうかやレスポンスにIDフィールドを含めるか含めないかといったID問題があったので、考えたこと・調べたことをまとめたいと思います。

結論

エンドポイントやレスポンスにIDを使用しないほうが良い。

理由

Railsにおいて、IDカラムは基本的にDBでAuto Incrementされて発行された連番のサロゲートキーに当たります。
そのため、サロゲートキーであるIDカラムが露出してしまうことで以下のような問題が発生します。

IDの値の大小によって、サービス規模が推測出来てしまう。

例えば、Get /api/users/:idといったIDに紐づくユーザ情報を取得するAPIが存在した場合、ユーザは:idに好きな値を入れることができます。
そのためGet /api/users/10000まではユーザ情報が取得出来ていたが、Get /api/users/10001以降の値では全くユーザ情報が返ってこなくなった場合、そのサービスのユーザ数は1万人前後ということが推測出来てしまいます。
エンドポイントだけでなく、レスポンスに含まれている場合でも推測可能です。
自社のサービス規模は企業にとって重要な情報の1つです。それが簡単に第三者にわかってしまう状況は害ことあっても利がありません。
(虚偽広告をするのは以ての外ですが、もし「会員登録者数10万人突破!」を宣伝で謳っていたのに、IDを調べてみたら実際は1万人前後しかいなければ確実に炎上してしまいます。)

また、IDの大小とは直接関係ないですがユーザIDのような個人情報に紐付いているIDが推測可能になってしまうのはセキュリティの観点でもNGです。

IDはインクリメントする値のため、スクレイピングやアタックが容易になってしまう。

例えば、書評サイトを運営していて/api/books/:idをいうエンドポイントで様々な本の書評を取得している場合、下記のように:idを連番にするだけで簡単にスクレイピングでき、情報を抜き取る事ができます。

Get /api/books/100
Get /api/books/101
Get /api/books/102



Get /api/books/999

urlが予測しやすくスクレイピングが容易な状態だと攻撃者や競合相手に情報を簡単に渡すリスクが非常に大きいだけでなく、攻撃によるセキュリティリスクも高いです。
上記の例だと仮に/api/books/100で攻撃に成功した場合、他のurlにも攻撃を繰り返すには簡単なfor文やwhile文で実現することが出来てしまうので、リスクが大きいです。

処理によってはIDにintegerの最大値を使用した場合、次のデータを作成する際にAuto Incrementでエラーとなってしまう可能性がある。

例えば、Getで連携されたIDが存在しない場合はそのIDのデータを作成して、後続処理を続けるという処理があったとします。
(find_or_initialize_byfind_or_create_byが使用されるイメージです。)
その際に、いたずら心を持ったユーザがMySQLのintegerの最大値である2147483647を使用してGet /api/hoge/2147483647を行った場合、ID:2147483647のデータが作成されます。
IDはAuto Incrementで発番されるので、次にこのテーブルのデータを作成しようとした時にIDはintegerの最大値+1となってしまい、MySQLでエラーが発生します。
このように開発者の予期せぬ所でエラーの発生原因を作ってしまいます。

では、どうするべきなのか

下記のようにIDではなく、システム内でランダム文字列として生成するkeyやcodeをエンドポイントやレスポンスに使用します。
IDの露出を極力抑えることでシステムの安全性を高める事ができます。
Get /api/piyo/abcd12345

もしくは、セキュアなuuidを使用する形でも問題ありません。

require 'securerandom'
p SecureRandom.uuid
#=> ad54c1ed-0ac7-47c4-8a4a-7f63fbbf9e6e
# Get /api/piyo/ad54c1ed-0ac7-47c4-8a4a-7f63fbbf9e6e

おわりに

これまで何気なくエンドポイントにIDを使用し、レスポンスにIDカラムを含めていましたがこれを機に見直していきたいです。

2
1
0

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
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?