😎

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"

https://docs.ruby-lang.org/ja/3.4/method/CGI/s/escape.html

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"

https://docs.ruby-lang.org/ja/3.4/method/URI/s/decode_www_form_component.html

ERB::Util.u

これは、%20 に変換される。

irb(main):001:0:> require 'erb'
:  true
irb(main):002:0:> ERB::Util.u 'A B'
:  "A%20B"

https://docs.ruby-lang.org/ja/3.4/method/ERB=3a=3aUtil/m/u.html

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"

https://docs.ruby-lang.org/ja/3.4/class/WEBrick=3a=3aHTTPUtils.html#M_ESCAPE

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"

https://www.rubydoc.info/gems/addressable/Addressable/URI#normalize_component-class_method

まとめ

半角空白については、CGI.escape, URI.encode_www_form_component は +、ERB::Util.u, WEBrick::HTTPUtils.escape, Addressable::URI.normalize_component は %20 を返す。

変換結果が何を返すかは irb で確認すればよいので、使うものによって変換結果が異なることを認識しておけばよい。

あしたのチーム Tech Blog

Discussion