なんてことはない、ただRubyでHTTPSなWebサーバにアクセスしてGETでデータをとってくるというだけの話なんだけど...
問題発生
まず思いついたのがこのコード。
require 'net/https'
Net::HTTP.get(URI.parse("https://mcrn.jp/ret.cgi"))
しかし、次のようなエラーが返ってきてしまう。
OpenSSL::SSL::SSLError: SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed from C:/Ruby21x/lib/ruby/2.1.0/net/http.rb:923:in `connect'
原因究明
もしかしてSNIに対応していない?と思ったけど違った(4年以上前に対応してる)。
さらに調べて、以下の記事をヒントに証明書の場所を調べてみてびっくり。
require 'openssl'
p OpenSSL::X509::DEFAULT_CERT_FILE
"C:/Users/Justin/Projects/knap-build/var/knapsack/software/x64-windows/openssl/1.0.1l/ssl/cert.pem"
=> "C:/Users/Justin/Projects/knap-build/var/knapsack/software/x64-windows/openssl/1.0.1l/ssl/cert.pem"
Justin!?誰???(※1)
RubyのインストールフォルダをサクラエディタでGrepしてみたところ、どうもlibeay32.dllに埋め込まれてるっぽい。
解決方法
上記記事によると「証明書(cacert.pem)」をダウンロードして、明示的に参照すればいいみたい。
https = Net::HTTP.new('mcrn.jp', 443)
https.open_timeout = SYSTEM_TIMEOUT_SEC
https.read_timeout = SYSTEM_TIMEOUT_SEC
https.use_ssl = true
https.verify_mode = OpenSSL::SSL::VERIFY_PEER
https.verify_depth = 5
https.ca_file = "C:\\cacert.pem" #証明書を明示的に参照
https.start do
response = https.get('/ret.cgi')
puts response.body
end
別の方法としては、同じく上記記事で紹介されてるcertified gemを導入してもOK。
やってることといえばNet::HTTP
とOpenSSL::X509::Store
にモンキーパッチを当てて
- 証明書の指定(※2)
- 証明書検証の必須
を自動化してるだけ。
今回は自サイトをフルHTTPS化してしまってるし、いちいち証明書指定するのが面倒なのでcertified gemを入れる方法を採用。
あとは冒頭のコードのrequireを
require 'certified'
に置き換えるだけ(※3)で無事HTTPSアクセスに成功。
ところで、さくらのレンタルサーバ内から自分自身(さくらのレンタルサーバでホスティングしてるSNI SSLサイト)にRubyでHTTPSアクセスしようとすると、何故かX-Sakura-Forwarded-Forヘッダ(※4)が設定されずにHTTPアクセスしようとして、.htaccessのリダイレクト設定にひっかかって301が返ってきてしまう。
強制HTTPSにしてなければサーバ内部の通信なんだから別にHTTPでもいいんだけど、.htaccessの判定条件を変えたくないし、強制HTTPSも止めたくない。
そこでちょっとしたトリック。
require 'net/https'
Net::HTTP.new("mcrn.jp",443).tap{|h|h.use_ssl=true}.get("/ret.cgi",{'X-Sakura-Forwarded-For' => '0.0.0.0'}).body
要するに「ヘッダを偽装して(SSLだとウソをついて)HTTP通信を強制」してるというわけ。
そして、実はこれを応用すると、telnetからX-Sakura-Forwarded-For(値はなんでもいい)を手打ちしてやれば、強制HTTPSを解除できてしまう。
脅威としては、マルウェアに感染して、そのマルウェアが必ずX-Sakura-Forwarded-Forを埋め込んで、さらにブラウザのHTTPS動作を上書きして「HTTPでアクセスできるホストはHTTPにしてしまう」あたりが考えられるかもしれないけど、そこまでされたらもう証明書検証をすっとばされたり、自己認証局証明書(オレオレ証明書)入れられたりしてるだろうし、たぶん手遅れ。
マルウェアなしの脅威だと、初回にHTTPでアクセスしたときに通信を改竄されてX-Sakura-Forwarded-Forを埋め込まれるぐらいだけど、一度でもHTTPSでアクセスすればHSTSで以降はHTTPS強制だし、現在preloadリストに申請中なので、無事通過すれば仮にアドレスバーに「http://mcrn.jp/」って打たれても初回からHTTPSになるから実質的には問題ない...かな?
さすがに根本的な対策をしようとすると、さくらのレンタルサーバ+SNI SSLの組み合わせ自体をやめないといけないので厳しい感じ。VPSにでも移行しちゃう?
コメント