-
Notifications
You must be signed in to change notification settings - Fork 10.4k
#62732 Fix password validation in PasswordHasher`1: add bound check for salt size before array allocation #62734
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR adds an upper bound check on the salt length in VerifyHashedPasswordV3
to prevent unbounded memory allocation when verifying a potentially corrupted or malicious password hash.
- Introduces a new
MaxSaltSize
constant (8 KiB) - Extends the existing salt-length validation to fail if
saltLength
exceedsMaxSaltSize
Comments suppressed due to low confidence (1)
src/Identity/Extensions.Core/src/PasswordHasher.cs:252
- Add unit tests for
VerifyHashedPasswordV3
to cover cases wheresaltLength
exceedsMaxSaltSize
, verifying that the method returns false and avoids allocating large arrays.
const int MaxSaltSize = 1024 * 8; // 8 KiB
@@ -260,7 +262,7 @@ private static bool VerifyHashedPasswordV3(byte[] hashedPassword, string passwor | |||
int saltLength = (int)ReadNetworkByteOrder(hashedPassword, 9); | |||
|
|||
// Read the salt: must be >= 128 bits | |||
if (saltLength < 128 / 8) | |||
if (saltLength < 128 / 8 || saltLength > MaxSaltSize) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Extract the minimum salt size value (128 / 8) into a named constant (e.g., MinSaltSize
) to avoid magic numbers and improve readability.
if (saltLength < 128 / 8 || saltLength > MaxSaltSize) | |
if (saltLength < MinSaltSize || saltLength > MaxSaltSize) |
Copilot uses AI. Check for mistakes.
@@ -260,7 +262,7 @@ private static bool VerifyHashedPasswordV3(byte[] hashedPassword, string passwor | |||
int saltLength = (int)ReadNetworkByteOrder(hashedPassword, 9); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As stated in the issue, this isn't a real problem. However, I do appreciate being cautious as we've been bit by overallocating arrays before.
Having an arbitrary upper limit though isn't super clean. If someone wants to use a massive salt then we shouldn't disallow them unless it's part of the spec.
If we incorporated something like the below, that would be much better as the bounds checking would be more implicit and actually based on the data present.
byte[] salt = hashedPassword.Slice(13, saltLength).ToArray();
byte[] expectedSubkey = hashedPassword.Slice(13 + saltLength).ToArray();
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately, the ArraySegment
and Slice are not fully supported for target frameworks other than .net10, so I just added the hashedPassword.Length - 13
as an upper-bound check for the salt size, which would have same logic as suggested Slice approach.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use hashedPassword.AsSpan(13, saltLength).ToArray()
instead.
…or upper bound for salt size before allocation an array
742f022
to
a6489b1
Compare
Verification should fail if salt size is less that minimum allowed (16 bytes) or greater that entire data lenght
@dotnet-policy-service agree |
@@ -260,7 +260,7 @@ private static bool VerifyHashedPasswordV3(byte[] hashedPassword, string passwor | |||
int saltLength = (int)ReadNetworkByteOrder(hashedPassword, 9); | |||
|
|||
// Read the salt: must be >= 128 bits | |||
if (saltLength < 128 / 8) | |||
if (saltLength < 128 / 8 || saltLength + 13 > hashedPassword.Length) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI this second clause should read (long)saltLength + 13 > hashedPassword.Length
or saltLength > hashedPassword.Length - 13
. Otherwise you're potentially subject to integer overflow.
Add bound check for salt size before array allocation
Description
To prevent memory allocation of arbitrary-sized array, in case when password hash is corrupted or intentionally modified.
Bytes 9-13 of password hash (
AspNetUsers.PasswordHash
) contain a 32-bit number. It is read during password verification, and array of that size is immediately allocated.Fixes #62732