メインコンテンツまでスキップ

Web アプリケーション診断

診断の仕組み

Shisho Cloud は、登録されたそれぞれのシナリオに対して擬似攻撃を送信し、レスポンスを精査して脆弱性を検出します。 シナリオは、依存関係を持つエンドポイントのシーケンスです。

アプリケーションによっては、リクエストに認証ヘッダーやログイン後の Cookie など特定の情報を付与する必要がある場合があります。 認証設定を登録することにより、巡回や診断でリクエストを送信する際に付与すべきヘッダー等を指定できます。 付与するヘッダー等の値は固定値だけでなく、ログイン操作を表すシナリオをリプレイして動的に抽出することもできます。

場合によっては、アプリケーションの一部機能を診断の対象外にしたいこともあるでしょう。 そのような場合には、スコープを適切に設定することにより、アプリケーションの特定部分のみを診断することができます。

以降のセクションでは、それぞれの概念をより詳しく説明します。

スコープ

スコープは、アプリケーションの診断対象とする範囲を定義します。

デフォルトの入力方式では、URL の prefix を指定すると、その prefix から始まる URL が診断対象になります。 「スコープを追加」ボタンをクリックして、複数の prefix を指定することもできます。

スコープ設定

トグルをクリックして上級者モードに切り替えると、prefix の代わりに正規表現を用いて、診断対象とする URL の範囲を定義できます。

スコープ設定

シナリオ

シナリオは、診断の単位であり、依存関係のあるエンドポイントのシーケンスを表します。例えば以下のようなリクエストの流れをシナリオとして表現でき、エンドポイントの関係性を踏まえた診断が可能となります。

  • 入力画面の CSRF トークンを抽出し、それを付与して POST リクエストを送信する
  • アイテムを削除するリクエストを送信する前に、アイテムを作成するリクエストを送信する
  • アイテムの作成リクエストを送信した後に、アイテム情報が表示される画面をリクエストし、蓄積型の脆弱性を検出する

シナリオは1つ以上のステップから構成され、1つ1つのステップはリクエストを送信するエンドポイントを指します。

例えば以下の2ステップからなるシナリオは、アイテムを作成した後にアイテム一覧画面をリクエストするものです。これにより、アイテム作成に起因する蓄積型の脆弱性を検出することができます。

各ステップの YAML 入力欄には以下の詳細オプションを記述できます。

  • ステップ間の入出力の関係を表すもの
    • extractors: リクエストの結果から値を抽出し、変数に格納して後続のステップで使えるようにする
    • bindings: 以前のステップの extractors で変数に格納した値を、このステップのリクエストパラメータに注入する
  • 診断での取り扱いを調節するもの
    • skipInjection: そのステップに攻撃ペイロードを注入しない

extractors

リクエストの結果から値を抽出し、変数に格納して後続のステップで使えるようにする設定項目であり、後述する Extractor オブジェクトの配列を指定します。配列の要素として複数の Extractor オブジェクトを指定すれば、複数の情報を抽出してそれぞれ変数に格納できます。

YAML 入力欄に例えば以下を入力すると、レスポンスボディに対して正規表現 token=([a-z0-9]+) によるマッチが行われ、([a-z0-9]+) にマッチした部分が変数 token に格納されます。

extractors:
- name: token
type: regex
regex:
- "token=([a-z0-9]+)"
group: 1

Extractor オブジェクト

Extractor オブジェクトは、リクエストの結果から値を抽出する方法と、抽出した値を格納する変数名を定義します。

基本的な構造は以下の通りです。name にて指定した変数名は、後続するステップの bindings で用いられます。type には抽出方法のタイプを指定し、さらに抽出方法ごとの設定項目が続きます。

name: "<格納先の変数名>"
type: "<抽出方法のタイプ>"
# ... <抽出方法に応じた設定> ...

