要約
ローカル開発環境の https 化 | blog.jxck.io をやってみたら、思っていたより手軽だった話です。
現象
ある日、サイトAからサイトBに編集データを保存できなくなりました。
毎回サイトBでのログインが要求されます。一度ログインしても、サイドログインを求められます。
サイトBへの保存は、サイトAのJavaScriptでHTTPリクエストのPOSTメソッドを送っています。
ブラウザの開発ツールでみると、サイトBへのHTTPリクエストにCookieがついていません。
このため、サイトBではユーザーのログイン状態を確認できずログインを要求します。
原因の候補
最近のブラウザやWebアプリケーションフレームワークは、CookieのSameSite属性のデフォルト値がLaxになっています123。
これはサイトAのJavaScriptで作ったサイトB宛てのHTTPリクエストにサイトBのCookieをつけない制約です456。
今回の事象にぴったりです。本現象の原因の候補です。
本当かどうか、実際に動かして確かめてみましょう。
命題
サイトBのレスポンスのSet-CookieヘッダーのSameSite属性をNoneに設定したとき、サイトAのJavaScriptで作ったHTTPリクエスト(特にPOSTメソッド)にサイトBのCookieをつけて送れるか確認します。
SameSite属性をNoneにするには、同時にSecure属性も設定する必要があります7。
これは、HTTPSで接続したときのみCookieを送る設定です。
つまり、サイトBをHTTPSで動かす必要があります。
ローカル開発環境のHTTPS化
確認のためにHTTPS環境を用意したいです。
検証用にサーバーを用意するのは面倒です。
ローカル開発環境をHTTPS化します。
オレオレ証明書はつくりません。
ドメインを取得し、Let's Encryptから本物のSSL証明書を取得します。
環境
- WSL + Ubuntu
- Let's Encrypt
- Rails 7.0.8
- Puma
ドメインの取得
さくらインターネットで取得しました。
過去に取得したことがあるので、さくらインターネットを選びました。
どこで取得しても良いと思います。
Let's Encryptの認証にDNSをつかうので、DNSが設定できると便利だと思います。
取得したドメインに次のレコードを追加します。
名前 | 値 |
---|---|
エントリ名 | localhost |
タイプ | A |
データ | 127.0.0.1 |
例えばドメインがledsun.jp の場合に、ブラウザで http://localhost.ledsun.jp を開いた時に http://127.0.0.1 が開くようにします。
つづいて localhost.ledsun.jp のSSL証明書をLet's Encryptから取得します。
Certbotのインストール
Let's EncriptでSSL証明書するためにCertbotをインストールします8。
Ubuntuではsnapdでインストール出来ます9。
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
SSL証明書の取得
次のコマンドを実行します。
sudo certbot certonly --manual -d localhost.ledsun.jp --preferred-challenges dns
次のようなメッセージが表示されます。
Requesting a certificate for localhost3.ledsun.jp
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please deploy a DNS TXT record under the name:
_acme-challenge.localhost.ledsun.jp.
with the following value:
Ik9IyRLRei27lWszeyYyWJmCVA13kO_7LGOucXcSuEc
Before continuing, verify the TXT record has been deployed. Depending on the DNS
provider, this may take some time, from a few seconds to multiple minutes. You can
check if it has finished deploying with aid of online tools, such as the Google
Admin Toolbox: https://toolbox.googleapps.com/apps/dig/#TXT/_acme-challenge.localhost3.ledsun.jp.
Look for one or more bolded line(s) below the line ';ANSWER'. It should show the
value(s) you've just added.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Press Enter to Continue
このメッセージから上手いこと情報を読み取って、DNSに次のレコードを追加します。
名前 | 値 |
---|---|
エントリ名 | _acme-challenge.localhost |
タイプ | TXT |
データ | Ik9IyRLRei27lWszeyYyWJmCVA13kO_7LGOucXcSuEc |
この値はこの記事を書くために生成してるので、この値をそのまま使っても特に得することはありません。
レコードを追加してから30分くらいまってEnterキーを押します。
レコードを追加した直後はDNSに反映されないらしく失敗します。
どれくらいまで良いのかはわかりません。とりあえず30分待てば大丈夫なようです。
成功すると /etc/letsencrypt/live/localhost.ledsun.jp
配下にSSL証明書が作成されます。
HTTPSでの起動
作成したSSL証明書をアプリケーションのディレクトリにコピーします。
root権限で作ったので、所有者を変更します。
sudo cp /etc/letsencrypt/live/localhost.ledsun.jp/privkey.pem .
sudo chown ledsun privkey.pem
sudo cp /etc/letsencrypt/live/localhost.ledsun.jp/fullchain.pem .
sudo chown ledsun fullchain.pem
config/puma.rb
に証明書を設定します。
ssl_bind "0.0.0.0", "3001", {
key: './privkey.pem',
cert: './fullchain.pem'
}
bin/rails s
でRailsアプリケーションを起動すれば、 https://localhost.ledsun.jp:3001/ で接続出来ます。
動作確認
Cookieの設定をSameSite=noneにして動作確認してみましょう。
config/application.rb
に次の設定を追加します。
config.action_dispatch.cookies_same_site_protection = :none
config.force_ssl = true
config.hosts << "localhost.ledsun.jp:3001"
bin/rails s
でRailsアプリケーションを起動します。
このあとサイトAのアプリケーションからHTTPリクエストのPOSTメソッドを送ってCookieを送信できれば成功です。
- ブラウザの開発コンソールログを開く
- POSTメソッドを送る
- ネットワークタブで、送信したPOSTメソッドを選択
- Cookieタブを開く
Cookieが送信されていることがわかります。
SameSite=Laxのとき
比較用にSameSite=Laxのときの開発コンソールの表示を載せます。
「フィルターで除外された要求 Cookie を表示する」をチェックすると送れなかったCookieを確認できます。
SameSite属性がLaxであることがわかります。
まとめ
ローカル開発環境をHTTP化することで、SameSite=noneの動作を確認できました。
オレオレ証明書ではないので、ブラウザでの警告等はでません。
ドメインの取得費用は掛かりますが、1つあれば色々なアプリケーションで活用できます。
今後のWeb技術はHTTPSを前提とした物が増えると思います。
個人で1つ開発用のドメインを持っておくのも良いのかも知れません。
参考
- pschinis/rails_same_site_cookie: Manages the new SameSite=None behavior for Rails apps that use cookie-based authentication for cross-domain requests
- rails_same_site_cookie gemで、RailsアプリにChrome 80向けのSameSite属性を指定する #Ruby - Qiita
- SameSite=None: Known Incompatible Clients
-
Chrome 80が密かに呼び寄せる地獄 ~ SameSite属性のデフォルト変更を調べてみた #Chrome - Qiita ↩
-
SameSite=Lax is default value since Rails 6.1 · Issue #31 · pschinis/rails_same_site_cookie ↩
-
Google Developers Japan: 新しい Cookie 設定 SameSite=None; Secure の準備を始めましょう ↩
-
Let's EncryptのSSL証明書をDNS認証で発行してみた。(DNSはお名前.com) #googlecloud - Qiita ↩
-
Install Let's Encrypt SSL on Ubuntu with Certbot | InMotion Hosting ↩