Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save ikaruga777/02871bb94e628fded9e7550d0db78577 to your computer and use it in GitHub Desktop.

Select an option

Save ikaruga777/02871bb94e628fded9e7550d0db78577 to your computer and use it in GitHub Desktop.

体系的に学ぶ安全なWebアプリケーションの作り方

2011年に発売された書籍の第二版が2018年6月に発売された。

目次

  1. Webアプリケーションの脆弱性とは
  2. 実習環境のインストール
  3. Webセキュリティの基礎
  4. Webアプリケーションの機能別にみるセキュリティバグ
    1. Webアプリケーションの機能と脆弱性の対応
    2. 入力処理とセキュリティ
    3. 表示処理に伴う問題
    4. SQL呼び出しに伴う脆弱性
    5. 「重要な処理】の際に混入する脆弱性
    6. セッション管理の不備
    7. リダイレクト処理にまつわる脆弱性
    8. クッキー出力にまつわる脆弱性
    9. メール送信の問題
    10. ファイルアクセスにまつわる問題
    11. OSコマンド呼び出しの際に発生する脆弱性
    12. ファイルアップロードにまつわる問題
    13. インクルードにまつわる問題
    14. 構造化データの読み込みにまつわる問題
    15. 共有資源やキャッシュに関する問題
    16. Web API実装における脆弱性
    17. JavaScriptの問題
  5. 代表的なセキュリティ機能
    1. 認証
    2. アカウント管理
    3. 認可
    4. ログ出力
  6. 文字コードとセキュリティ
  7. 脆弱性診断入門
    1. Nmapによるポートスキャン
    2. OpenVASによるプラットフォーム脆弱性診断
    3. OWASP ZAPによる自動脆弱性スキャン
    4. OWASP ZAPによる手動脆弱性診断
    5. RIPSによるソースコード診断
  8. Webサイトの安全性を高めるために
    1. なりすまし対策
    2. 盗聴・改ざん対策
    3. マルウェア対策
  9. 安全なWebアプリケーションのための開発マネジメント

1. Webアプリケーション脆弱性とは

脆弱性ってなによ

脆弱性とは「悪用できるバグ」のこと。
Webアプリケーションにとって脆弱性は身近なものである。

なんで脆弱性があるとだめなの?

脆弱性を悪用されることによる損失が結構でかい

  • 経済的損失
    • 利用者が受ける金銭的損失の補填
    • 詫び石
    • Webサイト停止に寄る機会損失
    • 脆弱性による損害を受けた取引先からの損害賠償
  • 法的要求
  • 利用者が回復不能なダメージを受けることが多い
  • Webサイト利用者に嘘をつくことになる
    • 「このサイトはセキュリティを考えてないので自己責任でね」とはいえない
  • 攻撃インフラに加担することになる

なぜ脆弱性が生まれるか

  • バグに寄るもの
    • SQLインジェクション、XSS
  • チェック機能の不足によるもの
    • ディレクトリトラバーサルとか

この本では上記2つを明確に分けている。

セキュリティバグとセキュリティ機能

セキュリティの担保にはセキュリティのバグを潰すだけでは不十分。
通信経路が暗号化されていない状態→バグじゃないけど機能として不足

このような積極的な安全性強化を「セキュリティ機能、セキュリティ要件」と呼ぶ。
非機能要件の一つとして数えられる。

参考にできるセキュリティガイドライン

安全なウェブサイトの作り方

IPAが公開している冊子

OWASP Top 10

OWASPという国際的なWebアプリケーションセキュリティの課題解決を目的とするオープンコミュニティ

いいところ

VirtualBoxの仮想アプリケーションが用意されていて、体系的に学ぶことができる。

使うツール

OWASP ZAP

httpリクエストをいじれる

FoxyProxy

FireFoxの拡張
リクエストを別portに転送できる


HTTPとセッション管理

リクエストとレスポンスメッセージ

リクエストメセージは

  • メソッド
  • URL
  • プロトコルバージョン から構成される。

レスポンスメッセージは

  • ステータスライン
  • ヘッダ
  • 1行の空行
  • ボディ(本文) から構成される。

http/2はバイナリになってる。

ステータスライン

HTTP/1.1 200 OK
プロトコールバージョン、ステータスコード、テキストフレーズに分かれる。
よく見るあれ。テキストフレーズもステータスコードに付随する。

