Quick definition: SID filtering is a trust-side control that removes foreign SIDs—including values in SIDHistory
—from a user’s authorization data as it traverses a trust. It prevents privilege escalation by honoring only the SIDs the trusting side expects.
- External/domain trusts: Quarantine=Yes by default → accept only SIDs from the directly trusted domain.
- Forest trusts: EnableSidHistory=No by default →
SIDHistory
is not honored across the boundary unless you temporarily enable it for migrations. - Filtering occurs on the trusting DC that issues the service ticket; the resource server sees the already-filtered token.
Why this matters now
Few enterprises run a single, tidy forest. You likely juggle account/resource forests, B2B external trusts, mergers, and carve-outs. In these layouts, a single flag on the trust—Quarantine or EnableSidHistory—decides whether a risky SID sneaks across or a migration proceeds without breaking access.
Misconfigured filtering invites SID-history injection and unexpected delegation paths. Over-zealous filtering blocks legitimate day-1 access during migrations. The craft is using filtering deliberately—tight for steady-state, relaxed only within strict guardrails during moves.
Newer Windows builds also introduce trust scanners and stronger defaults for cross-boundary auth. If you don’t plan for these, you’ll either punch holes unintentionally or trip hard failures during cutover.
Fundamentals (and what gets overlooked)
Most guidance says: “SID filtering strips foreign SIDs; turn it off for migration; re-enable afterward.” True—but incomplete. Three gotchas matter:
- Two knobs exist depending on trust type:
- External/domain trust: /Quarantine:{Yes|No}.
- Forest trust: /EnableSidHistory:{Yes|No} governs whether
SIDHistory
is honored across forests.
- Not every SID is filterable. Certain “NeverFilter” categories (e.g., Enterprise DC) are by spec not removed.
- Filtering interacts with modern trust hardening. Trust scanners and selective authentication change outcomes if they can’t read what they need.
Need refreshers?
Key terms at a glance
PACQuarantine
EnableSidHistorySelectiveAuth
What’s in the core?
Kerberos tickets contain a PAC (Privilege Attribute Certificate) with the user SID, all group SIDs, and any SIDHistory
. When a user from Forest B requests access in Forest A, a DC in Forest A evaluates the PAC and filters SIDs that don’t conform to the trust’s rules. The DC then issues the service ticket containing the already-filtered authorization data. The resource server simply enforces what the DC decided.
Trust type | Primary control | Default | Implication |
---|---|---|---|
External / domain | /Quarantine | Yes (on) | Only SIDs from the directly trusted domain survive. |
Forest | /EnableSidHistory | No (off) | SIDHistory is not honored across the forest boundary unless you enable it. |
What counts as “foreign”? It’s not guesswork—DCs match SIDs against trusted namespaces in the TDO (Trusted Domain Object). SIDs with a domain component outside the directly trusted namespace are filtered (unless covered by forest-aware rules or “NeverFilter”).
Token-size side effects are real. Extra SIDs—nested groups and SIDHistory
—inflate the PAC. During co-existence, tokens can exceed MaxTokenSize, causing odd logon issues and HTTP 401s. Plan to shed SIDHistory
quickly and trim memberships.
Runbook: Migrate safely, then harden
This is the part you’ll reuse. It assumes a two-forest migration where you need temporary access continuity via SIDHistory
.
Step A — Inventory & classify your trusts
# PowerShell (run from a management workstation with RSAT)
Import-Module ActiveDirectory
Get-ADTrust -Filter * |
Select-Object Name, Direction, TrustType, ForestTransitive,
SIDFilteringQuarantined, SIDFilteringForestAware, SelectiveAuthentication
Interpretation:
- SIDFilteringQuarantined → quarantine state (external/domain trusts).
- SIDFilteringForestAware → forest-aware SID handling (ties to EnableSidHistory).
- SelectiveAuthentication → explicit allow lists required on resources.
:: Validate from the trusting side (per direction)
netdom trust TrustingDomain /domain:TrustedDomain /quarantine
netdom trust TrustingForestRoot /domain:OtherForestRoot /enablesidhistory
Step B — Prepare a safe allowance for SIDHistory
(forest trust only)
- Pre-checks: confirm mutual admin approvals; open LDAP/RPC for trust scanners; if Selective Auth is on, grant Allowed to authenticate to the other forest’s PDC for scanner reads.
- Enable
SIDHistory
across the forest boundary (temporarily):
:: Run on the trusting forest root (outbound to the other forest)
netdom trust /domain: /EnableSidHistory:Yes ^
/UD:<OtherForestRoot\Administrator> /PD:* /UO:<TrustingForestRoot\Administrator> /PO:*
Re-read with Get-ADTrust to confirm state. Document the exit date for this exception.
Step C — Migrate with SIDHistory
and validate
- Use ADMT or a vendor tool that calls DsAddSidHistory under the hood and meets prerequisites.
- Spot-check migrated objects:
Get-ADUser -Identity -Properties SIDHistory |
Select-Object SamAccountName, SID, SIDHistory
Then validate the effective token from the trusting side:
klist purge
whoami /all
Step D — Cutover hardening: remove allowances and reduce blast radius
- Disable cross-forest
SIDHistory
:
netdom trust /domain: /EnableSidHistory:No
- Ensure quarantine on external/domain trusts:
netdom trust /domain: /Quarantine:Yes
- Clean
SIDHistory
quickly:
# Find accounts still carrying SIDHistory
Get-ADUser -Filter * -Properties SIDHistory | Where-Object SIDHistory
# Remove after ACLs are updated
Set-ADObject -Identity "" -Clear SIDHistory
- Review delegation & target validation: eliminate unconstrained delegation across incoming trusts; prefer constrained & resource-based constrained delegation.
- Instrument & monitor: export trust flags weekly and diff; alert on drift.
Troubleshooting quick hits
- “Access denied” toggling trust flags: run netdom on a DC using admin creds on both sides with /UD and /UO; verify time sync and RPC/LDAP reachability.
- Token bloat after enabling
SIDHistory
: trim group memberships; expediteSIDHistory
cleanup; temporarily raise MaxTokenSize for critical apps while you remediate. - “Filtering is on, but I still see SID X”: confirm whether X is in a “NeverFilter” category or Enterprise DC—by spec, it can traverse.
Inherent tendencies you can’t code around
- Security ↔ continuity is a spectrum. Relaxation (EnableSidHistory:Yes / Quarantine:No) smooths migrations but softens boundaries.
- Token growth is inevitable during co-existence. Plan for it, observe it, then reduce it.
- Modern Windows prefers to fail closed. Trust scanners and target validation break risky paths rather than silently allow them.
Some mental models for you
- Boundary contract: a trust is a contract about which SIDs are recognized. If there’s no clause, the SID doesn’t pass.
- PAC budget: every group and
SIDHistory
entry spends bytes. Spend them only while you must. - Directional asymmetry: every trust has two directions—decide settings for each.
- Temporary exception with exit: any relaxation must have a calendar end and a cleanup plan.
Misunderstandings, risks & corrective actions
Reality: you soften the boundary and expand blast radius. Keep it temporary.
Reality: external trusts should be quarantined by default; grant access with direct domain groups instead of universal groups crossing the boundary.
Expert essentials checklist
- External trusts steady-state: Quarantine=Yes.
- Forest trusts: EnableSidHistory only during the migration window; revert to No after cutover.
- Document per-direction settings; never assume symmetry.
- Monitor for Unsecure SIDHistory and remove it quickly.
- Prefer constrained delegation; enable modern target validation features.
- Budget PAC size; trim memberships; remove
SIDHistory
promptly.
Applications, decisions & what’s next
- Resource/account forest designs: keep EnableSidHistory=No steady-state. Use shadow principals or scoped roles for admin needs.
- M&A and carve-outs: time-box EnableSidHistory=Yes with a tracked ACL-refactor burn-down. Target zero references to source groups before turning it off.
- B2B external access: keep quarantine on; where exception is necessary, use Selective Authentication with Allowed to authenticate on specific resources.
FAQ
Is SID filtering enabled by default?
For external/domain trusts, quarantine is on by default. For forest trusts, SIDHistory
is blocked unless you explicitly enable it.
How do I check if filtering is on?
Use netdom trust … /quarantine or /enablesidhistory, and confirm with Get-ADTrust.
Can I quarantine inside a forest?
Not recommended—doing so breaks assumptions for universal groups and often causes outages.
Why did a specific SID still pass with filtering on?
Some SID categories are “NeverFilter” by specification (e.g., Enterprise DC); those may traverse by design.
Some more references
- Microsoft Learn — netdom trust (parameters incl. /Quarantine, /EnableSidHistory)
- Microsoft Open Specs — MS-PAC overview (PAC composition)
- Microsoft Open Specs — SID filtering & “NeverFilter” categories
- Microsoft — Kerberos token size, MaxTokenSize guidance
- Microsoft — DsAddSidHistory requirements
- ADSecurity — Security considerations for trusts (SID filtering & selective auth)
- ADSecurity — SID History persistence & risks
- MITRE ATT&CK — T1134.005 SID-History Injection
- Dirk-Jan Mollema — How does SID filtering work? (deep dive)
- SpecterOps (2025) — New trust attack paths & filtering notes
- MS Q&A — Selective Authentication & “Allowed to authenticate” rights
- MS KB 938118 — Managing MaxTokenSize via GPO