- golangでセキュアな認証システムを実装した
- 認証には goth を使っており、今回は通常のログインとTwitterログインを実装した
- SPAと組み合わせて使えるような REST API を意識して開発した
- 自動でログインされるようにcookieを保持している
- DB は Mock で作っており、メモリー上に情報を保持している(そのためSQL Injectionについて詳しく書いていないが、余力があれば実装していきたい)
- 以下に脆弱性対策やセキュティーを意識した開発をする上で最低限押さえておくべき対策を列挙した
- 今回はGolangを用いて開発しており、
gorilla
関連のpackageを使用している gorilla
関連のpackageは様々な機能を小分けで提供してくれているため必要な物を組み合わせて安全に開発を行える
- セッション情報は
Cookie
やJWT
などに保存する- Cookie <- 場合にもよるがこっちの方が良さそう?
Cookie
を使う場合はhttpOnly
やsecure
などのオプションから安全な設定を追加できるため、XSS
からの攻撃を防ぐことできるが、CSRF
は自前で実装して防ぐ必要がある- 安全に実装するなら
Cookie
のような気がする
- JWT
JWT
はブラウザのLocalStrage
に保存することができ、JWT
に情報を持たせることができるのでServer
をステートレスに保つことができるLocalStrage
はSame-Origin
の場合のみでしか、I/O処理を行うことができないためCSRF
の問題はないが、XSS
によって情報を抜き取られる可能性があるXSS
を100%含まないと言い切れるサイトはない?(https://techracho.bpsinc.jp/hachi8833/2019_10_09/80851)
- Cookie <- 場合にもよるがこっちの方が良さそう?
- Cookie の扱いに気をつける
- Cookie を信用しすぎない設計にする
- ユーザー情報の編集などの個人情報の編集には必ず Password を求めるようにする
- 予測可能な情報をSessionIDに指定してはいけない
- IDや日時など推測可能な値をSessionIDに指定すると、簡単に推測されて不正に情報をとられ、ログインなどの処理が可能になる
- また推測可能な値をハッシュ化して指定することもよくない(時間をかければ推測される可能性があるため)
- そのためSessionIDの生成には、言語が指定しているプログラムやフレームワークなどの機能を使うと良い
- Goでは
gorilla/sessions
を使うと楽
- SessionIDの固定化攻撃を防ぐ
- SessionIDの固定化攻撃とは攻撃者が被害者に対して、SessionIDをなんらかの方法で指定することにより、指定されたSessionIDで被害者がログインすると、攻撃者は指定したSessionIDにより、ログイン状態となる脆弱性である。
- また、ログインしていなくてもユーザーが入力した情報をSessionに逐一保存している場合、固定化攻撃によりSessionIDを指定されると、そのSessionに情報が蓄積されることで攻撃者に情報が抜き取られる可能性がある
- 多くの場合は心配ないが、セッションアダプションというセッションを外部から指定できるような機能を持っている言語(PHPなど)で起きやすい。しかし、基本的にこれらの機能はデフォルトでfalseになっているはずなので心配はいらないはずである。
- Cookie に SessionID を保存する(URLに保存しない)
- 認証成功後に SessionID を変更する(変更できない場合はTokenによりSessionIDの認証を行う)
- 認証前に機密情報をSessionに保存しない
- Cookieの
httpOnly
とsecure
をtrue
にする(httpOnly
はJSからアクセス不可能にするためで、secure
はhttps
でのみCookieを扱うことを指定する)- 開発の段階で
secure
をtrue
にしているとlocalhost
で使用できない可能性があるため、開発時はfalse
で良い(公開する時にはtrue
にすること)
- 開発の段階で
-
CORS
をちゃんと設定する(オリジン間リソース共有 (CORS))Access-Control-Allow-Origin
... 許可するOriginを指定する(デフォルトは同じOriginが指定される)Access-Control-Allow-Methods
... 許可するHTTP Method
を指定する(GET, POST, OPTIONS, HEAD
など)Access-Control-Allow-Headers
... 許可するヘッダーを指定する。プリフライトリクエストのレスポンスで使用される。(Content-Type, Authorization
など)Access-Control-Allow-Credentials
... 資格情報が必要なリクエストに対して、レスポンスを開示するかどうか(Cookieなどを含めるかどうか)Access-Control-Expose-Headers
... 通常は特定のHeaderしかブラウザでは参照できないため、公開したいHeaderを指定することで参照できるようになるAccess-Control-Max-Age
... プリフライトリクエストを何度も呼ぶのはオーバーヘッドになるので、このヘッダーに時間を指定することでキャッシュさせることができる- 上記の
CORS
をしっかり設定した上でCSRF Token
をレスポンスする gorilla/mux
では、mux.Route.Method
にOPTIONS
を指定することでpreflight request
を許可する
Preflight Request
とは、主にJSからのPOSTなどの副作用を保つメソッドに対するリクエストを行う時に、安全なリクエストを送るために事前にリクエストされる通信であるPreflight Request
によってAccess-Control-*
のHeader情報が検証されることで安全なリクエストを行うことができるPreflight Request
はOPTIONS
メソッドでリクエストされるので、OPTIONS
で処理するように指定する
-
CSRF
対策をする- gorilla/csrfを使うと楽
- CSRFの必要性(これで完璧!今さら振り返る CSRF 対策と同一オリジンポリシーの基礎, gorilla/csrf で安全なWebフォームを作る)
gorilla/csrf
ではDouble Submit Cookie
という方式を採用しているDouble Submit Cookie
では Token を cookie と Header でレスポンスを返し、Client では cookie と Header に Token を含めてリクエストして、cookie と Header に含まれる Token と比較して、どちらも同じ Token なら許可するというものであるDouble Submit Cookie
では cookie が変更された場合に機能しないという脆弱性が見つかっていたがgorilla/csrf
では、署名付き Token を cookie に含めることで解決しているGET, OPTIONS, HEAD, TRACE
はCSRFの検証をする必要がないはず(データの変更を行うような処理を含まないため)JWT
を使うことでステートレスなCSRF対策ができる(https://qiita.com/kaiinui/items/21ec7cc8a1130a1a103a)
- JSONを返す WEB API の場合、
Content-Type: application/json
を設定しないことでXSSが発生してしまう Content-Type: application/json
はブラウザにJSON形式のデータを返すことを伝えるヘッダーだが、これを設定しないことでContent-Type: text/html
が設定されてしまうContent-Type: text/html
が設定されると、JSONをHTMLとして返すことをブラウザーに伝えるため、HTMLとして読み込むことでXSSが発生する- XSSを防ぐには
Content-Type: application/json
の設定が必要であるが、IEの一部のバージョンではContent-Type
を書き換えられる仕様になっている - 書き換えを防ぐために
X-Content-Type-Options
を設定する
- クリックジャッキングなどの脆弱性対策として必要
- クリックジャッキングは、攻撃者が作成した偽サイトに
iframe
を使ってTwitterなどの一般的なサイトを表示し、その一般的なサイトの投稿ボタンなどの上に罠サイトへのリンクや、攻撃用のプログラムを含めておき、実行させるというものである。 - この攻撃を防ぐためには、Originまたは指定されたOrigin以外のサイトでは
iframe
を使用できないようにする必要がある。 - それが
X-Frame-Options
である - これを含めることで、知らないサイトから
iframe
を使って自分のサイトを表示される事はなくなるため、脆弱性を防ぐことができる - 実際にTwitterを
iframe
から参照しようとするとRefused to display 'https://twitter.com/' in a frame because it set 'X-Frame-Options' to 'deny'
というエラーが出力される - 参考: https://qiita.com/mejileben/items/39d897757d5c3a904721
- XSSを検知した時に無害な出力に変換する
- 各ブラウザでデフォルトでは有効になっているが、ユーザーの設定で無効にできるため、
X-XSS-Protection
を設定することで矯正させることができる
- MIME Type が変更されないように強制するために付与する
- https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/X-Content-Type-Options
SameSite
属性はSet-Cookie
に含まれる設定SameSite
にはStrict
・Lax
・None
があるStrict
... 異なるDomainのサイトに対してはCookieを付与しない(この設定が最も堅牢)Lax
... Get Method以外のリクエストにはCookieを含めない(Chrome80では何も指定しない場合にはこの値が設定される)None
... どのようなリクエストであっても、Cookieを含める(Chrome79以前ではデフォルト、Chrome80ではSecure属性をつけなければLaxになる)- 参考: https://qiita.com/ahera/items/0c8276da6b0bed2b580c
- 参考: https://blog.jxck.io/entries/2018-10-26/same-site-cookie.html
- SQL Injection
- Passwordなどの見られてはいけない重要な情報を暗号化してからDBに保存する