一覧SP-19-2 · AppSec製品:コード・依存関係・動作の検査

動的解析の認証と巡回範囲

検査したURLの数だけでなく、認証後の機能へ到達した範囲を確認します。

説明

DASTは動くアプリへ要求を送り、応答や挙動を調べます。ログインできているか、対象の機能へ到達できているかによって、検査できる範囲が変わります。

固定のWSGIアプリをテスト要求で呼び出します。未認証の応答、ログイン、認証後のリンク、API到達を同じ履歴で追い、応答とアプリ内の実行記録を照合します。

検査したURLの数だけでなく、認証後の機能へ到達した範囲を確認します。

1公開入口から得られるリンクを読む

WSGIのテスト要求でアプリを呼びます。

from sp19 import * lab=App() status,headers,body=request公開入口から得られるリンクを読む(lab,'/') print(status,body)

公開ページには/loginだけがあります。ここから見えるリンクだけでは/privateや/APIの内容を検査できません。

2認証なしで保護ページへ要求する

/privateを直接指定します。

print(request認証なしで保護ページへ要求する(lab,'/private')[0:1]) print(lab.trace[-1])

401 Unauthorizedです。要求は到達していますが、保護された業務処理を実行したことにはなりません。

3固定の試験資格情報でログインする

公開のpublic-trainingという試験文字列を使います。

status,headers,body=request(lab,'/login','POST',b'public-training') print(status,headers['Set-Cookie固定の試験資格情報でログインする'],body) cookie=headers['Set-Cookie'].split(';',1)[0]

セッションCookieと/privateへのリンクを受け取ります。これは教材用アプリの固定セッションです。

4セッションを付けて保護ページを開く

Cookieを次の要求に引き継ぎます。

print(request(lab,'/private',cookie=cookieセッションを付けて保護ページを開く)[::2])

200 OKと/api/exportへのリンクです。認証が必要な経路が新しく見つかりました。

5APIの実処理へ到達する

認証済みの要求で公開2行を取得します。

print(requestAPIの実処理へ到達する(lab,'/api/export',cookie=cookie)[::2])

alphaとbetaが返ります。アプリ内でSQLを実行し、その値を応答へ変換した結果です。

6Cookieの欠落で検査範囲が変わることを確認する

同じAPIへCookieなしで要求します。

print(requestCookieの欠落で検査範囲が変わることを確認する(lab,'/api/export')[::2])

401とauthenticationエラーです。エラー応答が返るだけの試行を、認証後機能の検査成功に含めません。

7応答とアプリ内の実行記録を照合する

各行はメソッド、パス、応答コード、認証状態です。

for row in lab.trace応答とアプリ内の実行記録を照合する:print(row) print(sorted({path for method,path,status,auth in lab.trace if auth and status=='200'}))

認証済みで200になったのは/privateと/api/exportです。IASTなど実行時の観測と照合する場合も、同じ要求に結び付けて範囲を確認します。

まとめ

認証の成功と対象機能への到達を確認します。

セッションを引き継いだ要求で検査します。

応答と実行時の記録を照合します。

操作履歴と解説を振り返れます。

Python 3.15 / AST・SQLite・WSGIの固定アプリ

1
from sp19 import * lab=App() status,headers,body=request(lab,'/') print(status,body)
200 OK {'links': ['/login']}
2
print(request(lab,'/private')[0:1]) print(lab.trace[-1])
('401 Unauthorized',)
('GET', '/private', '401', False)
3
status,headers,body=request(lab,'/login','POST',b'public-training') print(status,headers['Set-Cookie'],body) cookie=headers['Set-Cookie'].split(';',1)[0]
200 OK sid=public-training-s7; HttpOnly; SameSite=Strict {'links': ['/private']}
4
print(request(lab,'/private',cookie=cookie)[::2])
('200 OK', {'links': ['/api/export']})
5
print(request(lab,'/api/export',cookie=cookie)[::2])
('200 OK', {'rows': [['alpha'], ['beta']]})
6
print(request(lab,'/api/export')[::2])
('401 Unauthorized', {'error': 'authentication'})
7
for row in lab.trace:print(row) print(sorted({path for method,path,status,auth in lab.trace if auth and status=='200'}))
('GET', '/', '200', False)
('GET', '/private', '401', False)
('POST', '/login', '200', False)
('GET', '/private', '200', True)
('GET', '/api/export', '200', True)
('GET', '/api/export', '401', False)
['/api/export', '/private']

演習完了