抽出方法のタイプには以下のいずれかを指定します。

  • regex: 正規表現を用いて文字列から値を抽出
  • xpath: XPath を用いて HTML・XML 内の値を抽出
  • json: jq クエリを用いて JSON 内の値を抽出
regex Extractor

正規表現を用いた抽出を表す Extractor です。

設定項目:

  • regex: 抽出に用いる正規表現の配列
  • group: 値を抽出するキャプチャグループの番号
    • デフォルト: 0 (マッチの全体)
  • part: 正規表現でマッチする対象
    • body: レスポンスのボディ (デフォルト)
    • header: レスポンスのヘッダー

例えば以下の Extractor は、レスポンスのヘッダーに対してマッチを行い、1番目のグループ、つまり Location ヘッダーの値の部分を redirect_url という変数に格納します。

name: redirect_url
type: regex
regex:
- "(?m)^Location: (.+?)$"
group: 1
part: header
xpath Extractor

XPath を用いて HTML や XML から値を抽出する Extractor です。

設定項目:

  • xpath: 抽出に用いる XPath の配列
  • attribute: 抽出する属性名
    • 未指定の場合: ノードのテキスト

例えば以下の Extractor は、1番目の <form> 要素の内部にある <input name="csrf_token" value="..."> のような要素を検索し、その value 属性の値を csrf_token という変数に格納します。

name: csrf_token
type: xpath
xpath:
- '(//form)[1]//input[@name="csrf_token"]'
attribute: value

目的の要素を指す XPath は、ブラウザの開発者ツールを用いて簡単に取得できます。詳細な手順はブラウザごとに異なりますが、開発者ツール上で目的の要素を右クリックし、「Copy XPath」のようなメニューを選択すると、その要素を指す XPath がクリップボードにコピーされます。また、開発者ツールのコンソールでは、 $x('//form[1]//input[@name="csrf_token"]') のように $x 関数を用いて、XPath による検索結果を確認できます。

json Extractor

jq クエリを用いて JSON から値を抽出する Extractor です。

設定項目:

  • json: 抽出に用いる jq クエリの配列

例えば以下の Extractor は、レスポンスボディが {"item": {"id": "XXXX", ...}} のようなデータである場合に、 XXXX の部分を抽出して item_id という変数に格納します。

type: json
name: item_id
json:
- ".item.id"

bindings

以前のステップの extractors で変数に格納した値を、このステップのリクエストパラメータに注入する設定項目です。

例えば以下の設定は、以前のステップで csrf_token という変数に格納された CSRF トークンを、リクエストボディの .csrf_token というパラメータに注入します。

bindings:
- type: body # 注入するパラメータのタイプ
key: .csrf_token # 注入先
extractorName: csrf_token # 注入する値を格納した変数名

type には pathParam, header, query, body のいずれかを指定します。

key には注入先のパラメータを指定します。 タイプが pathParam, header, query の場合は、パラメータ名をそのまま指定します。例えばヘッダー Custom-Header: XXXX に注入したい場合は、key: 'Custom-Header' と指定します。 タイプが body の場合は、プロパティ名をドットで繋いで注入したい箇所を指定します。例えば、リクエストボディが {"item": {"id": "XXXX", ...}} のような JSON である場合に、key: '.item.id' と指定すると、XXXX の部分に値が注入されます。

extractorName には、以前のステップの extractors において、name フィールドに記載した変数名を指定します。

skipInjection

ステップに skipInjection: true と設定すると、診断時にそのステップに攻撃ペイロードが注入されなくなります。 例えば蓄積型の脆弱性を検出するために、1番目のステップで POST /items/new に攻撃リクエストを送信し、2番目のステップで GET /items をリクエストしてレスポンスに問題がないか検査するシナリオを定義したいとします。この目的のもとでは、攻撃ペイロードは1番目のステップだけに注入したいですから、2番目のステップには skipInjection: true を設定します。

認証設定

