Skip to content
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

Rust: Query for cleartext logging of sensitive information #18582

Open
wants to merge 22 commits into
base: main
Choose a base branch
from

Conversation

geoffw0
Copy link
Contributor

@geoffw0 geoffw0 commented Jan 23, 2025

New query for cleartext logging of sensitive information in Rust. There are plans to add more sinks in future, but what's here should give us fairly good coverage.

@geoffw0 geoffw0 added no-change-note-required This PR does not need a change note Rust Pull requests that update Rust code labels Jan 23, 2025
@Copilot Copilot bot review requested due to automatic review settings January 23, 2025 18:04
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Copilot reviewed 13 out of 13 changed files in this pull request and generated 1 comment.

Tip: If you use Visual Studio Code, you can request a review from Copilot before you push from the "Source Control" tab. Learn more


predicate isAdditionalFlowStep(Node node1, Node node2) {
// flow from `a` to `&a`
node2.asExpr().getExpr().(RefExpr).getExpr() = node1.asExpr().getExpr()
Copy link
Contributor Author

Choose a reason for hiding this comment

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

We possibly want something like this as a general flow step. It matters for basically all the test cases that mention &password in the source I think. @paldepind what do you think?

Copy link
Contributor

Choose a reason for hiding this comment

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

Agree that adding this as an additional flow step shouldn't be necessary.

I actually would've thought that this should work. If password is tainted then &password should have taint stored in the reference and this implicit read step at the sink should ensure that it is read. I can look into it and see why it doesn't work.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, please do look into this, it looks like it should work to me as well.

predicate allowImplicitRead(DataFlow::Node node, DataFlow::ContentSet c) {
// flow out from tuple content at sinks.
isSink(node) and
c.getAReadContent() instanceof TuplePositionContent
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I couldn't get tuple content to work in the models-as-data sink itself, so this might have to do for now.

Copy link
Contributor

Choose a reason for hiding this comment

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

The Tuple[i] notation should work for summaries (there's a test here). But I'm not sure if it's also expected to work for sinks.

Copy link
Contributor Author

@geoffw0 geoffw0 Jan 24, 2025

Choose a reason for hiding this comment

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

I tried the Tuple[i] notation and couldn't get it to work. I'm not sure I could've messed up the syntax, it's pretty simple. So best guess, it doesn't work on sinks at the moment???

Copy link
Contributor

github-actions bot commented Jan 23, 2025

QHelp previews:

rust/ql/src/queries/security/CWE-312/CleartextLogging.qhelp

Cleartext logging of sensitive information

Sensitive user data and system information that is logged could be exposed to an attacker when it is displayed. Also, external processes often store the standard output and standard error streams of an application, which will include logged sensitive information.

Recommendation

Do not log sensitive data. If it is necessary to log sensitive data, encrypt it before logging.

Example

The following example code logs user credentials (in this case, their password) in plaintext:

let password = "P@ssw0rd";
info!("User password changed to {password}");

Instead, you should encrypt the credentials, or better still, omit them entirely:

let password = "P@ssw0rd";
info!("User password changed");

References

  • M. Dowd, J. McDonald and J. Schuhm, The Art of Software Security Assessment, 1st Edition, Chapter 2 - 'Common Vulnerabilities of Encryption', p. 43. Addison Wesley, 2006.
  • M. Howard and D. LeBlanc, Writing Secure Code, 2nd Edition, Chapter 9 - 'Protecting Secret Data', p. 299. Microsoft, 2002.
  • OWASP: Logging Cheat Sheet - Data to exclude.
  • Common Weakness Enumeration: CWE-312.
  • Common Weakness Enumeration: CWE-359.
  • Common Weakness Enumeration: CWE-532.

@geoffw0 geoffw0 added the ready-for-doc-review This PR requires and is ready for review from the GitHub docs team. label Jan 23, 2025
@paldepind paldepind self-assigned this Jan 24, 2025
rust/ql/test/query-tests/security/CWE-312/test_logging.rs Outdated Show resolved Hide resolved
predicate allowImplicitRead(DataFlow::Node node, DataFlow::ContentSet c) {
// flow out from tuple content at sinks.
isSink(node) and
c.getAReadContent() instanceof TuplePositionContent
Copy link
Contributor

Choose a reason for hiding this comment

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

The Tuple[i] notation should work for summaries (there's a test here). But I'm not sure if it's also expected to work for sinks.


predicate isAdditionalFlowStep(Node node1, Node node2) {
// flow from `a` to `&a`
node2.asExpr().getExpr().(RefExpr).getExpr() = node1.asExpr().getExpr()
Copy link
Contributor

Choose a reason for hiding this comment

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

Agree that adding this as an additional flow step shouldn't be necessary.

I actually would've thought that this should work. If password is tainted then &password should have taint stored in the reference and this implicit read step at the sink should ensure that it is read. I can look into it and see why it doesn't work.

@@ -0,0 +1,3 @@
extractionWarning
| test_logging.rs:90:12:90:30 | expected R_PAREN |
| test_logging.rs:90:12:90:30 | macro expansion failed: the macro '$crate::__private_api::format_args' expands to ERROR but a Expr was expected |
Copy link
Contributor

Choose a reason for hiding this comment

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

Looks like there's an error on line 90. Maybe it's the :?? I'm not familiar with the error! macro.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, I had assumed this must be an extractor bug, but I never actually ran rustc on this code...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've made the test runnable, and ran it. The syntax on line 90 is accepted by the compiler and produces the expected output (well, I think SimpleLogger is discarding the keys / values / structured part, presumably a more sophisticated logger would use it).

@hubwriter hubwriter added ready-for-doc-review This PR requires and is ready for review from the GitHub docs team. and removed ready-for-doc-review This PR requires and is ready for review from the GitHub docs team. labels Jan 24, 2025
@mchammer01 mchammer01 self-requested a review January 24, 2025 16:07
mchammer01
mchammer01 previously approved these changes Jan 24, 2025
Copy link
Contributor

@mchammer01 mchammer01 left a comment

Choose a reason for hiding this comment

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

LGTM from a Docs perspective ✨
Added two minor suggestions for your consideration.

@geoffw0
Copy link
Contributor Author

geoffw0 commented Jan 24, 2025

DCA shows 19 query results:

  • 6 false positives on diem: num_accounts, requested_accounts, total_requested_accounts are not actually account numbers.
  • 1 false positive for rust: unaccounted is not an account number either.
  • the other 12 results look like a mix of good and maybe/plausible results to me. 🎉

I'll address the clear FPs in a follow-up, since they're all shortcomings in the (shared) sensitive data heuristics.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation no-change-note-required This PR does not need a change note ready-for-doc-review This PR requires and is ready for review from the GitHub docs team. Rust Pull requests that update Rust code
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants