Ruby のライブラリによる URL エンコード/エスケープの半角空白の扱いの違い
Ruby のライブラリで URL エンコード/エスケープする場合は、使うものによって半角空白の変換結果が異なるため、あらかじめ挙動を知っておかないと検証時不一致になってハマってしまう。
実際にハマったので、ここに違いを記録しておく。
私がハマった場面
Slack の slash command 向けの API を実装する際は、 Verifying requests from Slack にあるように、 SHA-256 と HMAC を使って検証することが推奨されている。このとき message body に URL エンコードされた値が設定されるが、半角空白を含む値がある場合は + に変換される。 Railsは半角空白を + に変更する。
これのテスト実装時に半角空白の変換の違いを忘れていて見事にハマった。
※ 2025/01/13 訂正
Rails にリクエストして params からアクセスすると半角空白が + になるだけだった。テストで request 通した値と予め計算した値を一致させようとしているうちに勘違いした。
実行環境
Ruby 3.4.1 で試している。
CGI.escape
これは、 + に変換される。
irb(main):001:0:> require 'cgi'
: true
irb(main):002:0:> CGI.escape 'A B'
: "A+B"
URI.encode_www_form_component
これは、+ に変換される。
irb(main):001:0:> require 'uri'
: true
irb(main):002:0:> URI.encode_www_form_component 'A B'
: "A+B"
ERB::Util.u
これは、%20 に変換される。
irb(main):001:0:> require 'erb'
: true
irb(main):002:0:> ERB::Util.u 'A B'
: "A%20B"
WEBrick::HTTPUtils.escape
これは、%20 に変換される。
なお、webrick は gem install webrick してインストールする必要がある。
irb(main):001:0:> require 'webrick'
: true
irb(main):002:0:> WEBrick::HTTPUtils.escape 'A B'
: "A%20B"
Addressable::URI.normalize_component
これは、%20 に変換される。
なお、addressable は gem install addressable してインストールする必要がある。
irb(main):001:0:> require 'addressable'
: true
irb(main):002:0:> Addressable::URI.normalize_component 'A B'
: "A%20B"
まとめ
半角空白については、CGI.escape, URI.encode_www_form_component は +、ERB::Util.u, WEBrick::HTTPUtils.escape, Addressable::URI.normalize_component は %20 を返す。
変換結果が何を返すかは irb で確認すればよいので、使うものによって変換結果が異なることを認識しておけばよい。
Discussion