Baby step - 思考と実験の足跡

日常のちょっとした、気になって試したこと集です。

base64の値をURLに使うとエラーになる時がある話

背景

サービスに未登録のユーザーが会員登録をするためのページとして以下を用意しました。

## routes.rb
get "entry/:access_key"

Railsでの実装例

access_keyとは、認証のためサービスが一時的に発行したトークンです。

access_key = SecureRandom.base64

さて、上記のページにアクセスをするとき、数回に一度404エラーになるときがありました。

はじめに結論

base64で生成される文字列にはURLに利用できない文字が含まれるため、urlsafeなbase64を使いましょう!

base64とは?

要点を箇条書きにするとこんな感じ。

  • エンコード方式の1つ
  • 半角英数字(a-zA-Z0-9)62文字と"+", "/"との64文字が使われる
  • 文字の長さを整えるために末尾に"="を付ける(パディングする)こともある

メールを送るためのプロトコルSMTPで、画像や音声データをやり取りする際に使われていたようです。

cf. base64ってなんぞ??理解のために実装してみた - Qiita

Rubyでは、SecureRandomクラスからbase64を使えます。

cf. module SecureRandom (Ruby 2.7.0 リファレンスマニュアル)

URLで使える文字・使えない文字

URI(URL)には、予約文字非予約文字があります。

  • 予約文字:区切り文字など特定の目的で使われる文字で、目的以外には利用不可
  • 非予約文字:自由にURLに使用できる文字

cf. URIで使用できる文字 - CyberLibrarian

base64で利用される記号の"+", "/"は、どちらも予約文字にあたりますね。

つまり、今回の問題の問題は base64で生成される文字列の中に予約文字が含まれている」 場合に発生する、ということになります。

解決策

urlsafeなbase64があるので、そちらを使いましょう。 Rubyではこちらですね。

module SecureRandom (Ruby 2.7.0 リファレンスマニュアル)

仕組みとしては、予約文字の"+"を"-"に、”/"を"_"にそれぞれ変換することでURLで使えるようにしていました。

cf. urlsafe_encode64 (Base64) - APIdock

なお、urlsafeなbase64Rubyだけでなく、PHPJavaなど他の言語でも対応してそうです。

cf. PHP base64をURLSafeにする方法

参考