認証設定を定義することにより、巡回や診断の際に送信されるリクエストに、適切なヘッダーや Cookie を付与させることができます。

認証設定追加画面

「認証情報の付加箇所」は、リクエストに付与したい情報の箇所を「ヘッダー」「クエリパラメータ」「ボディ」から選択します。例えば Cookie を付与したい場合は「ヘッダー」を選択し、「ヘッダー名」欄に Cookie と入力します。

リクエストに付与したい値については、「ヘッダー値」または「パラメータ値」の欄にて指定します。付与する値の指定方法としては、「固定値」と「認証リクエストを送信して抽出」の2つがあります。付与する値に有効期限等がない場合は「固定値」、有効期限等がある場合は「認証リクエストを送信して抽出」が適しています。

「固定値」を選択した場合には、リクエストに付与すべき値をそのまま入力します。

シナリオから認証情報を抽出する

「認証リクエストを送信して抽出」を選択した場合は、以下の設定項目が追加されます。

  • 「認証情報の取得方法」: シナリオから認証情報を取得するので、「シナリオから抽出」のままにする
  • 「認証リクエストのシナリオ」: 付与したい値を得るためのリクエスト手順を表すシナリオ
  • 「認証リクエストのパラメータ」: 上記シナリオに注入すべきパラメータ (ユーザー名、パスワード等)
  • 「認証情報の抽出方法」: レスポンスから値を抽出する方法
  • 「認証情報の更新条件」: 認証リクエストの送信と抽出を再実行すべき条件 (有効期限等)

「認証リクエストを送信して抽出」の入力例

認証リクエストのパラメータ

「認証リクエストのシナリオ」に注入するパラメータを指定します。

例えば以下の設定は、シナリオの1番目 (0-indexed) のステップに対して、usernamepassword の2つのパラメータを注入します。

- stepIndex: 1
type: body
key: .username
value: test
- stepIndex: 1
type: body
key: .password
value: n}0b6P@5jkjy&7<KFdwANh9f>IY_?,Ty

この設定項目には、以下のプロパティとするオブジェクトの配列を入力します。

  • stepIndex: パラメータを注入するステップのインデックス (0-indexed)
  • type: パラメータのタイプ (pathParam, header, query, body のいずれか)
  • key: パラメータの名前
    • タイプが pathParam, header, query の場合は、パラメータ名をそのまま指定します
    • タイプが body の場合は、プロパティ名をドットで繋いだものを指定します
      • 例えばリクエストボディが {"item": {"id": "XXXX", ...}} のような JSON である場合に、key: '.item.id' と指定すると、XXXX の部分に値が注入されます
  • value: パラメータの値

認証情報の抽出方法

「認証リクエストのシナリオ」のレスポンスから値を抽出する方法を、Extractor オブジェクトを用いて指定します。なお、Extractor の name フィールドは省略できます。

例えば以下の設定は、レスポンスヘッダーから Set-Cookie: session=XXXX のような文字列を検索し、XXXX の部分を抽出します。

part: header
type: regex
regex:
- "Set-Cookie: (session=[a-zA-Z0-9]+)"
group: 1

認証情報の更新条件

認証リクエストの送信と抽出を再実行すべき条件を設定します。

例えば以下のように設定すると、巡回や診断の初期状態や、前回の抽出から60分が経過した場合に、認証リクエストの送信と抽出が実行されます。

- type: BLANK # 初期状態で更新
config: {}
- type: EXPIRE # 生成から60分以上経過していたら更新
config:
expiresIn: 60m

配列の各要素は、type フィールドと config フィールドを持ちます。 type フィールドには以下のいずれかを指定します。

  • BLANK: 初期状態で更新
  • EXPIRE: 前回の更新から一定期間が経過した場合に更新

type: BLANK

config フィールドは空オブジェクト {} です。

type: EXPIRE

config フィールドには、更新が必要となる期限を expiresIn フィールドとして指定します。

- type: EXPIRE
config:
expiresIn: 60m

expiresIn フィールドの単位としては、s (秒), m (分), h (時間) を利用できます。

用意された認証処理から認証情報を抽出する

Shisho Cloud で用意された認証処理を使用して認証情報を取得することができます。現時点で対応している認証処理は以下です。

「認証リクエストを送信して抽出」を選択し、「認証情報の取得方法」は「用意された認証処理から抽出」に設定すると、指定された認証処理の設定項目が表示されます。

用意された認証処理

Firebase Authentication から認証情報を抽出

Firebase Authentication に接続して ID Token 等を取得する認証処理です。現時点では、メールとパスワードの認証のみ対応しております。

Firebase Authentication 認証

「認証情報の取得方法」は「用意された認証処理から抽出」を選択し、「認証処理の種類」は「Firebase 認証」を選択します。

Firebase Authentication の場合だと「認証処理の内容」は以下のフィールドを設定する必要があります。

  • email: ログインアカウントのメールアドレス
  • password: ログインのパスワード
  • apiKey: Firebase Authentication 発行の API キー
  • extractors: 抽出する認証情報の項目

「認証処理の内容」の例:

email: user@example.com
password: user_p@ssword
apiKey: 8gpUlIEUgExk8J5oM13
extractors:
- type: json
part: body
json:
- ".id_token"

extractorstypepart は上記同様それぞれ jsonbody を指定する必要があります。json のフィールドの値は以下が指定可能です。

  • .id_token
  • .refresh_token
  • .expires_in

json のフィールドは値が一つまでしか指定できません。extractors はシナリオを後続処理として行う場合のみに複数指定することが可能です。シナリオの後続処理を行わない場合は extractors で設定された値が抽出されます。

認証情報の更新条件」の設定はシナリオから認証情報を抽出する場合と同じです。

Cognito から認証情報を抽出

Cognito に接続して ID Token 等を取得する認証処理です。現時点では、メールとパスワードの認証のみ対応しております。

Cognito 認証

「認証情報の取得方法」は「用意された認証処理から抽出」を選択し、「認証処理の種類」は「Cognito 認証」を選択します。

Cognito 認証の場合だと「認証処理の内容」は以下のフィールドを設定する必要があります。

  • email: ログインアカウントのメールアドレス
  • password: ログインのパスワード
  • region: AWS のリージョンのコード
  • clientId: Cognito 発行のクライアント ID
  • extractors: 抽出する認証情報の項目

「認証処理の内容」の例:

email: user@example.com
password: user_p@ssword
region: ap-northeast-1
clientId: 7siaq79aq0hql9abh
extractors:
- type: json
part: body
json:
- ".id_token"

extractorstypepart は上記同様それぞれ jsonbody を指定する必要があります。json のフィールドの値は以下が指定可能です。

  • .id_token
  • .access_token
  • .refresh_token
  • .token_type
  • .expires_in

json のフィールドは値が一つまでしか指定できません。extractors はシナリオを後続処理として行う場合のみに複数指定することが可能です。シナリオの後続処理を行わない場合は extractors で設定された値が抽出されます。

認証情報の更新条件」の設定はシナリオから認証情報を抽出する場合と同じです。

Auth0 から認証情報を抽出

Auth0 に接続して ID Token 等を取得する認証処理です。現時点では、メールとパスワードの認証のみ対応しております。Auth0 認証のカスタムドメインも現時点では対応しておりません。

Auth0 認証

「認証情報の取得方法」は「用意された認証処理から抽出」を選択し、「認証処理の種類」は「Auth0 認証」を選択します。

Cognito 認証の場合だと「認証処理の内容」は以下のフィールドを設定する必要があります。

  • email: ログインアカウントのメールアドレス
  • password: ログインのパスワード
  • domain: Auth0 発行の認証ドメイン (auth0.com のサブドメイン)
  • clientId: Cognito 発行のクライアント ID
  • clientSecret: Cognito 発行のクライアントシークレット
  • callbackUrl: Cognito の設定で許容した callback の URL
  • extractors: 抽出する認証情報の項目

