-
Notifications
You must be signed in to change notification settings - Fork 762
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
Don't leak OTP in logs #3123
Don't leak OTP in logs #3123
Conversation
Codecov ReportPatch coverage has no change and project coverage change:
Additional details and impacted files@@ Coverage Diff @@
## develop #3123 +/- ##
===========================================
- Coverage 71.66% 71.65% -0.01%
===========================================
Files 449 452 +3
Lines 12815 12850 +35
===========================================
+ Hits 9184 9208 +24
- Misses 3631 3642 +11 see 7 files with indirect coverage changes Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here. ☔ View full report in Codecov by Sentry. |
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.
Cool idea! My concern, however, is there's no simple way to verify this automatically. Small changes could cause OTPs to leak and we might not notice for quite a while.
I think we can keep this in place for "defense-in-depth" purposes, but I think we need another approach as well.
Maybe commands should only be logged by command builder functions (which can be reused by different exploiters). The command builder function could log the command without the OTP redacted but return the entire command back to the caller.
Another option would be for command builders to return an object whose __str__()
method redacts the OTP but you can call a specific method on the object to get back the full command. I kind of like this option. It adds more structure to the command and could be useful in the future.
This solution seems a bit fragile. IF the OTP is logged in an error stack trace (without AGENT_OTP_ENVIRONMENT_VARIABLE_NAME) it will get logged plain text? The best solution I came up on top of my mind is to create a "ISecretVariable" interface. A specific implementation of that interface could use SecretStr. If we store the OTP in this type there's no chance it will get logged. |
from common.utils.i_secret_variable import ISecretVariable | ||
|
||
|
||
class SecretVariable(ISecretVariable): |
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.
Theoretically this should be called something like PydanticSecretVariable
, but It's unlikely that we'll need multiple implementations of ISecretVariable
, so I kept the name simple
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.
If it's unlikely that we'll need multiple implementations, why do we have an interface at all?
If we do decide to keep it, I say we rename this to PydanticSecretVariable
.
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.
It somewhat achieves dependency inversion, which has all kinds of benefits and some drawbacks. The most trivial benefit is for example, that we would be able to create a test MockSecretVariable
and pass it wherever ISecretVariable
is expected. If there's no interface, we would have to mock the actual SecretVariable
. You are right, we don't need the interface ATM, but I think it's better to have it and not need it, than to need it and not have it.
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.
Fair enough. I think we should rename it then.
30ab2fe
to
20300c0
Compare
# Custom error message to avoid leaking the OTP | ||
raise IslandAPIAuthenticationError("Failed to retrieve the authentication token") |
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.
We have no control over what happens in the HTTPClient
, but we can suppress the error message
def format(self, record): | ||
otp_regex = re.compile(f"{AGENT_OTP_ENVIRONMENT_VARIABLE}=[a-zA-Z0-9]*") | ||
otp_replacement = f"{AGENT_OTP_ENVIRONMENT_VARIABLE}={'*' * 6}" |
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.
Do we still need this? I guess it's unlikely that f"{AGENT_OTP_ENVIRONMENT_VARIABLE}=[a-zA-Z0-9]*"
will appear in the logs (because that's not the __repr__
of the OTP)
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.
It provides us with defense-in-depth. Unless it's causing issues, I vote we leave it.
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.
That's what I'm saying, it looks like the "depth" we gain is not worth the code we want to add. We are always running against the clock, maintaining the "depth" is not always worth it.
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.
Looks good, but rebase on develop
.
This secret variable is a utility to gide output of variables into logs
Casting OTP and using it as a SecretVariable means that OTP won't get logged
Theoretically SecretVariable should support any data type, intergace should reflect it
3a7300a
to
d7e6869
Compare
8b0b79c
to
ccf0aba
Compare
What does this PR do?
Fixes a part of #3077
PR Checklist
Was the CHANGELOG.md updated to reflect the changes?Was the documentation framework updated to reflect the changes?Testing Checklist
Added relevant unit tests?Do all end-to-end tests pass?If applicable, add screenshots or log transcripts of the feature working