レスポンスヘッダ

  • Content-Length
    • ボディ部のバイト数
  • Content-Type
    • MINEタイプというリソースの種類を指定。
MINEタイプ 意味
text/plain テキスト
text/html HTML文書
application/xml XML文書
application/json json
text/css CSS
image/gif GIF画像
application/pdf PDF文書
application/x-www-form-urlencoded name=valueを&でつなげたリクエスト形式

とかとか。ブラウザがこれを見てボディがなんなのか判断する。

POSTメソッドとGETメソッドの使い分け

  • GETメソッドは参照のみに用いる
  • GEtTメソッドには副作用がない
  • 秘密情報の送信にはPOSTを用いる

機密情報をPOSTで投げなければいけない理由、つまるところGETで機密情報を投げては行けない理由は

  • URLに指定指定されたパラメータがReferer経由で外部に出る
  • URLに指定されたパラメータがアクセスログに残る(POSTの本文はまず残らない)
  • URLのパラメータがブラウザのアドレスバーに表示され他人に覗かれる。
  • パラメータがついたURLをそのままSNSで共有できちゃう

とうとう。2つめについては情報安全確保支援士の午後IIにも出てきたね。

hiddenパラメータについて

Webページの確認画面はpostボタンしかない場合が多い。
でもこのボタンを押したときに送信されるデータはそのページ内に保持されていないといけない。
このとき、inputタグにhiddenパラメータを入れて、非表示の入力欄を設ける。
入力画面から確認画面の遷移時に入力内容を非表示の入力欄に入力してやるとボタン押したときにちゃんとpostできるわけ。
でも、このタグは書き換えができる。

Basic認証

単純なブラウザ認証方式
アクセスすると401:unauthorizedを返す。
ブラウザはuserとpasswordの入力を要求する。
入力して返すとブラウザはAuthorization: Basic dXNlcjE6cGFzczE=を返す。
後ろの文字列はuserとpasswordを":"でつないでbase64エンコードしたもの。
この認証は認証状態が一切保持されない。認証が必要な場合はページにアクセスするたびにuserとpasswordの入力を要求される。
流石に面倒なので、ブラウザがBasic認証後に同じディレクトリ内で遷移するときにAuthorizationヘッダーを勝手にくっつけてくれる。
これによってページ遷移したあとでも認証ダイアログが表示されないってわけ。

セッション

セッションを使用する場合バックエンドサーバーはレスポンスのSet-CookieヘッダにセッションIDを付加する。
ブラウザはこのセッションIDをCookieに保存する。

POSTメソッド送信時にはpostのリクエストボディと一緒にCookieヘッダを付加してセッションIDをもたせる。

セッションIDの要件

  1. 第三者がセッションIDを推測できないこと
  2. 第三者からセッションIDを強制されないこと
  3. 第三者にセッションIDが漏洩しないこと

推測ができると、第三者が認証後のセッションI�Dを乗っ取って何でも出来ちゃう
推測されないようにするためには、セッションIDを長くして、乱数の質を上げる。
アプリケーションレベルの話をすると、セッションIDの管理を自作しない!!

強制されると、第三者がセッションIDだけ取得して正規のユーザにセッションIDを渡して認証させることで何でも出来ちゃう(セッションIDの固定化攻撃)
強制されないようにするためには、認証後に新しいセッションIDを発行してやる。

漏洩しちゃうと、他人になりすましができる。 漏洩の原因には

  • クッキー発行の属性に不備がある
  • ネットワーク的にセッションIDが盗聴される環境にある
  • クロスサイトスクリプティングなどのアプリケーションの脆弱性がある
  • プラットフォームの脆弱性がある
  • セッションIDをURLに保持している(Refereヘッダから漏洩する)

とかとかがある。

クッキー属性

Domain属性

クッキーを送信するドメインを指定できる。
デフォルトだと、発行したサーバー以外ではクッキーの読み書きができない。
複数のサーバーでクッキーの読み書きを行いたい場合に設定する。
だいたいミスって漏れる。デフォルトの状態が一番強固なので、変更しない。

HttpOnly属性

JavaScriptからのクッキーの読み書きを禁止する。
これも支援士の問題で出てきたねー

Secure属性

https通信の場合にのみクッキーをサーバーに送信する。 これも支援士の問題にでてきたねー


