Insufficiently protected credentials
Description
When creating or updating credentials for single-user access, Apache NiFi wrote a copy of the Login Identity Providers configuration to the operating system temporary directory. On most platforms, the operating system temporary directory has global read permissions. NiFi immediately moved the temporary file to the final configuration directory, which significantly limited the window of opportunity for access. NiFi 1.16.0 includes updates to replace the Login Identity Providers configuration without writing a file to the operating system temporary directory.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Apache NiFi before 1.16.0 wrote Login Identity Providers config to a world-readable temporary directory during credential updates, risking information disclosure.
Vulnerability
Apache NiFi prior to version 1.16.0, when creating or updating credentials for single-user access, wrote a copy of the Login Identity Providers configuration to the operating system temporary directory, which typically has global read permissions on most platforms. The file was then moved immediately to the final configuration directory, but the temporary file remained briefly with world-readable permissions [1][2]. The vulnerability affects versions before 1.16.0.
Exploitation
An attacker with local access to the system could read the temporary file during the short window before it is moved. No authentication is required beyond local file system access. The attacker would need to monitor the temporary directory for new files and read the configuration before it is deleted (though the file is moved, not deleted, but the temporary file path is known). The race window is limited but exploitable if the attacker can continuously scan the directory [2][3].
Impact
Successful exploitation could lead to disclosure of the Login Identity Providers configuration, which may contain sensitive information such as usernames, password hashes, or other authentication-related data. This could allow an attacker to gain further access to the NiFi instance. The impact is limited to information disclosure, as the temporary file is removed after move [1][2].
Mitigation
The fix is included in Apache NiFi version 1.16.0, released March 2022, which rewrites the configuration without writing a temporary file to disk [1][3]. Users should upgrade to 1.16.0 or later. No workarounds are documented for earlier versions. This vulnerability is not listed on the CISA KEV at the time of this writing.
AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.apache.nifi:nifi-single-user-utilsMaven | < 1.16 | 1.16 |
Affected products
3- osv-coords2 versions
>= 1.14.0, < 1.16.0+ 1 more
- (no CPE)range: >= 1.14.0, < 1.16.0
- (no CPE)range: < 1.16
- Apache Software Foundation/Apache NiFiv5Range: NiFi 1.14.0 to 1.15.3
Patches
2859d5fe8cfe0NIFI-9785 Improved Login Credentials Writer File Handling
2 files changed · +35 −19
nifi-commons/nifi-single-user-utils/src/main/java/org/apache/nifi/authentication/single/user/writer/StandardLoginCredentialsWriter.java+18 −19 modified@@ -29,26 +29,20 @@ import javax.xml.stream.events.EndElement; import javax.xml.stream.events.StartElement; import javax.xml.stream.events.XMLEvent; +import java.io.ByteArrayInputStream; import java.io.File; -import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.UncheckedIOException; import java.nio.file.Files; -import java.nio.file.StandardCopyOption; import java.util.Iterator; /** * Standard Login Credentials Writer updates Login Identity Providers Single User definition with Login Credentials */ public class StandardLoginCredentialsWriter implements LoginCredentialsWriter { - - private static final String PROVIDERS_PREFIX = "login-identity-providers-"; - - private static final String PROVIDERS_SUFFIX = ".xml"; - private static final String CLASS_TAG = "class"; private static final String PROVIDER_TAG = "provider"; @@ -71,25 +65,30 @@ public StandardLoginCredentialsWriter(final File providersFile) { @Override public void writeLoginCredentials(final SingleUserCredentials singleUserCredentials) { - try { - final File updatedProvidersFile = File.createTempFile(PROVIDERS_PREFIX, PROVIDERS_SUFFIX); - writeLoginCredentials(singleUserCredentials, updatedProvidersFile); - Files.move(updatedProvidersFile.toPath(), providersFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + final byte[] providers = readProviders(); + try (final InputStream providersInputStream = new ByteArrayInputStream(providers)) { + writeLoginCredentials(singleUserCredentials, providersInputStream); } catch (final IOException e) { throw new UncheckedIOException("Writing Login Identity Providers Failed", e); } catch (final XMLStreamException e) { throw new RuntimeException("Processing Login Identity Providers Failed", e); } } - private void writeLoginCredentials(final SingleUserCredentials singleUserCredentials, final File updatedProvidersFile) throws IOException, XMLStreamException { - try (final OutputStream outputStream = new FileOutputStream(updatedProvidersFile)) { + private byte[] readProviders() { + try { + return Files.readAllBytes(providersFile.toPath()); + } catch (final IOException e) { + throw new UncheckedIOException("Reading Login Identity Providers Failed", e); + } + } + + private void writeLoginCredentials(final SingleUserCredentials singleUserCredentials, final InputStream inputStream) throws IOException, XMLStreamException { + try (final OutputStream outputStream = new FileOutputStream(providersFile)) { final XMLEventWriter providersWriter = getProvidersWriter(outputStream); - try (final InputStream inputStream = new FileInputStream(providersFile)) { - final XMLEventReader providersReader = getProvidersReader(inputStream); - updateLoginIdentityProviders(singleUserCredentials, providersReader, providersWriter); - providersReader.close(); - } + final XMLEventReader providersReader = getProvidersReader(inputStream); + updateLoginIdentityProviders(singleUserCredentials, providersReader, providersWriter); + providersReader.close(); providersWriter.close(); } } @@ -131,7 +130,7 @@ private void updateLoginIdentityProviders(final SingleUserCredentials singleUser * * @param providersReader Providers Reader * @param providersWriter Providers Writer - * @param propertyValue Property Value to be added + * @param propertyValue Property Value to be added * @throws XMLStreamException Thrown on XMLEventReader.nextEvent() */ private void processProperty(final XMLEventReader providersReader, final XMLEventWriter providersWriter, final String propertyValue) throws XMLStreamException {
nifi-commons/nifi-single-user-utils/src/test/java/org/apache/nifi/authentication/single/user/writer/StandardLoginCredentialsWriterTest.java+17 −0 modified@@ -19,14 +19,18 @@ import org.apache.nifi.authentication.single.user.SingleUserCredentials; import org.junit.jupiter.api.Test; +import java.io.File; import java.io.IOException; +import java.io.UncheckedIOException; import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.util.UUID; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; public class StandardLoginCredentialsWriterTest { @@ -38,6 +42,19 @@ public class StandardLoginCredentialsWriterTest { private static final String PROVIDER_CLASS = SingleUserCredentials.class.getName(); + @Test + public void testWriteLoginCredentialsProvidersNotFound() { + final File providersNotFound = new File(UUID.randomUUID().toString()); + assertFalse(providersNotFound.exists()); + + final StandardLoginCredentialsWriter writer = new StandardLoginCredentialsWriter(providersNotFound); + + final String username = UUID.randomUUID().toString(); + final String password = UUID.randomUUID().toString(); + final SingleUserCredentials credentials = new SingleUserCredentials(username, password, PROVIDER_CLASS); + assertThrows(UncheckedIOException.class, () -> writer.writeLoginCredentials(credentials)); + } + @Test public void testWriteLoginCredentialsBlankProviders() throws IOException, URISyntaxException { final Path sourceProvidersPath = Paths.get(getClass().getResource(BLANK_PROVIDERS).toURI());
859d5feNIFI-9785 Improved Login Credentials Writer File Handling
2 files changed · +35 −19
nifi-commons/nifi-single-user-utils/src/main/java/org/apache/nifi/authentication/single/user/writer/StandardLoginCredentialsWriter.java+18 −19 modified@@ -29,26 +29,20 @@ import javax.xml.stream.events.EndElement; import javax.xml.stream.events.StartElement; import javax.xml.stream.events.XMLEvent; +import java.io.ByteArrayInputStream; import java.io.File; -import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.UncheckedIOException; import java.nio.file.Files; -import java.nio.file.StandardCopyOption; import java.util.Iterator; /** * Standard Login Credentials Writer updates Login Identity Providers Single User definition with Login Credentials */ public class StandardLoginCredentialsWriter implements LoginCredentialsWriter { - - private static final String PROVIDERS_PREFIX = "login-identity-providers-"; - - private static final String PROVIDERS_SUFFIX = ".xml"; - private static final String CLASS_TAG = "class"; private static final String PROVIDER_TAG = "provider"; @@ -71,25 +65,30 @@ public StandardLoginCredentialsWriter(final File providersFile) { @Override public void writeLoginCredentials(final SingleUserCredentials singleUserCredentials) { - try { - final File updatedProvidersFile = File.createTempFile(PROVIDERS_PREFIX, PROVIDERS_SUFFIX); - writeLoginCredentials(singleUserCredentials, updatedProvidersFile); - Files.move(updatedProvidersFile.toPath(), providersFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + final byte[] providers = readProviders(); + try (final InputStream providersInputStream = new ByteArrayInputStream(providers)) { + writeLoginCredentials(singleUserCredentials, providersInputStream); } catch (final IOException e) { throw new UncheckedIOException("Writing Login Identity Providers Failed", e); } catch (final XMLStreamException e) { throw new RuntimeException("Processing Login Identity Providers Failed", e); } } - private void writeLoginCredentials(final SingleUserCredentials singleUserCredentials, final File updatedProvidersFile) throws IOException, XMLStreamException { - try (final OutputStream outputStream = new FileOutputStream(updatedProvidersFile)) { + private byte[] readProviders() { + try { + return Files.readAllBytes(providersFile.toPath()); + } catch (final IOException e) { + throw new UncheckedIOException("Reading Login Identity Providers Failed", e); + } + } + + private void writeLoginCredentials(final SingleUserCredentials singleUserCredentials, final InputStream inputStream) throws IOException, XMLStreamException { + try (final OutputStream outputStream = new FileOutputStream(providersFile)) { final XMLEventWriter providersWriter = getProvidersWriter(outputStream); - try (final InputStream inputStream = new FileInputStream(providersFile)) { - final XMLEventReader providersReader = getProvidersReader(inputStream); - updateLoginIdentityProviders(singleUserCredentials, providersReader, providersWriter); - providersReader.close(); - } + final XMLEventReader providersReader = getProvidersReader(inputStream); + updateLoginIdentityProviders(singleUserCredentials, providersReader, providersWriter); + providersReader.close(); providersWriter.close(); } } @@ -131,7 +130,7 @@ private void updateLoginIdentityProviders(final SingleUserCredentials singleUser * * @param providersReader Providers Reader * @param providersWriter Providers Writer - * @param propertyValue Property Value to be added + * @param propertyValue Property Value to be added * @throws XMLStreamException Thrown on XMLEventReader.nextEvent() */ private void processProperty(final XMLEventReader providersReader, final XMLEventWriter providersWriter, final String propertyValue) throws XMLStreamException {
nifi-commons/nifi-single-user-utils/src/test/java/org/apache/nifi/authentication/single/user/writer/StandardLoginCredentialsWriterTest.java+17 −0 modified@@ -19,14 +19,18 @@ import org.apache.nifi.authentication.single.user.SingleUserCredentials; import org.junit.jupiter.api.Test; +import java.io.File; import java.io.IOException; +import java.io.UncheckedIOException; import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.util.UUID; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; public class StandardLoginCredentialsWriterTest { @@ -38,6 +42,19 @@ public class StandardLoginCredentialsWriterTest { private static final String PROVIDER_CLASS = SingleUserCredentials.class.getName(); + @Test + public void testWriteLoginCredentialsProvidersNotFound() { + final File providersNotFound = new File(UUID.randomUUID().toString()); + assertFalse(providersNotFound.exists()); + + final StandardLoginCredentialsWriter writer = new StandardLoginCredentialsWriter(providersNotFound); + + final String username = UUID.randomUUID().toString(); + final String password = UUID.randomUUID().toString(); + final SingleUserCredentials credentials = new SingleUserCredentials(username, password, PROVIDER_CLASS); + assertThrows(UncheckedIOException.class, () -> writer.writeLoginCredentials(credentials)); + } + @Test public void testWriteLoginCredentialsBlankProviders() throws IOException, URISyntaxException { final Path sourceProvidersPath = Paths.get(getClass().getResource(BLANK_PROVIDERS).toURI());
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
7- github.com/advisories/GHSA-rvp4-r3g6-8hxqghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2022-26850ghsaADVISORY
- www.openwall.com/lists/oss-security/2022/04/06/2ghsamailing-listx_refsource_MLISTWEB
- github.com/JLLeitschuh/security-research/security/advisories/GHSA-rvp4-r3g6-8hxqghsaWEB
- github.com/apache/nifi/commit/859d5feghsaWEB
- github.com/apache/nifi/commit/859d5fe8cfe05ad24600b021f0ebf15753a8105cghsaWEB
- nifi.apache.org/security.htmlghsax_refsource_MISCWEB
News mentions
0No linked articles in our index yet.