kasei_sanのブログ

かせいさんのIT系のおぼえがきです。胡乱の方はnoteとtwitterへ

cookieのSameSite属性 おぼえがき

f:id:kasei_san:20201029124833p:plain
same

経緯

cookieにはSameSiteという属性があって、これがChrome80からデフォルトの扱いが変わった

  • 正確には変わる予定だったが、コロナの影響で変更は先延ばしにされて、2020/07/14 のChrome84から変更されるようになった。なお、83, 81, 80 にも適時適用されたらしいです(なお、82は欠番)

何が変わったの?

SameSite属性が未指定の場合のデフォルト値が、None から、Lax に変更になった

そもそも、SameSite属性ってどういう属性なの?

ドメインAでset-cookieされた時に、ドメインBからドメインAにアクセスした時に、リクエストヘッダにそのcookieを入れるかどうかを決める属性

どういう振る舞いがあるの?

Strict、Lax、Noneの3つの値がある

  • Strict: いかなる場合もcookieをリクエストに含めない
  • Lax: POST、画像読み込み、XHR、iframeでのリクエストは、cookieをリクエストに含めない。通常のGETならば含める
  • None: すべてのリクエストに含める。ただし、Chrome80からはSecure属性も必須

Secure属性って?

HTTPSの場合のみcookieの送信を許可するという設定

これは、通信経路上で、cookieを盗み見 & 書き換えが行われないための施策らしいです

なんでデフォルト値が、None から、Lax に変更になったの?

CSRF対策

CSRFってなんだっけ?

CSRF (クロスサイト・リクエスト・フォージェリ)。異なるドメインから偽装リンク等を通じて、ユーザに意図としない動作をさせる攻撃手法

インターネット古老勢だと知っているぼくはまちちゃん攻撃が有名

www.atmarkit.co.jp

mixiにPOSTするURLを偽装して掲示板などに貼る -> mixiにログインしているユーザがそのURLを踏むと、POSTが行われて、意図しない書き込みが行われる。という攻撃

で、SameSite属性のデフォルトをLaxに変えることで、他ドメインからのPOSTを規制して、CSRF攻撃を潰すのが今回のアップデートの目的(らしいです)

他のブラウザの動向はどうなの?

Firefox

検討中

bugzilla.mozilla.org

safari

2020 年 3 月に 3rd Party Cookie (ドメインをまたぐcookieのリクエスト)を完全にブロックするとアナウンス

webkit.org

SameSite属性をNoneにした場合は、設定によって挙動が変わるらしい

blog.cybozu.io

safari: MacOS 10.14とiOS 12の SameSiteバグ

SameSite=Noneを誤って、SameSite=Strictとするバグがあるので注意

t.co

これのために、Chrome80移行対応で SameSite=None に変更したい場合、サーバなりの設定でUAを見てiOSかMacOSの特定のバージョンの場合「 SameSite=None を設定しない」という処理を入れる必要がある

まだ、MacOS 10.14とiOS 12のユーザはそこそこいるはずなので...

古いブラウザでは SameSite=None が実装されていない

古いChromeやAndroidのUC Browserの古いバージョンでも、 SameSite=None が実装されていないため、正しく動かないという問題もある

これもiOSやMacOSのように、UAを見て SameSite=None を入れない。という方法を取る必要がある

この辺の対応をしないと困ることって何?

自前でコンバージョントラッキングをしていて、お客様のコンバージョンページにcookieトラッキングするようなタグを埋め込んでるサイトだと、 SameSite=None にしないと挙動しなくなる

トラッキング関係以外だとなにかあるのかな...?

TLSおぼえがき

TLSってなに?

Transport Layer Security(トランスポート・レイヤー・セキュリティ、TLS)は、インターネットなどのコンピュータネットワークにおいてセキュリティを要求される通信を行うためのプロトコルである。主な機能として、通信相手の認証、通信内容の暗号化、改竄の検出を提供する

Transport Layer Security - Wikipedia

SSLとどう違うの?

以前はSSLと呼ばれていた。SSL3.0の後に、TLS1.0と名前が変わった

  • SSL3.0は、使用上の脆弱性(POODLE) が発見されてTLS1.0への移行が推奨。2015/06 には、RFC 7568 で使用が禁止されている

HTTPSとどう違うの?

HTTPをTLSで通信をセキュアにしたものがHTTPS

  • HTTP+TLS=HTTPS

TLSは複数のバージョンがあるみたいだけど?

