---
title: "One SAML assertion, two sessions: update Snipe-IT to 8.5.0 or later"
description: "In Snipe-IT up to 8.4.1, one captured SAML assertion opens two sessions for the victim, and revoking at the identity provider reaches only one. Fixed in 8.5.0."
url: https://ihsanalapsi.dev/articles/snipe-it-saml-replay-fix
lang: en
date: 2026-11-01
updated: 2026-11-01
tags: ["Security updates", "App security"]
author: Ihsan Alapsi (إحسان العبسي)
---
# One SAML assertion, two sessions: update Snipe-IT to 8.5.0 or later

If you manage your company's assets in Snipe-IT and your staff sign in through SAML single sign-on, [the project's security advisory on GitHub](https://github.com/grokability/snipe-it/security/advisories/GHSA-w49g-f2jc-qxhg) describes a flaw that breaks the simplest assumption of that login: that one assertion is used once. If that is your setup, below you will find whether your version is affected, how the guard broke, and how to close a session that may have stayed open after the identity provider revoked it.

## How serious is this flaw?

It is rated High on CVSS 4.0, even though the project's own advisory rates it "Moderate"; and the fix has been out since May.

The flaw's number, CVE-2026-116201, was published on October 25, 2026, and its score is 8.7 on CVSS 4.0, according to [VulnCheck's page for the flaw](https://www.vulncheck.com/advisories/snipe-it-before-8.5.0-saml-assertion-replay-via-race-condition). The project's advisory on GitHub rates it "Moderate", so do not let that word delay the update: the flaw hands the attacker a signed-in session in the victim's account.

[The project's advisory](https://github.com/grokability/snipe-it/security/advisories/GHSA-w49g-f2jc-qxhg) lists 8.5.0 as the patched version. That release came out in May, and [its release notes](https://github.com/grokability/snipe-it/releases/tag/v8.5.0) say it addresses several security issues and encourage everyone to update as soon as is feasible. The advisory itself came out later, on October 8, 2026, and the CVE number came after that. So anyone still on 8.4.1 or older, which [the advisory](https://github.com/grokability/snipe-it/security/advisories/GHSA-w49g-f2jc-qxhg) lists as affected, has had a fix waiting since May and has not installed it.

## How does one assertion open two sessions?

Because the replay guard was "check, log in, then add" with no lock, and the database itself did not refuse the same value twice.

Snipe-IT's SAML handler blocks assertion replay with the `saml_nonces` table: it checks whether the assertion's nonce is already there, and if it is not, it logs the user in and then adds the nonce to the table. This guard broke in two places. First, the nonce column had an ordinary index, not a UNIQUE one, so the table accepts the same value twice. Second, the check ran before `Auth::login()` and the insert after it, with no transaction around them, so nothing stops two requests from getting through together.

So two `POST /saml/acs` requests sent at the same moment with the same captured assertion both pass the check, both log in, and both add the value. One assertion gives the victim two parallel sessions.

This kind of mistake has a well-known name, "time-of-check, time-of-use" (TOCTOU), and the advisory itself files the flaw under it (CWE-367). Here the race succeeded for two reasons together: the login sat between the check and the insert with no transaction, and the nonce column had no UNIQUE constraint to refuse the second value.

## What does the attacker need?

Three conditions, and none of them is the victim's password.

First, SAML login enabled and configured with valid IdP metadata. Second, the attacker holds one valid SAML assertion for the victim: from intercepting the connection between the identity provider and the app (MITM), from a malicious browser extension, or from an assertion leaked in logs or in the Referer header. Third, they send the assertion twice inside the race window, which is under a second under realistic load.

## Why is revoking at the identity provider not enough?

Because the revocation reaches one session only, and the other stays open.

When the identity provider revokes the victim's session after the theft is found, the second session made from the same assertion keeps working. The victim and the security team think the revocation closed the case, and the attacker is still inside on a session the revocation never reached. That is the worst part of this flaw, because it gives the feeling the door is shut while it is still open.

We saw the same lesson with the [stolen UpdraftCentral keys](/articles/updraftcentral-key-compromise-backdoors): stolen material keeps working until you revoke it yourself.

## What did the fix in 8.5.0 change?

It flipped the order: it now adds the value first and catches the duplicate around `Auth::login()`, and the nonce column now has a UNIQUE index.

[The pull request that brought the fix](https://github.com/grokability/snipe-it/pull/18979) says the code now inserts a record into the `saml_nonces` table first, and if the database refuses it (most likely as a duplicate) the request fails. A UNIQUE constraint was also added to the column. According to the project's advisory, the constraint comes with a migration that first runs `saml:clear_expired_nonces` to clean out old rows, then applies the constraint.

With this order, the insert comes before the login, so the second request fails at the insert because the database itself refuses the duplicate value, and it never reaches the login. Another request can beat a check in code by a fraction of a second, while no request can beat a constraint in the database.

## What to do now?

Update first, then close every session if you suspect an assertion was captured.

1. Update to the latest release, which at the time of writing is [Snipe-IT 8.8.3](https://github.com/grokability/snipe-it/releases/tag/v8.8.3).
2. Install at least 8.5.0 if you cannot reach the latest release today, because [the project's advisory](https://github.com/grokability/snipe-it/security/advisories/GHSA-w49g-f2jc-qxhg) lists it as the patched version.
3. Search your web server logs for two or more `POST /saml/acs` requests in the same second: that is what an assertion sent more than once looks like.
4. Check that the session cookie itself is protected too, with the steps in [how to check your session cookie](/articles/session-cookie-flags-check).
5. Close every session after updating if you suspect an assertion was captured: the command `php artisan snipeit:global-logout` logs out all users, according to [the command's own code in Snipe-IT](https://github.com/grokability/snipe-it/blob/master/app/Console/Commands/KillAllSessions.php), and everyone signs in again. One revocation at the identity provider is not enough, because it does not reach the second session.

## When did you last check that your database index prevents duplicates?

If you cannot remember, check it today: any table that prevents duplicates with a check in code followed by an insert, with no UNIQUE constraint, is open to the same race that hit Snipe-IT.

If you run Snipe-IT with SAML, update today. Then search your code for the tables that hold one-time values, such as account activation codes or password reset links, and see whether the database itself refuses a duplicate value with a UNIQUE constraint.

If you want someone to check this on your platform before anyone else finds it, [Muhassan](/muhassan) audits your platform and hands you a clear fix plan.

## FAQ

**Which versions are affected?**

Every version up to 8.4.1 is affected, and the fix is in 8.5.0 and later. Better still, update to the latest release, not only to 8.5.0.

**Does the flaw work without SAML?**

No. SAML login must be enabled and configured with valid IdP metadata, and without it the affected path is not in use on your install. Update all the same: the 8.5.0 release notes say it addresses several security issues.

**Does the attacker need the victim's password?**

No. The conditions in the official advisory include no password: the attacker needs one valid SAML assertion for the victim, and sends it twice at the same moment to get two sessions.

**Why does the project's advisory say Moderate when the score is 8.7?**

Because the two ratings come from two different parties: the project's advisory on GitHub rates the flaw Moderate, and 8.7 is VulnCheck's score on CVSS 4.0, which is High. The step is the same: update.

**If I update, do old stolen sessions close?**

Not according to the official advisory: it does not say the update closes open sessions, and the fix stops replays from then on. If you suspect an assertion was captured, run php artisan snipeit:global-logout after updating; it closes every user's sessions, and everyone signs in again.