「認証処理の内容」の例:

email: user@example.com
password: user_p@ssword
domain: dev-sample-app.jp.auth0.com
clientId: 7siaq79aq0hql9abh
clientSecret: HTOuBdIlg0LukDe0Fm5lTDCjTkYcBDu
callbackUrl: https://example.com/callback
extractors:
- type: json
part: body
json:
- ".id_token"

extractorstypepart は上記同様それぞれ jsonbody を指定する必要があります。json のフィールドの値は以下が指定可能です。

  • .id_token
  • .access_token
  • .token_type
  • .expires_in

json のフィールドは値が一つまでしか指定できません。extractors はシナリオを後続処理として行う場合のみに複数指定することが可能です。シナリオの後続処理を行わない場合は extractors で設定された値が抽出されます。

認証情報の更新条件」の設定はシナリオから認証情報を抽出する場合と同じです。

用意された認証処理の後続処理としてシナリオを設定する

用意された認証処理の後続処理としてシナリオを設定することが可能です。Firebase Authentication 等の外部認証サービスからトークンを受け取り、それを元にアプリケーションがセッションを発行する場合などに適しています。

「認証情報の取得方法」は「用意された認証処理に後続処理のシナリオを追加して抽出」を選択します。

Scenario 後続処理認証

上記の例だと Cognito 認証を行った後に「ログイン」というシナリオを実行します。基本的には設定は用意された認証処理とシナリオを単独で設定する場合と変わりませんが、値を用意された認証処理の結果からシナリオに渡すための変更だけは必要です。

まず、用意された認証処理の extractorsname のフィールドを追加します。シナリオの入力パラメータでこの name を指定します。シナリオの後続処理がある場合には複数の extractors を設定できますので、以下の例では、ID Token と Access Token をシナリオに渡すよう name を追加して指定します。

extractors:
- type: json
part: body
name: id-token
json:
- ".id_token"
- type: json
part: body
name: access-token
json:
- ".access_token"

次に、「認証リクエストのパラメータ」で上記 extractors で設定した値を受け取るよう設定します。「認証リクエストのパラメータ」では value の代わりに useExtractor を指定します。useExtractorname のフィールドを上記 extractors の参照したい値の name のフィールドと同じ設定にします。以下の例では上記 extractors の ID Token と Access Token を参照するよう設定されています。

- type: query
key: id_token
useExtractor:
spec: cognitoSatisfierSpec
name: id-token
stepIndex: 0
- type: query
key: access_token
useExtractor:
spec: cognitoSatisfierSpec
name: id-token
stepIndex: 0

useExtractors には spec というフィールドも設定する必要があります。この値は使用している用意された認証処理の種類によって異なります。設定する値は以下です。

  • Firbase 認証の場合: firebaseSatisfierSpec
  • Cognito 認証の場合: cognitoSatisfierSpec
  • Auth0 認証の場合: auth0SatisfierSpec

これで、用意された認証処理の後にシナリオが実行され、「認証情報の抽出方法」の設定によりシナリオから認証情報が抽出されます。

複数のステップから構成される認証処理を設定する

順序関係を持つ複数のステップから構成される認証処理を設定する場合、「巡回・診断の認証設定の追加」画面において「YAML Editor」タブを選択してください。

Precondition の YAML Editor

その後、表示されるエディターにおいて、以下の項目を YAML フォーマットで設定してください。

metadata

metadata では認証設定のメタデータを指定します。 description には認証設定の説明を記入してください。ここに設定された内容は、一覧画面にてタイトルとして表示されます。 なお、id は自動的に生成されるため、変更する必要はありません。

metadata:
id: 2359978...
description: "Cookie 用の認証設定"