受動的攻撃と同一オリジンポリシー

攻撃

Webアプリケーションに対する攻撃は以下の二種類に区別される

  • 能動的攻撃
    • サーバにダイレクトアタック
  • 受動的攻撃
    • サーバに罠を仕掛け、罠にかかった正規ユーザを通してサーバを攻撃する

最近は正規サイトに罠を仕掛ける受動的攻撃が主流

  • 罠サイトに誘導する手間がいらない
  • 正規利用者の数が多いので被害拡大が早い
  • 利用者の個人情報が取れたりする

手口としては

  • FTPなどのパスワードを入手して書き換えちゃう
  • Webサーバーの脆弱性をついて書き換えちゃう
  • SQLインジェクション攻撃でコンテンツを書き換えちゃう
  • SNSなどの利用者が投稿できるサイト機能のクロスサイトスクリプティングを利用する。

防止策

サンドボックス

プログラムのできることを制限する環境のこと。
androidアプリケーションとかのOS機能に介入するやつとかWebサービスがOAuth認証するときのレベルのあれ。

同一オリジンポリシー

JavaScriptは異なるドメインに対してオブジェクトを渡すことができない。
同一オリジンである条件は

  • URLのFQDNが一致している。ホストが同じ
  • スキーム(プロトコル)が同じ
  • ポート番号が同じ

書籍の例では同一オリジンである場合とホスト名が異なる場合で同一オリジンポリシーが正しく動いていることを確認している。

ちなみに、Ajaxで使用するXMLHttpRequestについてはCORSという規格があり、これによって同一オリジンでなくても相手の許可があれば通信できる。

色々なクロスドメインアクセス

異なるドメインのアクセスが許可されている要素は以下のとおり

frame要素とiframe要素

けど、ドメインが異なるページが2枚ある場合、そのドキュメントには互いにアクセスできない

img要素

他ドメインの画像は見れる。

script要素

他ドメインのスクリプトは実行できる。CDN

CSS

scriptと同様

CORS

Cross-Origin Resource Sharingの略 フロントエンドで他ドメインのコンテンツを表示させる(Ajax)ときとかに通信ができる仕組み。

通常はexample.jpからJavascriptでapi.example.netのapiを叩いても取得できない。

シンプルなリクエストで通信を可能にするためには、Access-Control-Allow-Origin [対象ドメイン]ヘッダをレスポンスに追加する。

Access-Control-Allow-Originで送れる条件

シンプルなリクエストだとこれだけで送れる。 シンプルなリクエストとは

  • GET,POST,NEADメソッドのいずれか
  • setRequestHEaderには以下のヘッダのみ設定されている
    • Accept
    • Accept-Language
    • Content-Language
    • Content-Type
  • Content-Typeヘッダhが以下のいずれか
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain

シンプルじゃないリクエストはプリフライトリクエストと言われる。 リクエストなので、レスポンスは関係ない。
レスポンスのcontent-typeがapplication/jsonだとしても、リクエストタイプのcontent-typeが空ならそれはシンプルなリクエストになる。

プリフライトリクエスト

XMLHttpRequest呼び出しの際に以下のリクエストを投げる。(プリフライトリクエスト)
メソッドがOPTIONSになっている。

OPTIONS http://api.example.net/33/33-004b.php HTTP/1.1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:61.0) Gecko/20100101 Firefox/61.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: ja,en-US;q=0.7,en;q=0.3
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type
Origin: http://example.jp
Connection: keep-alive
Host: api.example.net


APIサーバーはこのリクエストに対してレスポンスヘッダを付加して応答する。

Access-Control-Allow-Origin: http://example.jp
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: Content-Type
Access-Control-Max-Age: 1728000

このレスポンスがきてようやくPOSTを送れる

認証情報を含むリクエスト

クロスオリジンアクセスではHTTP認証やクッキーなどの認証に使用されるリクエストヘッダは送信されない。
が、下記設定を行うことで対応ができる。

フロントエンドで対応すること

var req = new XMLHttpRequest();
  req.open('GET', 'http://api.example.net/33/33-006.php');
+  req.withCredentials = true;

XMLHttpRequstオブジェクトのwithCredentialsプロパティをtrueにする。

バックエンドで対応すること

レスポンスヘッダにAccess-Control-Allow-Credentials: trueを付加する。


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment