Skip to content
This repository has been archived by the owner on Aug 11, 2024. It is now read-only.

Refactor logging to avoid leaking sensitive data #906

Merged
merged 8 commits into from
Sep 28, 2023

Conversation

gordonbrander
Copy link
Collaborator

@gordonbrander gordonbrander commented Sep 27, 2023

Fixes #905.

Refactors logging to make it easier to avoid logging sensitive data.

Changes:

  • Remove CustomLogStringConvertible. This was too fancy, and had fallbacks which made it difficult to reason about where and how to redact.
    • Replace all calls to String.loggable() with String(describing:).
    • Use default string representation for actions in more places.
    • Relatedly, give actions their own loggers. Actions will be chattier. Giving them a separate logger makes for easier filtering.
  • Introduce @Redacted property decorator. Apply to any property to implement CustomStringConvertible and CustomDebugStringConvertible to return the literal string "--redacted"
  • Refactor RecoveryPhrase to use @Redacted
    • Previously this was redacting debugDescription, but not description. This change redacts both, so you have to be very specific that you want the string value of the mnemonic by referencing the property directly.
  • Update FormField to redact all strings for value actions. We should not be logging user's input for most fields anyway, and validated form fields are most likely to contain sensitive information.
  • Update Noosphere and NoosphereService to receive mnemonic as String (this matches other parts of the API)

To prevent log leak, we need to reduce the number of code paths things
go through. This protocol was too much magic.

Logging favors `CustomStringConvertible`, since interpolation knows
how to do that automatically for most types. Given this is the case, we
will standardize on using String(describing:) when logging.

I've piped actions through a separate `Action.logger`. They'll be
chattier now that we aren't changing their description. Separate
loggers allow for better filtering when needed.
This affords us an extra layer of "the right thing by default". By
wrapping a property, you define CustomStringConvertible and
CustomDebugStringConvertible values that redact the underlying string.
This prevents leakage via actions.
This matches our use in other parts of the API.
static let logger = Logger(
subsystem: Config.default.rdns,
category: "AppAction"
)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Give actions their own logger. Standardizing on this new pattern for actions, at least until we address subconsciousnetwork/ObservableStore#38.

case .focusChange(let focused):
return "focusChange(\(focused)"
case .setValue:
return "setValue(--redacted--)"
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Manually redact setValue. It's too hard to get away from strings for validated form fields, since most of our custom types return nil for invalid values, and we need the input to support intermediate values.

Validated form fields are more likely than other places to contain sensitive information which could leak through this action, so the easiest thing to do is redact all setValue() actions.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call, I don't think we need to see the value anyway. It's always reflected on the UI in the context that these actions are generated.

public let verbatim: String
public var id: String { description }
// !!!: Mnemonic is secret and should never be logged or persisted
@Redacted public private(set) var mnemonic: String
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Create an explicit field for the secret mnemonic and redact it. This ensures you must access the property explicitly.

Calling String(describing: recoveryPhrase) will not accidentally reveal the mnemonic.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice.

@gordonbrander gordonbrander mentioned this pull request Sep 27, 2023
4 tasks
Copy link
Collaborator

@bfollington bfollington left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, I checked it out and playtested and couldn't leak anything. I definitely prefer this approach.

@bfollington bfollington merged commit ae98fdc into main Sep 28, 2023
@bfollington bfollington deleted the 2023-09-27-do-not-leak-recovery-code-through-inputs branch September 28, 2023 00:56
bfollington added a commit that referenced this pull request Sep 28, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
Status: 🌱 Done
Development

Successfully merging this pull request may close these issues.

Recovery phrase leaks through input field actions
2 participants