.spec.items

.spec.items では、その認証設定で管理するデータ自体を定義します。 当該フィールドの要素に指定するオブジェクトには、以下のフィールドを指定してください。

  • name: 認証情報の名前
  • type: その認証情報が注入するパラメータのタイプ (header, query, body)
  • key: その認証情報を注入するパラメータのキー名 (例: Cookie)
  • value: 認証情報の値
    • 静的に設定するとき以外は空値に設定してください

以下に例を示します。

items:
- name: cookie
type: header
key: Cookie
value: ""

.spec.satisfierSpec.steps

.spec.satisfierSpec.steps では、認証処理のステップを定義します。 当該フィールドの要素に指定するオブジェクトには、以下のフィールドを指定してください。

  • type: ステップの種別
    • request: リクエストを送信するステップ
    • firebase: Firebase 認証を行うステップ
    • cognito: Cognito 認証を行うステップ
    • auth0: Auth0 認証を行うステップ
  • spec: ステップの具体的な設定

ここで、spec に指定すべきオブジェクトのスキーマは、type で指定された種別によって異なります。

typerequest の場合は、以下のフィールドを指定することでそのステップで送信するリクエストの詳細を設定してください。

# HTTP メソッド
method: GET

# リクエストを送信する Origin (スキーム、ホスト、ポートの組み合わせ)
origin: https://example.test

# リクエストを送信するパス
path: /api/v1/

# リクエストヘッダー
headers:
- key: Authorization
value: Bearer 123456789

# クエリパラメータ
queries:
- key: id
value: 1

# ステップ間で伝播させるパラメータのための Extractor の設定
extractors:
- { ... }

# あるステップで抽出したパラメータをこのステップで利用するための設定
bindings:
- { ... }

なお、パラメータを伝播させるための extractors の設定は、Extractor オブジェクト と同一です。また、bindings の設定は bindings と同様です。

typefirebase, cognito, auth0 の場合は、以下のフィールドを指定することでそのステップで認証処理を行うための詳細を設定してください。

# Firebase 認証の場合
email: test@example.test
password: testpassword
apiKey: testapikey
extractors:
- {...}

# Cognito 認証の場合
email: test@example.test
password: testpassword
region: ap-northeast-1
clientId: testclientid
extractors:
- {...}

# Auth0 認証の場合
email: test@example.test
password: testpassword
domain: example.test
clientId: testclientid
clientSecret: testclientsecret
callbackUrl: https://example.test/callback
extractors:
- {...}

.spec.satisfierSpec.extractors

.spec.satisfierSpec.extractors では、各ステップで取得したレスポンスに対して実行する Extractor の設定を行います。Extractor の記法は Extractor オブジェクト と同様です。

Extractor で抽出した値を Item に注入する

.spec.satisfierSpec.steps[_].extractors および .spec.satisfierSpec.extractors に指定された各 Extractor によって抽出された値は、その Extractor の name 属性の値が items に指定された認証情報の name 属性の値と一致する場合、その認証情報に注入されます。

例えば、以下の例では cookie という名前の Extractor によって抽出された値は、items に定義された cookie という名前の認証情報に注入されます。

spec:
items:
- name: cookie
type: header
key: Cookie
value: ""
satisfierSpec:
steps: []
extractors:
- type: regex
part: header
group: 1
regex:
- "Set-Cookie: (SESSID=[a-zA-Z0-9]+)"
name: cookie
validatorSpecs:
- { ... }

.spec.validatorSpecs

.spec.validatorSpecs では、認証設定を更新するトリガーとなる条件を指定します。

この設定はエディターを開いた時点ですでに設定されていますが、更新の間隔を変更したい場合にのみ、expiresIn の値を変更してください。なお、当該フィールドには Go 言語において time.Duration として解釈可能な値を指定してください。

- type: BLANK
config: {}
- type: EXPIRE
config:
expiresIn: 10m