pygamlastan.security

The Web Browser SSO validation suite, its configuration, the structured result, and the replay cache. See the Validation and replay protection guide.

class pygamlastan.security.SecurityConfig

Controls which checks run and their tolerances. The default constructor uses production-safe defaults. Attributes are read/write properties.

static permissive() SecurityConfig

Relaxed config for tests only. Never use in production.

static strict() SecurityConfig

All checks enabled, including the optional ones.

clock_skew_seconds: int

Tolerance, in seconds, applied to every time-window comparison (NotBefore / NotOnOrAfter / issue instants) to absorb clock drift.

max_assertion_age_seconds: int

Reject an assertion whose IssueInstant is older than this many seconds, independent of its NotOnOrAfter (a freshness ceiling).

require_signed_assertions: bool

Require each assertion to be individually signed (the common federation rule; SPs typically set WantAssertionsSigned).

require_signed_responses: bool

Require the enclosing <Response> to be signed (in addition to, or instead of, signed assertions).

require_encrypted_assertions: bool

Require assertions to arrive as <EncryptedAssertion> (e.g. a PEFIM-style profile). Off by default.

verify_destination: bool

Check the message Destination matches the URL it was received at.

verify_recipient: bool

Check the bearer SubjectConfirmationData/@Recipient equals the ACS URL.

check_client_address: bool

Bind the assertion to the client’s source address (SubjectConfirmationData/@Address). Requires passing the client address to validation. Off by default.

enforce_persistent_id_uniqueness: bool

For a persistent NameID, require a persistent_id_store and reject a persistent identifier silently re-bound to a different principal (SAML erratum E78). On by default.

reject_signatures_with_ds_object: bool

Reject XML-DSig signatures that carry a <ds:Object> (signature-wrapping hardening, SAML erratum E91). On by default.

sanitize_relay_state: bool

Enforce the RelayState size/character limits (erratum E90). On by default.

require_integrity_with_cbc: bool

Require an integrity mechanism when CBC-mode encryption is used, blocking padding-oracle attacks (erratum E93). On by default.

pygamlastan.security.validate_response(response, config, received_url, expected_idp_entity_id, sp_entity_id, acs_url, expected_request_id=None, client_address=None, relay_state=None, response_signature_verified=None, verified_signed_ids=None, current_proxy_depth=0, now=None, replay_cache=None, persistent_id_store=None, unsafe_no_replay_cache=False, unsafe_no_persistent_id_store=False) ValidationResult

Run the full validation suite over a parsed pygamlastan.core.Response and return a structured ValidationResult (does not raise on a validation failure). replay_cache is required by default and may be an InMemoryReplayCache or any object implementing the replay-cache protocol. If persistent NameID uniqueness is enabled and the response carries a persistent NameID, persistent_id_store is also required unless unsafe_no_persistent_id_store=True is explicit.

class pygamlastan.security.ValidationResult
is_valid() bool
total_checks() int
checks: list[ValidationCheck]
failures() list[ValidationCheck]
class pygamlastan.security.ValidationCheck

One numbered check from the suite.

check_number: int
check_name: str
passed: bool
detail: str | None

Replay cache

class pygamlastan.security.InMemoryReplayCache

A single-process replay cache.

check_and_insert(id: str, expiry: datetime.datetime) bool

Return True if id is new (now recorded), False if it was already seen and not yet expired (a replay).

Note

A recorded entry counts as a replay only while expiry is in the future relative to the wall clock at call time; the cache uses its own now and does not honour a caller-supplied clock. Pass an expiry derived from real time (e.g. the assertion’s NotOnOrAfter), not a fixed test constant.

cleanup() None

Drop entries whose expiry has passed.

Note

For multi-worker deployments, pass any object implementing check_and_insert(id, expiry) -> bool and cleanup() (for example a Redis-backed cache) wherever a replay_cache is accepted. gamlastan calls into it and fails closed if a call raises. See Validation and replay protection.