kouの技術的メモ

学習した内容の定着やアウトプット用に開設しました

ログイン周りとセキュリティ関連の情報収集

SPAでのログイン・ログアウト機能実装方法のベストプラクティスが分からなかった事と、脆弱性についての知識が不足しているので情報収集。

session認証とトークン認証の違い。

  • cookiesを使ったsession認証
    • Cookieとは、Webサーバーがクライアント(PC等)に預けておく極小さなファイル。クライアントがWebサーバーに初めて接続(Login)した際に、Webサーバーがクライアントに対してCookieファイル(SessionID)を発行し、HTTPレスポンスのヘッダを利用して送ります。その際に発行されたSession情報(SessionID)にはログイン情報が含まれます。
    • サーバにアクセスする度にクライアント側のリクエストヘッダに含まれるCookieファイル(session ID)とサーバ側に保存されているSessionID情報を比較して合致した際に認証されたとみなされる。
  • トークンを使った認証
  • Tokenによる認証も、Cookieと同じく、まずはクライアントがWebサーバーに接続(Login)した際に、Tokenが返されます。ただCookieと違ってあ、サーバーにその情報を保存はしません。「認証に成功した」というToken(いわゆる「認証情報」)を持ち、リクエストする度にリクエストヘッダにTokenを含んで送っています。

また、railsを使った認証系のセキュリティは以下も参考にします。

Railsアプリの認証システムをセキュアにする4つの方法(翻訳)|TechRacho(テックラッチョ)〜エンジニアの「?」を「!」に〜|BPS株式会社

実装方法の検討

JWTとは

cookieとは別によくSPAで認証系で使われる技術としてJWTというものがある。

JWT・Cookieそれぞれの認証方式のメリデメ比較 - Qiita

Cookieはサーバ側でsession情報を持つ。CookieにはsessionIdを入れており、リクエストの都度sessionIdに紐づくサーバ側sessionデータを参照している。 Tokenはクライアント側で「認証に成功した」という情報(=Token)を持ち、リクエストの都度それを送る。

SPAの認証方法

SPAを使った認証系は(OauthやSSOを除く)大まかに4つに別れる

  1. サーバー: Sessionトークンを発行 -> クライアント: Cookieに保存
  2. サーバー: Sessionトークンを発行 -> クライアント: Web Storageに保存
  3. サーバー: JWTを発行 -> クライアント: Cookieに保存
  4. サーバー: JWTを発行 -> クライアント: Web Storageに保存

4の様にSPAではフロント側にセッション情報やJWTを保存する際HTML5の新機能local storage(web strage)と呼ばれる領域を使ってブラウザ側に保存するパターンも多いらしいのですが、下のリンクの様にセキュリティ上脆弱性を孕む(scriptを使うと簡単に中身を覗ける)様です

HTML5のLocal Storageを使ってはいけない(翻訳)|TechRacho(テックラッチョ)〜エンジニアの「?」を「!」に〜|BPS株式会社

local storageはあらゆるJavaScriptコードから自由にアクセスできてしまいます(データ保護的なものはありません)。これはセキュリティ上非常に重大です。

webサイトに悪意のあるJavaScriptが埋め込まれたサイトの外部リンクなどを使って、local strage上のデータを全てコピーする事もできます。

とのことでXSSクロスサイトスクリプティング)攻撃で簡単に情報を取られる危険性があり、セキュリティ上認証情報をweb storage保持しておくのは望ましくないとのことで、2と4はやめます。

残る1と3ですが、cookieでhttpOnly属性をtrueにすれば、JavaScriptからcookieにアクセスできなくなるのでXSSの被害を緩和できます。(JavaScript の Document.cookie API にはアクセスできなくなる)

調べた結果JWTである理由も今回は特に無いと思いますので、

普通に1のセッションをcookieを使って実現したいと思います。

また、その場合CSRFは防げないためcookieのセッションIDとは別にCSRFトークンも実装します。

Railsの場合、例えばフォームでpostするときは自動的にトークンが生成されブラウザサーバ間でCSRF対策がなされるのですが、APIの場合は自分で対策しなくてはいけません。

最初はCORS (Cross-Origin Resource Sharing)の設定で許可されたオリジン以外のからアクセスは弾かれるので、CSRFトークンは必要ないのではないかと思いましたが、CORSはサーバへのアクセスを防ぐものではなく、サーバからのレスポンスをクライアント側で読めなくするものなので、CSRFによるサーバへの不正なリクエスト自体は成功してしまいます(例えば罠サイトで情報を書き換えようとして不正なPostリクエストを送った場合でも、レスポンスは帰ってこないが、このPost自体は成功してしまう)

APIドメインが認証用Cookieドメインと同じ場合:CookieのSameSite属性をLaxに指定 APIドメインが認証用Cookieドメインと異なる場合: CookieのSameSite属性をNoneに指定し、Secure属性を付与する CSRF tokenを利用する

cookieの属性

cookieはいろんな属性がありますが、セキュリティに関連する設定属性は以下になります。

  • Secure属性
  • trueの場合CookieHTTPS通信でしか送受信されない。
  • HttpOnly属性
  • JavaScriptからcookieにアクセスや操作をすることが出来ない
  • SameSite属性
    • Lax、Strict、Noneの3つの属性があり、リクエスト元によってそのCookieを送信するか否か決める。

参考にした書籍、情報

徳丸 浩 『安全なWebアプリケーションの作り方』

安全なウェブサイトの作り方:IPA 独立行政法人 情報処理推進機構

セキュリティ用語

XSS(クロスサイトスプリクティング)

ウェブアプリケーションの中には、検索のキーワードの表示画面や個人情報登録時の確認画面、掲示 板、ウェブのログ統計画面等、利用者からの入力内容や HTTP ヘッダの情報を処理し、ウェブページとし て出力するものがあります。ここで、ウェブページへの出力処理に問題がある場合、そのウェブページに スクリプト等を埋め込まれてしまいます。この問題を「クロスサイト・スクリプティング脆弱性」と呼び、こ の問題を悪用した攻撃手法を、「クロスサイト・スクリプティング攻撃」と言う。

古い情報ではありますがIPAによると

クロスサイト・スクリプティング脆弱性の届出件数は、他の脆弱性に比べて多くなっています。この脆 弱性については、届出受付開始から 2014 年第 4 四半期までに、ウェブサイトの届出件数の約 5 割に相 当する届出を受けています。

との事なので非常に被害の多い攻撃法と言えます。

