-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
[Java] CodeQL query to detect Log Injection #3882
Conversation
from LogInjectionConfiguration cfg, DataFlow::PathNode source, DataFlow::PathNode sink | ||
where cfg.hasFlowPath(source, sink) | ||
select sink.getNode(), source, sink, "Outputting $@ to log.", source.getNode(), | ||
"sensitive information" |
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.
I guess this is a left-over from another query?
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.
Yes, you are right! I was inspired by a similar query (SensitiveInfoLog.ql
). Thanks for pointing this out. I fixed the message.
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.
#3090 already added some logger types and #3600 will add more.
Maybe it would make sense to define logger types at one place (though in a later pull request?) to prevent code duplication.
For example what about:
abstract class LoggerType extends RefType {
abstract Method getALoggingMethod();
}
Where getALoggingMethod()
then returns the respective logging methods for that type.
What do the maintainers of this project think about this?
<p>In the example, a username, provided by the user, is logged using `logger.warn` (from `org.slf4j.Logger`). | ||
In the first case (`\bad` endpoint), the username is logged without any sanitization. | ||
If a malicious user provides `Guest'%0AUser:'Admin` as a username parameter, | ||
the log entry will be splitted in two different lines, where the first line will be `User:'Guest', while the second one will be `User:'Admin'`. | ||
</p> | ||
<p> In the second case (`\good` endpoint), <code>replace</code> is used to ensure no line endings are present in the user input. | ||
If a malicious user provides `Guest'%0AUser:'Admin` as a username parameter, | ||
the log entry will not be splitted in two different lines, resulting in a single line `User:'Guest'User:'Admin'`. |
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.
Does `
work for formatting text as code, or does <code>...</code>
have to be used?
Either way, to be consistent, it would probably be good to use <code>
for formatting.
java/ql/src/experimental/Security/CWE/CWE-117/LogInjection.help
Outdated
Show resolved
Hide resolved
</p> | ||
<p> In the second case (`\good` endpoint), <code>replace</code> is used to ensure no line endings are present in the user input. | ||
If a malicious user provides `Guest'%0AUser:'Admin` as a username parameter, | ||
the log entry will not be splitted in two different lines, resulting in a single line `User:'Guest'User:'Admin'`. |
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.
two different lines
"two separate lines" might be more correct
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.
Thanks. I made the change.
java/ql/src/experimental/Security/CWE/CWE-117/LogInjection.help
Outdated
Show resolved
Hide resolved
<p>In the example, a username, provided by the user, is logged using `logger.warn` (from `org.slf4j.Logger`). | ||
In the first case (`\bad` endpoint), the username is logged without any sanitization. | ||
If a malicious user provides `Guest'%0AUser:'Admin` as a username parameter, | ||
the log entry will be splitted in two different lines, where the first line will be `User:'Guest', while the second one will be `User:'Admin'`. |
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.
splitted in two different lines
"two separate lines" might be more correct
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.
Thanks. I made the change.
import semmle.code.java.dataflow.FlowSources | ||
|
||
module LogInjection { | ||
string logLevel() { |
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.
JBoss Logging also has ...f
and ...v
variants.
* A class of popular logging utilities. | ||
*/ | ||
class LoggingType extends RefType { | ||
LoggingType() { |
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.
Would probably be good to cover the following ones as well:
- Log4j1:
org.apache.log4j.Category
- Log4j2:
org.apache.logging.log4j.LogBuilder
- SLF4J:
org.slf4j.spi.LoggingEventBuilder
- Apache Commons Logging:
org.apache.commons.logging.Log
- JBoss Logging:
org.jboss.logging.BasicLogger
And with that many types it might be good to add comments here explaining which type belongs to which framework, since especially for Log4j1, Log4j2 and Apache Commons Logging it can become quite confusing.
|
||
class TypeSanitizer extends Sanitizer { | ||
TypeSanitizer() { | ||
this.getType() instanceof PrimitiveType or this.getType() instanceof BoxedType |
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.
A primitive type could be '\n'
which is still problematic and not a sanitizier I assume
// /good?username=Guest'%0AUser:'Admin | ||
@GetMapping("/good") | ||
public String good(@RequestParam(value = "username", defaultValue = "name") String username) { | ||
username = username.replace("\n", ""); |
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.
Maybe it would be good if a sanitizer would detect this.
Though it might be difficult to reduce false positives without introducing false negatives at the same time.
public String good(@RequestParam(value = "username", defaultValue = "name") String username) { | ||
username = username.replace("\n", ""); | ||
log.warn("User:'{}'", username); | ||
return new String(username); |
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.
Why return a new string (same in line 27)? String
is immutable so there is no need to create a copy
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.
You are right. I was doing some experiments and I forgot to change the return type. I made the change. Thanks for all the other feedback. I really appreciated. I will do the other changes/suggestions as soon as I will have some time (unfortunately I am a little bit busy at the moment).
Co-authored-by: Marcono1234 <[email protected]>
Co-authored-by: Marcono1234 <[email protected]>
// /good?username=Guest'%0AUser:'Admin | ||
@GetMapping("/good") | ||
public String good(@RequestParam(value = "username", defaultValue = "name") String username) { | ||
username = username.replace("\n", ""); |
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.
Replacing \n
will likely not be enough, you probably also have to replace \r
Hi @Marcono1234, This mean that I will not be able to continue to work on this query in the next days/weeks/months. Thank you for all the feedback and sorry for the inconvenience. |
No problem and no worries. Note that I am not a member of this project so they might have different requirements for the queries. My review comments were mostly improvement suggestions. |
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 like a fine port of https://github.com/github/codeql/blob/main/csharp/ql/src/Security%20Features/CWE-117/LogForging.ql -- since my review suggests factoring in an existing logger definition with much more coverage, I'll wait for you to respond (either using it or opting to leave that until after merging to experimental) before I start an evaluation for this query.
</example> | ||
|
||
<references> | ||
<li>OWASP: <a href="https://www.owasp.org/index.php/Log_Injection">Log Injection</a>.</li> |
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.
Recommend linking straight to the new URL (https://owasp.org/www-community/attacks/Log_Injection) -- perhaps update other links in other qhelp files at the same time.
result = "trace" or | ||
result = "info" or | ||
result = "warn" or | ||
result = "error" or | ||
result = "fatal" |
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.
result = "trace" or | |
result = "info" or | |
result = "warn" or | |
result = "error" or | |
result = "fatal" | |
result in ["trace", "info", "warn", "error", "fatal"] |
/** | ||
* A class of popular logging utilities. | ||
*/ | ||
class LoggingType extends RefType { |
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.
Instead of defining this, suggest factoring out https://github.com/github/codeql/blob/main/java/ql/src/experimental/Security/CWE/CWE-532/SensitiveInfoLog.ql#L31 as well as the logging methods used there (though we may not need the restriction to only low-priority messages seen in that query)
I'd say it belongs at java/ql/src/semmle/code/java/dataflow/FlowSinks.qll
, alongside the existing FlowSources.qll
.
* An argument to a logging mechanism. | ||
*/ | ||
class LoggingSink extends Sink { | ||
LoggingSink() { this.asExpr() = any(LoggingCall console).getAnArgument() } |
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.
LoggingSink() { this.asExpr() = any(LoggingCall console).getAnArgument() } | |
LoggingSink() { this.asExpr() = any(LoggingCall lc).getAnArgument() } |
Since it's not necessarily a console
@dellalibera are you still working on this one? |
@smowton I apologize for the late reply. At the moment I'm very busy. To answer your question: I will not have enough time now to work on this PR (and also on others). Feel free to close it or reuse the code as a starting point for another PR. |
If you're likely to come back to it at some point I'm happy to leave open; if not then you can close it. |
Ok great. If in the next few months I will not come back on it, I will let know so we can close it. But for now, for me it's ok to leave it open. |
Superseded by #5099. |
Log Injection query is available in c# query, javascript (experimental) query but it is not available in java query.
I created a query to detect a log injection vulnerability in java code.
This query addresses the scenario where user controlled input is logged-as is.
Examples of scenarios detected:
Any feedback is welcome.
I hope this will help.