APIの把握とスキーマ・権限の検証
未登録API、入力形式の不備、対象への権限不足を、それぞれの検査で確認します。
説明
API保護では、対象のエンドポイントを把握し、入力形式と権限を評価します。スキーマに合うJSONであっても、その利用者が指定した対象を更新してよいとは限りません。
固定のAPI台帳と観測記録を照合し、JSON Schema 2020-12の実装で型・上限・余分な項目を検査します。最後に所有者条件を含めた更新を試します。
未登録API、入力形式の不備、対象への権限不足を、それぞれの検査で確認します。
1台帳と観測された経路を照合する
メソッドとパスの組をキーにします。
from sp23 import *
from sp02 import show
db=connect()
show(db,"SELECT o.method,o.path,e.owner,e.schema_version FROM observed o LEFT JOIN台帳と観測された経路を照合する endpoint e USING(method,path) ORDER BY o.path,o.method")POST /legacy/ordersは台帳にありません。既知エンドポイントだけの検査結果を、全APIの保護範囲と扱わないようにします。
2入力スキーマを開く
idとquantityが必須で、余分な項目を許さない形式です。
print(json.dumps(SCHEMA,indent=2))
Draft202012Validator.check_schema入力スキーマを開く(SCHEMA)quantityは1以上5以下の整数です。check_schemaはスキーマ自体を確認し、実際の要求データの合否は次に調べます。
3正しい入力と型の違いを検査する
整数、数字文字列、真偽値を比較します。
print(errors正しい入力と型の違いを検査する({'id':7,'quantity':2}))
print(errors({'id':7,'quantity':'2'}))
print(errors({'id':7,'quantity':True}))整数だけが空のエラー一覧です。数字の文字列やtrueを数量として黙って変換しません。JSON Schemaの整数判定では真偽値は整数に含まれません。
4上限と余分な項目を検査する
数量6と、許可していないowner項目を試します。
print(errors上限と余分な項目を検査する({'id':7,'quantity':6}))
print(errors({'id':7,'quantity':2,'owner':'bob'}))maximumとadditionalPropertiesのエラーです。アプリが更新してよい項目を契約として限定します。
5形式が正しい他人の対象を指定する
aliceがbobの注文8を指定した場合です。
payload={'id':8,'quantity':2}
print(errors(payload))
cur=db.execute("UPDATE orders SET quantity=? WHERE id=? AND owner=?形式が正しい他人の対象を指定する",(payload['quantity'],payload['id'],'alice'))
print(cur.rowcount)スキーマは通りますが更新は0行です。入力形式の検査と、対象の所有者に基づく認可を組み合わせます。
6本人の対象だけを更新する
同じ数量2をaliceの注文7へ適用します。
payload={'id':7,'quantity':2}
print(errors(payload))
print(db.execute("UPDATE orders SET quantity=? WHERE id=? AND owner=?",(payload['quantity'],payload['id'],'alice')).rowcount)
show本人の対象だけを更新する(db,"SELECT * FROM orders ORDER BY id")注文7だけ2になり、bobの注文8は1のままです。更新件数と原表を読み直して範囲を確かめます。
7検査対象外を台帳の課題として残す
未登録のメソッド・パスを抽出します。
show検査対象外を台帳の課題として残す(db,"SELECT o.method,o.path FROM observed o LEFT JOIN endpoint e USING(method,path) WHERE e.path IS NULL")POST /legacy/ordersが残ります。既知APIの入力検査を強化しただけで、未登録経路の問題も解消したとは報告しません。
まとめ
メソッドとパスをAPI台帳へ照合します。
実スキーマ検証で型・範囲・項目を確認します。
スキーマと資源単位の認可を両方適用します。
操作履歴と解説を振り返れます。
Python 3.15 / 固定要求列・HMAC・JSON Schema 2020-12
from sp23 import *
from sp02 import show
db=connect()
show(db,"SELECT o.method,o.path,e.owner,e.schema_version FROM observed o LEFT JOIN endpoint e USING(method,path) ORDER BY o.path,o.method")method | path | owner | schema_version POST | /legacy/orders | NULL | NULL GET | /orders | team-a | 1 POST | /orders | team-a | 1
print(json.dumps(SCHEMA,indent=2))
Draft202012Validator.check_schema(SCHEMA){
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": [
"id",
"quantity"
],
"properties": {
"id": {
"type": "integer",
"minimum": 1
},
"quantity": {
"type": "integer",
"minimum": 1,
"maximum": 5
}
},
"additionalProperties": false
}print(errors({'id':7,'quantity':2}))
print(errors({'id':7,'quantity':'2'}))
print(errors({'id':7,'quantity':True}))[]
[('type', 'quantity')]
[('type', 'quantity')]print(errors({'id':7,'quantity':6}))
print(errors({'id':7,'quantity':2,'owner':'bob'}))[('maximum', 'quantity')]
[('additionalProperties', '')]payload={'id':8,'quantity':2}
print(errors(payload))
cur=db.execute("UPDATE orders SET quantity=? WHERE id=? AND owner=?",(payload['quantity'],payload['id'],'alice'))
print(cur.rowcount)[] 0
payload={'id':7,'quantity':2}
print(errors(payload))
print(db.execute("UPDATE orders SET quantity=? WHERE id=? AND owner=?",(payload['quantity'],payload['id'],'alice')).rowcount)
show(db,"SELECT * FROM orders ORDER BY id")[] 1 id | owner | quantity 7 | alice | 2 8 | bob | 1
show(db,"SELECT o.method,o.path FROM observed o LEFT JOIN endpoint e USING(method,path) WHERE e.path IS NULL")method | path POST | /legacy/orders
演習完了