はじめに
Qiita初投稿です。
この記事は2023年4月1日時点のものです。
(追記:2023年8月29日あり)
書籍「RubyとSinatraではじめる Webアプリケーション開発の教科書」で勉強しています。
手順通りにやっていて、エラーが出て苦戦しました。
そのエラーの原因について書いていこうと思います。
結論
先に結論を言うと、
Sinatra2系でやりましょう!
ということです。
作業環境
- Ruby 3.1.2
- Sinatra 3.0.5
苦戦した所
7.3.2 SinatraでCookieを手軽に扱う (151ページ)
sessiontest.rbを作成します。
require 'sinatra'
set :sessions,
secret: "xxx"
set :environment, :production
get '/page1' do
session[:message] = 'ABC'
redirect '/page2'
end
get '/page2' do
session[:message]
end
実行します。
$ bundle exec ruby sessiontest.rb
SECURITY WARNING: Your secret is not long enough. It must be at least
32 bytes long and securely random. To generate such a key for use
you can run the following command:
ruby -rsecurerandom -e 'p SecureRandom.hex(32)'
Called from: /Users/omisan/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/rack-2.2.6.4/lib/rack/builder.rb:158:in `new'.
[2023-04-07 14:26:01] INFO WEBrick 1.8.1
[2023-04-07 14:26:01] INFO ruby 3.1.2 (2022-04-12) [x86_64-darwin22]
== Sinatra (v3.0.4) has taken the stage on 4567 for production with backup from WEBrick
[2023-04-07 14:26:01] INFO WEBrick::HTTPServer#start: pid=2940 port=4567
警告が出たものの、Sinatraは動いたようです。
書籍に従って/page1
に繋いでみるもエラーが出ました。
[2023-04-07 14:23:27] ERROR ArgumentError: key must be 32 bytes
/Users/omisan/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/rack-protection-3.0.5/lib/rack/protection/encryptor.rb:24:in `key='
/Users/omisan/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/rack-protection-3.0.5/lib/rack/protection/encryptor.rb:24:in `encrypt_message'
/Users/omisan/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/rack-protection-3.0.5/lib/rack/protection/encrypted_cookie.rb:241:in `write_session'
/Users/omisan/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/rack-2.2.6.4/lib/rack/session/abstract/id.rb:388:in `commit_session'
/Users/omisan/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/rack-2.2.6.4/lib/rack/session/abstract/id.rb:268:in `context'
/Users/omisan/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/rack-2.2.6.4/lib/rack/session/abstract/id.rb:260:in `call'
/Users/omisan/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/rack-2.2.6.4/lib/rack/logger.rb:17:in `call'
/Users/omisan/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/rack-2.2.6.4/lib/rack/common_logger.rb:38:in `call'
/Users/omisan/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/sinatra-3.0.5/lib/sinatra/base.rb:261:in `call'
/Users/omisan/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/sinatra-3.0.5/lib/sinatra/base.rb:254:in `call'
/Users/omisan/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/rack-2.2.6.4/lib/rack/head.rb:12:in `call'
/Users/omisan/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/rack-2.2.6.4/lib/rack/method_override.rb:24:in `call'
/Users/omisan/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/sinatra-3.0.5/lib/sinatra/base.rb:219:in `call'
/Users/omisan/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/sinatra-3.0.5/lib/sinatra/base.rb:2007:in `call'
/Users/omisan/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/sinatra-3.0.5/lib/sinatra/base.rb:1566:in `block in call'
/Users/omisan/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/sinatra-3.0.5/lib/sinatra/base.rb:1782:in `synchronize'
/Users/omisan/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/sinatra-3.0.5/lib/sinatra/base.rb:1566:in `call'
/Users/omisan/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/rack-2.2.6.4/lib/rack/handler/webrick.rb:95:in `service'
/Users/omisan/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/webrick-1.8.1/lib/webrick/httpserver.rb:140:in `service'
/Users/omisan/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/webrick-1.8.1/lib/webrick/httpserver.rb:96:in `run'
/Users/omisan/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/webrick-1.8.1/lib/webrick/server.rb:310:in `block in start_thread'
127.0.0.1 - - [07/Apr/2023:14:23:26 JST] "GET /page1 HTTP/1.1" 500 300
- -> /page1
[2023-04-07 14:23:27] ERROR ArgumentError: key must be 32 bytes
/Users/omisan/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/rack-protection-3.0.5/lib/rack/protection/encryptor.rb:24:in `key='
/Users/omisan/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/rack-protection-3.0.5/lib/rack/protection/encryptor.rb:24:in `encrypt_message'
/Users/omisan/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/rack-protection-3.0.5/lib/rack/protection/encrypted_cookie.rb:241:in `write_session'
/Users/omisan/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/rack-2.2.6.4/lib/rack/session/abstract/id.rb:388:in `commit_session'
/Users/omisan/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/rack-2.2.6.4/lib/rack/session/abstract/id.rb:268:in `context'
/Users/omisan/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/rack-2.2.6.4/lib/rack/session/abstract/id.rb:260:in `call'
/Users/omisan/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/rack-2.2.6.4/lib/rack/logger.rb:17:in `call'
/Users/omisan/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/rack-2.2.6.4/lib/rack/common_logger.rb:38:in `call'
/Users/omisan/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/sinatra-3.0.5/lib/sinatra/base.rb:261:in `call'
/Users/omisan/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/sinatra-3.0.5/lib/sinatra/base.rb:254:in `call'
/Users/omisan/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/rack-2.2.6.4/lib/rack/head.rb:12:in `call'
/Users/omisan/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/rack-2.2.6.4/lib/rack/method_override.rb:24:in `call'
/Users/omisan/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/sinatra-3.0.5/lib/sinatra/base.rb:219:in `call'
/Users/omisan/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/sinatra-3.0.5/lib/sinatra/base.rb:2007:in `call'
/Users/omisan/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/sinatra-3.0.5/lib/sinatra/base.rb:1566:in `block in call'
/Users/omisan/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/sinatra-3.0.5/lib/sinatra/base.rb:1782:in `synchronize'
/Users/omisan/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/sinatra-3.0.5/lib/sinatra/base.rb:1566:in `call'
/Users/omisan/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/rack-2.2.6.4/lib/rack/handler/webrick.rb:95:in `service'
/Users/omisan/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/webrick-1.8.1/lib/webrick/httpserver.rb:140:in `service'
/Users/omisan/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/webrick-1.8.1/lib/webrick/httpserver.rb:96:in `run'
/Users/omisan/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/webrick-1.8.1/lib/webrick/server.rb:310:in `block in start_thread'
127.0.0.1 - - [07/Apr/2023:14:23:27 JST] "GET /favicon.ico HTTP/1.1" 500 300
http://127.0.0.1:4567/page1 -> /favicon.ico
SECURITY WARNING: Your secret is not long enough. It must be at least
32 bytes long and securely random. To generate such a key for use
you can run the following command:
ruby -rsecurerandom -e 'p SecureRandom.hex(32)'
「あなたのsecretは長さが不十分です」ということでした。
警告メッセージに従ってみます。
$ ruby -rsecurerandom -e 'p SecureRandom.hex(32)'
"4fdd9f5514e196ed36cf07e0f8e168929320516c881946687382a1fc34b76cca"
ここで得られた
"4fdd9f5514e196ed36cf07e0f8e168929320516c881946687382a1fc34b76cca"
をsecretに入れてみます。(sessiontest2.rbとします)
require 'sinatra'
set :sessions,
secret: "4fdd9f5514e196ed36cf07e0f8e168929320516c881946687382a1fc34b76cca"
set :environment, :production
get '/page1' do
session[:message] = 'ABC'
redirect '/page2'
end
get '/page2' do
session[:message]
end
すると警告もなくSinatraが起動され、エラーも一応起こりませんでした。
$ bundle exec ruby sessiontest2.rb
[2023-04-08 17:17:31] INFO WEBrick 1.8.1
[2023-04-08 17:17:31] INFO ruby 3.1.2 (2022-04-12) [x86_64-darwin22]
== Sinatra (v3.0.4) has taken the stage on 4567 for production with backup from WEBrick
[2023-04-08 17:17:31] INFO WEBrick::HTTPServer#start: pid=7529 port=4567
127.0.0.1 - - [08/Apr/2023:17:17:46 +0900] "GET /page1 HTTP/1.1" 302 - 0.0338
127.0.0.1 - - [08/Apr/2023:17:17:46 JST] "GET /page1 HTTP/1.1" 302 0
- -> /page1
127.0.0.1 - - [08/Apr/2023:17:17:46 +0900] "GET /page2 HTTP/1.1" 200 3 0.0018
127.0.0.1 - - [08/Apr/2023:17:17:46 JST] "GET /page2 HTTP/1.1" 200 3
- -> /page2
127.0.0.1 - - [08/Apr/2023:17:17:46 +0900] "GET /favicon.ico HTTP/1.1" 404 469 0.0025
127.0.0.1 - - [08/Apr/2023:17:17:46 JST] "GET /favicon.ico HTTP/1.1" 404 469
http://127.0.0.1:4567/page2 -> /favicon.ico
次に進んでみます。
7.4 Cookieを中身から理解する (152ページ)
書籍に従い得られたCookieを貼り付けてdecodecookie.rbを作成します。
"xxx"部分も先ほどのsecretに入れた文字列に変更します。
require 'cgi'
require 'base64'
require 'openssl'
require 'sinatra'
s = "9R7173F0gEBcTVOLxxlWaJ8yGKWeQnPTi8bTHXjTdcYCOdW1l9uEbA%2Fl%2F4ER7DxgAXGVSCqLCXNwn8pPTH1ij%2B826OnN15wLrgiT3s1fvTxmmXbjafGUxKECWLow%2FNBqDZAACEhWtxHvPt8uBde1iViPXQ9K8xbtQ2o0jBUmun1xK20yEk1l6kGI325jiTU%2BnbeVJMmAvkMHxTRpG25lP679e9A7fC3YJIybd1q9FC16ogShvPoKHW06dPj7cQpe1slNoeEkxwb3YdZ1VT6ldrH95y7EbS4JeCXCIqUtM1%2FAz6JX1mHABz9BiKpYlImgDXkVNDLWDdfvVMkmTRGMu4un%2FAO0FUyDDQ7IQOpwGQL4P9rJzhCsUMCB3C%2BoSh8C2aB21f4eJbr1Y%2FOSxU31Dg%3D%3D--p2WeR3WUg8yCN5Bq--RPUJtU%2BxylAQ6ORwZ0m4Gw%3D%3D"
sb64, digest = CGI.unescape(s).split("--")
puts Marshal.load(Base64.decode64(sb64))
puts OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA1.new, "4fdd9f5514e196ed36cf07e0f8e168929320516c881946687382a1fc34b76cca", sb64)
puts digest
実行すると、エラーになりました。
$ bundle exec ruby decodecookie.rb
<internal:marshal>:34:in `load': incompatible marshal file format (can't be read) (TypeError)
format version 4.8 required; 245.30 given
from decodecookie.rb:9:in `<main>'
ここで苦戦してしまいました。
暗号化されたcookieの復元方法はわかりませんでした。
警告・エラーの原因
最初の警告・エラーの原因は、Sinatra 3.0になってcookieが暗号化されるようになったからと思われます。
下記の2番目のところです。
詳細は以下になります。
解決策
暗号化されたcookieの復元方法は分からなかったので、
本書を読み進める場合はSinatraのバージョンを2系に固定したほうが良いかと思います。
Sinatraのバージョンを2系でインストールする方法
Gemfileに以下を記載しインストールします。
gem "sinatra", "~> 2.2.3"
gem "webrick"
Sinatra2系にした実行結果
sessiontest.rbを実行し、警告・エラーなくCookieを取り出すことができました。
$ bundle exec ruby sessiontest.rb
[2023-04-08 17:50:31] INFO WEBrick 1.8.1
[2023-04-08 17:50:31] INFO ruby 3.1.2 (2022-04-12) [x86_64-darwin22]
== Sinatra (v2.2.3) has taken the stage on 4567 for production with backup from WEBrick
[2023-04-08 17:50:31] INFO WEBrick::HTTPServer#start: pid=8355 port=4567
127.0.0.1 - - [08/Apr/2023:17:50:47 +0900] "GET /page1 HTTP/1.1" 302 - 0.0116
127.0.0.1 - - [08/Apr/2023:17:50:47 JST] "GET /page1 HTTP/1.1" 302 0
- -> /page1
127.0.0.1 - - [08/Apr/2023:17:50:47 +0900] "GET /page2 HTTP/1.1" 200 3 0.0018
127.0.0.1 - - [08/Apr/2023:17:50:47 JST] "GET /page2 HTTP/1.1" 200 3
- -> /page2
書籍に従いdecodecookie.rbを実行すると、書籍通りの結果を得ることができました。
$ bundle exec ruby decodecookie.rb
{"session_id"=>"f4cd87f8f2893dab436864d4087907821e7600730a71dadc9d02c81f32e84509", "csrf"=>"jeylTPdKJ5bO0UpB8qYurYYHmqqfKjDhmYCCYecevWs=", "tracking"=>{"HTTP_USER_AGENT"=>"ec8d6f2f19615b997197e0af82095e2dbbcbfb10"}, "message"=>"ABC"}
27bc3e7ffefc30481237f5a605235382f04e9f2a
27bc3e7ffefc30481237f5a605235382f04e9f2a
[2023-04-08 17:52:51] INFO WEBrick 1.8.1
[2023-04-08 17:52:51] INFO ruby 3.1.2 (2022-04-12) [x86_64-darwin22]
== Sinatra (v2.2.3) has taken the stage on 4567 for development with backup from WEBrick
[2023-04-08 17:52:51] INFO WEBrick::HTTPServer#start: pid=8432 port=4567
書籍のSinatraのバージョンについて
本書では、bundlerでSinatraをインストールしておりGemfileは以下のように記載しています。
gem "sinatra"
gem "webrick"
Gemfile.lockの内容は書籍に記載がなく、バージョンは分かりませんでした。
推測ですが、書籍の発行が2021年7月30日となっていたので、
書籍のSinatraは2系であったのではないかと思われます。
追記 2023年8月29日
本書のページの関連サイトに、著者によるサポートページがありました!
そちらに以下の記載がありました。
本書ではSinatraのバージョン2系を使っています。
Sinatra2系に変更するやり方、またSinatra3系で同じ結果を得るやリ方も記載がありましたので、ぜひ確認してみてください!
おわりに
最初からSinatra2系でインストールしたほうが、書籍と同様の結果を得られます。
その方が理解を深めやすく学びやすいかと思います。
バージョンを気にせずインストールすると、現時点ではSinatra3系になりますので、ご注意ください。
最後に、たくさんご指導いただきました@jnchitoさん、本当にありがとうございました!