LoginSignup
3
1

「RubyとSinatraではじめるwebアプリケーション 開発の教科書」Sinatraのバージョンに注意しましょう

Last updated at Posted at 2023-04-06

はじめに

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を作成します。

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

image.png
先ほどでた警告部分を抜粋します。

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とします)

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

image.png

次に進んでみます。

7.4 Cookieを中身から理解する (152ページ)

書籍に従い得られたCookieを貼り付けてdecodecookie.rbを作成します。
"xxx"部分も先ほどのsecretに入れた文字列に変更します。

decodecookie.rb
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に以下を記載しインストールします。

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は以下のように記載しています。

Gemfile
gem "sinatra"
gem "webrick"

Gemfile.lockの内容は書籍に記載がなく、バージョンは分かりませんでした。
推測ですが、書籍の発行が2021年7月30日となっていたので、
書籍のSinatraは2系であったのではないかと思われます。

追記 2023年8月29日

本書のページの関連サイトに、著者によるサポートページがありました!
そちらに以下の記載がありました。

本書ではSinatraのバージョン2系を使っています。

Sinatra2系に変更するやり方、またSinatra3系で同じ結果を得るやリ方も記載がありましたので、ぜひ確認してみてください!

おわりに

最初からSinatra2系でインストールしたほうが、書籍と同様の結果を得られます。
その方が理解を深めやすく学びやすいかと思います。
バージョンを気にせずインストールすると、現時点ではSinatra3系になりますので、ご注意ください。

最後に、たくさんご指導いただきました@jnchitoさん、本当にありがとうございました!

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