2020/10/12 現在、TLS 1.0/1.1/1.2/1.3 がある。サーバもブラウザも複数のバージョンのTLSを取り扱うことができる

サーバ管理しているのだけど、どのバージョンまで許可すればよいの?

TLS1.0/1.1は無効化して、TLS 1.2/1.3のみを許可するのが良さそう

TLS1.0/1.1 は、暗号鍵の長さなどによって通信内容が漏洩するなどの脆弱性があり、主要ブラウザはすでにTLS1.0/1.1を無効化している

www.cybertrust.co.jp

IPAもTLS1.0/1.1 を「非推奨」としている

www.claris.com

だけど、それなりに古いブラウザのユーザはいるので、その辺はどれくらい売上に影響があるかと、リスクのバランスを取って決めたら良いのかな...? って思ってる(できる限り対応すべきではあるけど

他の会社はどうしてるの? ビジネス側を説得するのに知りたい

楽天やYahoo! は2018年にTLS1.0/1.1をすでに無効化している

個人情報扱ってないから問題ないかな?

通信内容が漏洩すると「中間者攻撃」のリスクもあるので、できるだけ対応した方が良いと思っている

中間者攻撃?

サーバとブラウザの間で通信を読み取って、改ざんしたり、情報を詐取する攻撃のこと

実際にTSL1.2以前のバージョンにおいて、中間者攻撃が可能な脆弱性が発見されている

www.security-next.com

AWSのELBで TLS1.0/1.1 を無効化するにはどうしたらよいの?

リスナーの SSL Policy を ELBSecurityPolicy-TLS-1-2-Ext-2018-06 にすれば、TLS 1.2 以上のみ許可するようになる

デフォルトの ELBSecurityPolicy-2016-08 は TLS 1.0/1.1 も許可するので注意

docs.aws.amazon.com

Terraform でランダムなパスワードを設定する方法

方法

モジュール random_password を使う

ほぼ同じ random_string もある。 random_password は生成結果を標準出力に出さないため、 random_string よりセキュア

使い方

resource "random_password" "master_password" {
  length  = 10
  special = false
}

パラメータ

  • length: 文字列長

他はオプション

よく使うのは、 special (!@#$%&*()-_=+[]{}<>:? の使用可否 ) くらいか (大体 false にする )

使いみち

AWS Systems Manager パラメータストアをterraformで定義したいけど、パスワードの値はterraformに持たせたくないので、仮のパスワードをひとまず入れておきたい場合などに使う

resource "random_password" "db_password" {
  length  = 10
  special = false
}

resource "aws_ssm_parameter" "db_password" {
  name = "/hoge/db/passowrd"
  type = "SecureString"
  value = random_password.db_password.result

  lifecycle {
    ignore_changes = [value]
  }
}
  • lifecycle ignore_changes = [value] を設定することで、terraform上では、value は上書きされなくなる
  • terraform apply 実行後、管理コンソールなどから本当のパスワードを手入力する

Terraform と Terraform Cloud について理解し直す

久しぶりに使うことになったのでアンラーニングを兼ねてメモ

Terraformって何?

クラウドインフラに対するinfrastructure as code。クラウドインフラの設定をコード化して、保存/実行できる

www.terraform.io

infrastructure as codeできると何が良いの?

コードにすることで、インフラの更新をVCSに残せる

VCSに残せると、変更履歴が残せる。インフラの変更をレビューしやすくなる。CIに乗せることができる。とメリットが多い

Terraformがinfrastructure as codeできるインフラって何があるの?

AWSとかGCPとかAzureとかいろいろ。Datadogの設定とかもTerraform化できる

AWSだとCloudFormationもあるけど?

肌感覚だとTerraform使っている人が多い印象。ぶっちゃけどっちでも良いと思うのでチームで決めれば良いと思う。弊社はTerraform

Terraformの流れ

  • インフラの設定を .tf ファイルに独自言語で記述
  • terraform plan で変更内容を確認
  • terraform apply で実際に変更

変更されたあと、生成されたリソースのIDなどの具体的な情報が tfstate というテキストファイルに保持される。ここがポイント

tfstateをVCSで管理することは非推奨

VCS を用いることは非推奨とされています。これは例えば複数人が git clone して同時に terraform apply を実行してしまった場合などに、競合が発生する可能性があるからです。 tfstate は常に唯一無二のファイルがどこかに存在し、誰もがそのファイルを参照する必要があります。

chroju.github.io

んじゃどうやって管理するの?

Terraformでは、backendという設定でtfstateの管理場所を設定できる。AWSだと大体S3にupして管理するのが主流っぽい

ただしS3にしても、複数箇所で同時にTerraform applyが実行されると、変更がコンフリクトする可能性がある

これを回避するため、S3+何かしらのデータベースでロックして、1回に1人しかTerraform applyできないように設定することもできる(らしい)

ここまでやれば安心だけど、Terraformのためにインフラを作る必要があったりして大変

そこで Terraform Coud

Terraform Cloudは公式のterraformのSaaS

www.terraform.io

できることは大まかに

  • tfstateの管理
  • Terraformコマンドの実行
  • TerraformのCI化(VCSの更新をトリガに terraform apply を実行できる)
  • APIキーなどの秘匿情報のセキュアな管理

エンジニアはtfファイルを書いて、github に push すれば、あとのことはTerraform Cloudがやってくれるので大変便利

今現在、フリープランだとチーム5名まで無料なので、小規模なチームだったらとりあえず入れちゃって良いんじゃないかなという理解

www.hashicorp.com

無料版と有料版の違いはこちらも参照

qiita.com

TerraformのLinerやセキュリティスキャナーについて

このあたりをgithub Actionsで動作させると幸せになれるはず

tflint

Terraformのlinter。存在しないインスタンスタイプなど、Terraformではエラーにならないが、NGなものをチェックして指摘してくれる

github.com

tfsec

Terraformのセキュリティスキャナー。機密情報が紛れていないかとか、AWSやGCPのベストプラクティスに違反していないかチェックしてくれる

github.com

まとめ

  • Terraformはクラウドインフラのinfrastructure as code
  • Terraform Cloudをつかうことで、面倒くさいところをSaaSに移譲できる
  • Linerやセキュリティスキャナーがあるので使おう

dockle の警告 `CIS-DI-0008` をどのように処理すべきか考えた

dockle って?

コンテナイメージのセキュリティチェックツールです。ビルドしたImageを通すと、セキュリティ的に問題がある部分を指摘してくれます

github.com

警告 CIS-DI-0008 って?

これですね

CIS-DI-0008: Remove setuid and setgid permissions in the images

github.com

この警告が出てきた! という記事は多いのですが、それをどういう風に処理した。という記事がなかったので書いてみました

setuidsetgid って?

linuxにおける特殊なアクセス権限で、

  • setuid: ユーザが 所有者の権限 でそのファイルを実行できる設定
  • setgid: ユーザが 所有者グループの権限 でそのファイルを実行できる設定

なので、rootユーザが所有者や所有者グループのままの実行ファイルがあると、それをroot権限で実行できてしまうようです

dockleで警告を受けた、実行ファイルってどれ?

base Imageが debian-slim の時に、これらの実行ファイルについて警告が出ました

/bin/umount
/usr/bin/expiry
/bin/su
/usr/bin/wall
/usr/bin/gpasswd
/bin/mount
/usr/bin/passwd
/sbin/unix_chkpwd
/usr/bin/chage
/usr/bin/chsh
/usr/bin/chfn
/usr/bin/newgrp

それぞれどんな実行ファイルなの?

調べたらこんな感じでした

  • /bin/umount: ファイルシステムのマウントを解除する
  • /usr/bin/expiry: パスワードの有効期限ポリシーを定義する
  • /bin/su: ユーザ切り替えコマンド
  • /usr/bin/wall: ログインしている端末に一斉に通知を送る(昔共有マシンを落とす時とかによく使いましたね...
  • /usr/bin/gpasswd: ユーザが所属するグループを管理する
  • /bin/mount: ファイルシステムをマウントする
  • /usr/bin/passwd: パスワードを管理する
  • /sbin/unix_chkpwd: パスワードをチェックする
  • /usr/bin/chage: パスワードの有効期限を設定する
  • /usr/bin/chsh: ログインシェルを変更する
  • /usr/bin/chfn: ユーザ情報を管理する
  • /usr/bin/newgrp: 新しいグループにログインする

それで、どうしたの?

上記実行ファイルすべての setuid、setgid を外すことにしました

# 特殊なアクセス権、suid, sgid を排除
# 想定外のアクセス権でコマンドを実行されることを防ぐ
#
# see: https://github.com/goodwithtech/dockle/blob/master/CHECKPOINT.md#CIS-DI-0008
RUN \
  chmod u-s /bin/umount && \
  chmod g-s /usr/bin/expiry && \
  chmod u-s /bin/su && \
  chmod g-s /usr/bin/wall && \
  chmod u-s /usr/bin/gpasswd && \
  chmod u-s /bin/mount && \
  chmod u-s /usr/bin/passwd && \
  chmod g-s /sbin/unix_chkpwd && \
  chmod g-s /usr/bin/chage && \
  chmod u-s /usr/bin/chsh && \
  chmod u-s /usr/bin/chfn && \
  chmod u-s /usr/bin/newgrp

そんなことしちゃって大丈夫なの?

それぞれ、Dockerコンテナを運用する限り、普通は使わないコマンドだと思いました

それに、放置して脆弱性が発生した場合に面倒だと思い、最初から上位権限で実行できないようにしたほうが早いなと思ったので

(まだ本番運用はしていないけど多分大丈夫なはず...)

個人的なdockleの運用方針

  • warnも全部潰す(下手に例外を作るとそのほうが最終的に面倒くさくなるので)
  • 警告については、それぞれ公式のgithubに対応方法が書かれているのでそれに従う(めっちゃ親切ですね)
  • (まだやれていないけど)CIに仕込んで、毎回実行して失敗した場合には修正するようにしたい

参考

👇 setuidsetgid の解説ページ

linuxg.net

そんなかんじです

パイプを使うと最後の処理が成功すると戻り値は0になるので、`pipefail` を使ったほうが事故が少なくなるよというお話

コード例

#!/bin/bash -e

exit 1 | echo "a"
echo "b"

実行結果

a
b

-e オプションで、行単位で失敗したら終了するはずなのに、 echo "b" が実行されてしまっている...!

解説

pipeを使う場合、一番右側の処理の戻り値で行の成功/失敗が判定される

exit 1 | echo "a"
echo $? # <= 0

対策

set -o pipefail を使う

  • pipefail は、パイプの左側の処理で失敗した場合、戻り値を失敗扱いにする処理

コード例

#!/bin/bash -e

set -o pipefail

exit 1 | echo "a"
echo "b"

実行結果

a

パイプの右側も実行されるが、行全体としては失敗扱いとなり、 -e オプションにより処理が終了する

参考

DockerfileのLintツール hadolint でも、以下のようにして RUN 実行時に pipefail が設定されるように推奨している

SHELL ["/bin/bash", "-o", "pipefail", "-c"]

see: https://github.com/hadolint/hadolint/wiki/DL4006

UnicornとNginxの接続方法は、UNIXドメインソケットとリバースプロキシの2つの方法がある

UNIXドメインソケット

単一マシン上の高効率なプロセス間通信に用いられる機能・インターフェースの一種である

UNIXドメインソケット - Wikipedia

  • ファイルシステムを介してプロセス同士の通信を行う仕組み
  • 普通にリバースプロキシするより高速だが、ファイルシステムを介するので単一マシン上でしか動作できない
  • UnicornとNginxが同一マシン上にある場合は、これ一択

Railsの場合の設定方法

config/unicorn-config.rb

# Nginxとはソケット通信
listen "/tmp/unicorn.sock"
  • listen + ソケットのPATH でリスナーを設定する

nginx側の設定

http {
  upstream unicorn {
    server unix:/tmp/unicorn-cms.sock
  }
  location / {
     proxy_pass http://unicorn;
  }
}
  • serverunix:${path} で、unicornで設定したUNIXドメインソケットのpathを設定する
  • proxy_pass でリクエストをUNIXドメインソケットに渡す

リバースプロキシ

普通にTCP通信で、unicornにリクエストを投げる方法

  • UNIXドメインソケットより低速
  • UnicornとNginxが別マシンの場合はこちら

Railsの場合の設定方法

config/unicorn-config.rb

listen 3000
  • listen + ポート番号 でリスナーを設定する

nginx側の設定

http {
  upstream unicorn {
    server exsample.com:3000
  }
  location / {
     proxy_pass http://unicorn;
  }
}
  • serverドメイン:ポート番号 で、Unicornがある転送先を設定
  • proxy_pass でリクエストを転送先に渡す

DockerでUnicornとNginxが別コンテナの場合

別コンテナでもボリューム経由でファイルを共有できるので、UNIXドメインソケットを使える

やっぱりコンテナ間で通信より、早いらしいので、UNIXドメインソケットを使ったほうが良い

qiita.com

参考

qiita.com

www.honeybadger.io