被害の例

  • ブラウザが保存している Cookie を取得される
    • Cookie にセッション ID が格納されている場合、利用者へのなりすましにつながる(いわゆるセッションハイジャック
    • Cookie に個人情報等が格納されている場合、その情報が漏えいする
  • 任意の Cookie をブラウザに保存させられる
    • セッション ID が利用者に送り込まれ、「セッション ID の固定化」 攻撃に悪用される

対策

  • ウェブページに出力する全ての要素に対して、エスケープ処理を施す。

    • ウェブページを構成する要素として、ウェブページの本文や HTML タグの属性値等に相当する全て の出力要素にエスケープ処理を行います。エスケープ処理には、ウェブページの表示に影響する特別 な記号文字(「<」、「>」、「&」等)を、HTML エンティティ(「<」、「>」、「&」等)に置換する方法が あります。また、HTML タグを出力する場合は、その属性値を必ず「"」(ダブルクォート)で括るようにし ます。そして、「"」で括られた属性値に含まれる「"」を、HTML エンティティ「"」にエスケープします。 脆弱性防止の観点からエスケープ処理が必須となるのは、外部からウェブアプリケーションに渡さ れる「入力値」の文字列や、データベースやファイルから読み込んだ文字列、その他、何らかの文字列 を演算によって生成した文字列等です
  • URL を出力するときは、「http://」や 「https://」で始まる URL のみを許可する

    • URL には、「http://」や「https://」から始まるものだけでなく、「javascript:」の形式で始まるもの もあります。ウェブページに出力するリンク先や画像の URL が、外部からの入力に依存する形で動的 に生成される場合、その URL にスクリプトが含まれていると、クロスサイト・スクリプティング攻撃が可 能となる場合があります。たとえば、利用者から入力されたリンク先の URL を「<a href ="リンク先の URL">」の形式でウェブページに出力するウェブアプリケーションは、リンク先の URL に「javascript:」 等から始まる文字列を指定された場合に、スクリプトを埋め込まれてしまう可能性があります。リンク先 の URL には「http://」や「https://」から始まる文字列のみを許可する、「ホワイトリスト方式」で実装 する様にします。
  • 入力された HTML テキストから、スクリプトに該当する文字列を排除する。

    • 入力された HTML テキストに含まれる、スクリプトに該当する文字列を抽出し、排除します。抽出した文字列の排除方法には、無害な文字列へ置換することが推奨されます。たとえば、「<script>」や「javascript:」を無害な文字列へ置換する場合、「<xscript>」「xjavascript:」のように、その文字列に適当な文字を付加します。他の排除方法として、文字列の削除が挙げられますが、削除した結果が危険な文字列を形成してしまう可能性があるため、一般的に推奨されません。
  • HTTP レスポンスヘッダの Content-Type フィールドに文字コード(charset)を 指定する。

  • HTTP のレスポンスヘッダの Content-Type フィールドには、「 Content-Type: text/html; charset=UTF-8」のように、文字コード(charset)を指定する。 一部のブラウザにおいては、HTML テキストの冒頭部分等に特定の文字列が含まれていると、必ず特定の文字コードとして処理されるという挙動が知られています。 Content-Type フィールドで文字コードの指定を省略した場合、攻撃者が、この挙動を悪用して、故 意に特定の文字コードをブラウザに選択させるような文字列を埋め込んだ上、その文字コードで解釈 した場合にスクリプトのタグとなるような文字列を埋め込む可能性があります。 たとえば、具体的な例として、HTML テキストに、 「+ADw-script+AD4-alert(+ACI-test+ACI-)+ADsAPA-/script+AD4-」という文字列が埋め込まれた 場合が考えられます。この場合、一部のブラウザはこれを「UTF-7」の文字コードエンコードされた文 字列として識別します。これが UTF-7 として画面に表示されると 「<script>alert('test');</script>」として扱われるため、スクリプトが実行されてしまいます。
  • Cookie 情報の漏えい対策として、発行する Cookie に HttpOnly 属性を加える
  • 「HttpOnly」は、Cookie に設定できる属性のひとつで、これが設定された Cookie は、HTML テキスト 内のスクリプトからのアクセスが禁止されます。これにより、ウェブサイトにクロスサイト・スクリプティン グから守れます

CSRF

Cross Site Request Forgeriesクロスサイト・リクエスト・フォージェリ。

XSSと似ているが異なるものとしてCSRFがあります。 XSSがブラウザ側で不正なスクリプトを用いて不正を行うものなのに対して、CSRFCSRF脆弱性を利用してサーバ側に不正な攻撃用リクエストを投げる攻撃です。

CSRF攻撃の例
  1. ユーザAがサイトXにログインしている(つまりユーザAはサイトXのcookieセッション情報を持っている)
  2. ユーザAは罠用URLが施された別の攻撃用サイトのURLをクリックしてしまう。
  3. 攻撃用サイトのクリックにより、サイトXのユーザAの登録個人情報を書き換える不正情報がPostされる。
  4. ユーザAはすでにログインしているため、サイトXサーバにcookie情報が正しいと認識されPostが成功する。 5.登録個人情報の書き換えが成功してしまう。

保持しているcookieはリクエスト先のサーバが発行元であれば、リクエスト元が正規のものであろうが、別のサイトだろうがそのサイトで生成されたcookie情報を送ってしまうので、上記の攻撃が成立してしまう。

被害の例

ログイン後の利用者のみが利用可能なサービスの悪用や編集可能な情報の改ざん、新規登録 不正な送金、利用者が意図しない商品購入、利用者が意図しない退会処理 等

各種設定の不正な変更(管理者画面、パスワード等)、掲示板への不適切な書き込み

対策

  • 処理を実行するページを POST メソッドでアクセスするようにし、「hidden パラメータ」に秘密トークンが挿入されるようして実行ページではその値が正しい場合のみ処理を実行する。
    • CSRFが成功するのは、cookieだけでは誰のリクエストなのか判断ができない為なので、ログイン時cookieのセッションID以外の第2の識別情報(CSRFトークン)を擬似乱数生成器を用いて生成、これを重要な処理の際にサーバ側の情報と比較し、一致しなければ処理を実行しない様にする。
    • このCSRFトークンはリクエストヘッダやリクエストボディに仕込むとよい。xssなどによりcookie情報が流出した場合、意味がなくなるため
    • 下記項目でも書くがCORSの設定では防げない。CORSによるフィルタリングはクライアント側で結果を取得できないだけなので、サーバへの攻撃リクエスト自体は成功してしまう為。
    • ちなみにRuby On Railsの場合は、Railsが提供するform_withやform_forなどのヘルパーを使うと、自動でセキュリティトークン(authenticity_token)を仕込んでくれる

同一生成元ポリシーとCORS

同一生成元ポリシー(Same-Origin Policy, SOP)

CSRFなどのセキュリティ攻撃を防止するためにブラウザに備わっている仕組みで、異なるオリジンのリソースへのアクセスに制約をかけるもの。 同一生成元ポリシーによりWebページを生成したドメイン以外へのHTTPリクエストはできない。 異なるオリジンの場合あくまリクエストでして帰ってきたレスポンスをブラウザで読めなくするものなので、サーバへのリクエスト自体は成功してしまうので、CSRFは防げない。

ブラウザがリクエストを異なるオリジンのサーバーに投げる際にHTTPレスポンスヘッダーに以下の様な情報を乗せて送る。 Origin: http://hoge.com

因みにブラウザで実行されたJavaScriptからAPIサーバにアクセスする場合、ブラウザ側のオリジンはHTMLやCSS、JSを読み込んだフロントエンド側サーバのオリジンとなる。

この制約はCORS(Cross-Origin Resource Sharing) と呼ばれる仕組みを利用することで一部解除することができる。

オリジンとは

スキーム + ホスト + ポート の組み合わせを オリジン(Origin)という スキーム + ホスト + ポートのどれかが違うことをオリジンが異なると表現します。

例えば以下のURLの場合はそれぞれこの様に対応します。

http://hoge:80/directry1

  • http
    • スキーム
  • hoge
    • ホスト
  • 80
    • ポート番号

以下は同一オリジンになります。 http://hoge:80/directry2

以下は異なるオリジンになります。 http://fuga:80/directry1 http://hoge:8080/directry1

CORS

Cross-Origin Resource Sharingオリジン間リソース共有

同一生成元ポリシーのアクセス制約を一部解除し、異なるオリジン間でリソースを共有するための仕組み。

SPAの場合は、フロントエンド側のhtml,css,Javascriptを生成したオリジンとAPI側のオリジンが異なることが多い。

サーバ側でHTTPレスポンスヘッダにAccess-Control-Allow-Origin:「フロント側オリジン」を付与してあげることでアクセス制約がなくなる。

例えば、異なるオリジンにXHRやFetch API、axiosでアクセスする場合、

例えばサイトAから他のオリジンのリソースにアクセスしたい場合、ブラウザはリクエストヘッダに以下の様にアクセス元のオリジン情報を付加します。

Origin: http://site-a.example.com

これに対し、site-b 側で、site-a へのアクセスを許可する場合、下記の様なレスポンスヘッダを返します。

Site-B → Site-A

Access-Control-Allow-Origin: http://site-a.example.com

参考元サイト: http://www.tohoho-web.com/ex/cors.html

ECサイト - 法律関係の勉強

知らずに違法行為をしてしまうのもまずいので、まずEコマースサイトを構築する際に必要な法律関係について情報を収集しています。

ECサイト関係の法律と内容と具体的な施策について。

  • 2021年4月1日から総額表示義務が適用される。
    • 消費税込みの価格を表示しなければならない。
    • 今作っているECサイトは現時点では扱う物は全て軽減税率対象商品(消費税率8%)
    • 軽減税率制度は今のところ実地期限が設けられていないため、ある程度の長期間この税率が続くと思われるため消費税8%固定でも良い気がする。後々扱うものが増えていったときのため、消費税は変数化してそれぞれ定数定義をしても良いかも。
  • 電子契約法の要点⑴消費者の操作ミスの救済
    • 民法95条3項「錯誤が表意者の重大な過失によるものであった場合には、次に掲げる場合を除き、第1項の規定による意思表示の取り消しをすることができない。」に基づくと、消費者側のスマホ、パソコン操作ミスによる意図しない申込みによってECサイトで誤注文した場合取り消すことができず、消費者が不利になってしまう。
    • このため電子契約法ではB2Cの電子商取引において、事業者側がパソコン等の画面上に申込み手続きを設定するような契約については、事業者側が、消費者の申込み内容などの意思を確認するための適切な措置を設けていない場合には、原則として、操作ミスによる契約を無効とすることしている。
    • 具体的な対策
      • 送信ボタンが存在する画面上に意思表示の内容を明示し、そのボタンをクリックすることで意思表示となることを、消費者が明らかに確認できる画面を設置する。
      • 最終的な意思表示となる送信ボタンを押す前に申込みの内容を表示し、そこで訂正する機会を与える画面を設置する。
  • 電子契約法(2) 契約の成立時期
    • 民法第526上の「隔地者間の契約は承諾の通知を発したる時に成立」に基づくと、通知を送った時点で契約が成立する。
      • つまりメールでの契約承諾通知の場合は、メールを送信した時点で成立する事になり、消費者が利用しているメールサービスのサーバ故障が発生していて、承諾通知メールが受信されていない、あるいは読み取り不可能となっている場合でも契約の成立となる。
      • これでは消費者不利なので、電子契約法では到達主義が取られている。つまり 契約が成立するタイミングは「メールが、メールボックス中で読み取り可能な状態になったとき」となります。つまり、消費者がメールを開封する必要はなく、メールを受信し、読み取り可能になった時点で契約が成立します。
    • 具体的な対策
      • 例えばSendGridなどのサービスでは送信したメールの状態を確認できるらしい(Email ActivityがDeliveredになったら相手のめーるサーバには到達している)ので、慎重を期すならそれを利用する。

インフラの選定 

今回はAWSを使ってみたいのですが、EC2やらECSやらlambda、fargate等どれを使えばいいか検討したいと思います。

クラウドインフラ

  • EC2
  • 特徴
    • おそらく最も広く使われている。通常のサーバ構成。
    • ローカル開発環境はコンテナ、本番環境EC2という構成は多い。
    • 過去に自分も使った事があるので、ある程度知っている。
  • ECS
  • 特徴
    • コンテナ

CI/CD関連

  • CI(自動ビルド、自動テスト)
  • 現時点で広く使われているCircleCIの導入
  • CD(自動デプロイ)
  • railsでよく使われているcapistranoの導入

もし余裕があれば AWSの設定画面で手動で設定していくのが面倒臭かったので、インフラ構成管理ツールも後々導入しようとおもいます。 ansibleとterraformの違いがよくわからなかったのですが、どうも - インフラ側の構成管理 - terraform - アプリ側ミドルウェアなどのインストール - ansible

という事らしい

ECサイトの構築(React+Rails)

ECサイトを0から作ることになった。 技術習得も兼ねているので構成はフロントReact+Railsを使ってSPAで今っぽいサイトを作って行こうと思います。

SPAでフロント、サーバサイド、インフラの全てを設計から実装まで自分一人でするのは初めて、かつ決済にStripeを初めて使ってみたり色々詰まるところはあると思うので、忘備録的に書いていこうと思います。

こんな流れになるかと思います

Reactのベストプラクティス - PresentialコンポーネントとContainerコンポーネントとAtomic Design

コンポーネント設計

役割によるコンポーネントの分離

Presentialコンポーネント

見た目の部分を担当するコンポーネント。どのようにUIを描画するかの部分に責務を持ち、Propsを受け取りそれをどのように表示するかを担当する。 state(状態)は持たないことが推奨され、単に値(props)を受け取りそれをどう表示するかだけの役割のため、SFC(Stateless Functional Component)が適しています。 またpropsに渡す値として関数を渡すことも可能。

データの流れとして、Containeコンポーネントの子コンポーネントとして実装される。

Containerコンポーネント

データや状態、挙動を子コンポーネントであるPresentialへ提供するコントローラ的な役割。 Containerコンポーネント自身は具体的なDOM表現は持たない。 データに関して集中管理する存在。

Atomic Design

  • Atomos 原子
    • UIの最小単位。例えば、検索フォームなら、見出し、入力エリア、送信ボタンで分割されるがこれらのパーツがAtomosに相当する。
  • Molecules 分子
    • 複数のAtomosから構成される。見出し、入力エリア、送信ボタンのAtomosで構成された検索フォームがMoleculesに相当する。
  • Organisms 有機
    • AtomosやMoleculesから構成される。直接Atomosを参照することもある。例えばヘッダーはロゴ(Atomos)、検索フォーム(Molecules)、グローバルナビゲーション(Molecules)から成り立ち、これがOrgasmsに相当する。
  • Templates テンプレート
    • ワイヤーフレームのように、ページに表示するための実データが反映される前のページ全体のレイアウトの構造や骨格を表現する。
  • pages ページ
    • テンプレートに対して、実データを反映させた状態。ページ全体の実態。プログラミング的に言えばページはテンプレートのインスタンスに相当する。

AtomicDesignの概念を各コンポーネント実装へ反映する

  • Atomos 原子
    • 値を受け取って描画するだけなので、SFCとして構成されることが多い。Presentialコンポーネント
  • Molecules 分子 - 実装上Atomosと大差ない。受け取ったPropsを子コンポーネントのAtomosにそのまま流す役割。Atomosが必要としているデータが不変の場合、固定データをMolecules上で定義しても良い。Presentialコンポーネント
  • Organisms 有機

    • 複数のAtomosやMoleculesを並べて、親のPropsをそのまま流し込み描画することが主な役割。必要なデータを親コンポーネントから受け取りそれを元に描画するPresentialコンポーネント。ただかなり具体性が必要なレベルの構成要素なので、Containerコンポーネントの役割を担う場合もある
  • Templates テンプレート,pages ページ

  • エントリーポイントからデータが仕込まれたりするのでContainerコンポーネントに相当する。コンポーネントファイルとして存在するのではなく、エントリーポイントがここの役割を担っている場合が多い。

babelとwebpack

babel

超簡単に言うとJavaScriptのコードを新しい書き方から古い書き方へと変換するツール。

JavaScriptは頻繁に言語仕様が変化していて、最新の書き方をすると一部のブラウザしかその仕様に対応していないと言う状況がよくある。 最新のECMAScript構文やJSX構文で書いても、それを古い構文に変換して最新の言語仕様に対応していないブラウザに読み込めるように古い構文の形式へ変換してあげる(フォールバックしたコードへの変換)役割をするのがbabel。

webpack

超簡単に言うと 複数のファイル(.jsや.css等)を一つのファイルにまとめて出力するツール。いわゆるバンドラー。 標準のwebpackはjsファイルしか束ねられないので、他のファイルを束ねる時は対応したローダーを設定に記載して使う。 例えばtxtファイルも束ねたい時はraw-loaderを使う。

また、最新のJavaScript構文を使うとwebpackでは解釈できない可能性がありその場合ビルドに失敗する。 そこでbabel-loaderを使い、babelを事前に実行する。

拡張機能設定

  • loader
    • 読み込むファイルに対して実行する。babel-loader等
  • plug-in
    • バンドルされた出力ファイルに対して実行する UglifyJsPlugin(ファイルの圧縮)、HtmlWebPackPlugin(bundel.jsが読み込まれた雛形htmlの生成)等。

webpac-dev-server

webpack開発用のローカルサーバー機能を提供するライブラリ。