Skip to content

[Correctness] SecureData.Decrypt — on crypto failure of marked payload, returns the post-marker payload (not the documented 'original cipherText') #848

@Christophe-Rogiers

Description

@Christophe-Rogiers

Severity: Warning

File: src/Servy.Core/Security/SecureData.cs
Lines: 161, 174-181, 211-214

Description:
The XML doc on Decrypt(string cipherText) advertises a defensive fallback contract:

/// <returns>The decrypted plain text if successful; otherwise, the original <paramref name=\"cipherText\"/>.</returns>

The catch block backs that up with a log line that says the same thing:

catch (Exception ex) when (ex is FormatException || ex is CryptographicException)
{
    Logger.Warn($\"Decryption failed for marked payload ({ex.GetType().Name}): {ex.Message} Returning original input.\");
    return payload.ToString();
}

But payload is not the original cipherText. Earlier in the method:

ReadOnlySpan<char> textSpan = cipherText.AsSpan();
ReadOnlySpan<char> markerSpan = EncryptMarker.AsSpan();
bool hasMarker = textSpan.StartsWith(markerSpan, StringComparison.Ordinal);
ReadOnlySpan<char> payload = hasMarker ? textSpan.Slice(markerSpan.Length) : textSpan;

So when the input was \"SERVY_ENC:v2:Z29vZA==...\" and decryption fails (e.g. the AES key file was rotated or replaced), the fallback returns \"v2:Z29vZA==...\" — the marker prefix is stripped silently. The log message claims "Returning original input" but the actual return value is not the original input.

Operational consequences:

  • Callers that round-trip values through Decrypt then re-store them (e.g. an export-then-import flow that hits a corrupted record) will silently rewrite \"SERVY_ENC:v2:...\"\"v2:...\", breaking subsequent decryption attempts entirely.
  • Operators reading the warning log can't reconcile what they see in the DB column with what the catch block actually emitted downstream.

Suggested fix:
Return cipherText (the original parameter), not payload.ToString():

catch (Exception ex) when (ex is FormatException || ex is CryptographicException)
{
    Logger.Warn($\"Decryption failed for marked payload ({ex.GetType().Name}): {ex.Message} Returning original input.\");
    return cipherText;
}

If returning the post-marker payload is intentional (e.g. for a specific legacy migration path), update both the XML doc and the log message to reflect that.

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions