0%

日志使用总结

Android 日志最佳实践

  • 日志有哪些风险?
  • 怎么通过日志分析应用安全?
  • 应该如何正确的输出日志?
  • 关于日志的吐槽,碎碎念。

日志的风险

敏感信息泄漏

敏感信息的定义

Classifications of sensitive information differ by industry and country. In addition, organizations may take a restrictive view of sensitive data, and they may have a data classification policy that clearly defines sensitive information.

There are three general states from which data may be accessible:

  • At rest - the data is sitting in a file or data store
  • In use - an application has loaded the data into its address space
  • In transit - data has been exchanged between mobile app and endpoint or consuming processes on the device, e.g., during IPC (Inter-Process Communication)
    The degree of scrutiny that’s appropriate for each state may depend on the data’s importance and likelihood of being accessed. For example, data held in application memory may be more vulnerable than data on web servers to access via core dumps because attackers are more likely to gain physical access to mobile devices than to web servers.

    When no data classification policy is available, use the following list of information that’s generally considered sensitive:

  • user authentication information (credentials, PINs, etc.)
  • Personally Identifiable Information (PII) that can be abused for identity theft: social security numbers, credit card numbers, bank account numbers, health information
  • device identifiers that may identify a person
  • highly sensitive data whose compromise would lead to reputational harm and/or financial costs
  • any data whose protection is a legal obligation
  • any technical data generated by the application (or its related systems) and used to protect other data or the system itself (e.g., encryption keys).
    A definition of “sensitive data” must be decided before testing begins because detecting sensitive data leakage without a definition may be impossible.

日志测试

Overview
There are many legitimate reasons to create log files on a mobile device, such as keeping track of crashes, errors, and usage statistics. Log files can be stored locally when the app is offline and sent to the endpoint once the app is online. However, logging sensitive data may expose the data to attackers or malicious applications, and it violates user confidentiality. You can create log files in several ways. The following list includes two classes that are available for Android:

  • Log Class
  • Logger Class

    Use a centralized logging class and mechanism and remove logging statements from the production release because other applications may be able to read them.

    Static Analysis
    You should check the apps’ source code for logging mechanisms by searching for the following keywords:

  • Functions and classes, such as:
    • android.util.Log
    • Log.d | Log.e | Log.i | Log.v | Log.w | Log.wtf
    • Logger
  • Keywords and system output:
    • System.out.print | System.err.print
    • logfile
    • logging
    • logs

      While preparing the production release, you can use tools like ProGuard (included in Android Studio). ProGuard is a free Java class file shrinker, optimizer, obfuscator, and preverifier. It detects and removes unused classes, fields, methods, and attributes and can also be used to delete logging-related code.

      To determine whether all logging functions from the android.util.Log class have been removed, check the ProGuard configuration file (proguard-rules.pro) for the following options (according to this example of removing logging code and this article about enabling ProGuard in an Android Studio project):

-assumenosideeffects class android.util.Log
{
public static boolean isLoggable(java.lang.String, int);
public static int v(...);
public static int i(...);
public static int w(...);
public static int d(...);
public static int e(...);
public static int wtf(...);
}

Note that the example above only ensures that calls to the Log class’ methods will be removed. If the string that will be logged is dynamically constructed, the code that constructs the string may remain in the bytecode. For example, the following code issues an implicit StringBuilder to construct the log statement:

Example in Java:

Log.v("Private key tag", "Private key [byte format]: " + key);

Example in Kotlin:

Log.v("Private key tag", "Private key [byte format]: $key")

The compiled bytecode, however, is equivalent to the bytecode of the following log statement, which constructs the string explicitly:

Example in Java:

Log.v("Private key tag", new StringBuilder("Private key [byte format]: ").append(key.toString()).toString());

Example in Kotlin:

Log.v("Private key tag", StringBuilder("Private key [byte format]: ").append(key).toString())

ProGuard guarantees removal of the Log.v method call. Whether the rest of the code (new StringBuilder …) will be removed depends on the complexity of the code and the ProGuard version.

This is a security risk because the (unused) string leaks plain text data into memory, which can be accessed via a debugger or memory dumping.

Unfortunately, no silver bullet exists for this issue, but one option would be to implement a custom logging facility that takes simple arguments and constructs the log statements internally.

SecureLog.v("Private key [byte format]: ", key);

Then configure ProGuard to strip its calls.

Dynamic Analysis
Use all the mobile app functions at least once, then identify the application’s data directory and look for log files (/data/data/). Check the application logs to determine whether log data has been generated; some mobile applications create and store their own logs in the data directory.

Many application developers still use System.out.println or printStackTrace instead of a proper logging class. Therefore, your testing strategy must include all output generated while the application is starting, running and closing. To determine what data is directly printed by System.out.println or printStackTrace, you can use Logcat as explained in the chapter “Basic Security Testing”, section “Monitoring System Logs”.

Remember that you can target a specific app by filtering the Logcat output as follows:
?
$ adb logcat | grep "$(adb shell ps | grep <package-name> | awk '{print $2}')"
If you already know the app PID you may give it directly using –pid flag.

You may also want to apply further filters or regular expressions (using logcat’s regex flags -e , –regex= for example) if you expect certain strings or patterns to come up in the logs.

最佳实践

  • 不要将类名字符串直接作为日志TAG,这个字符串不会被混淆,会暴露原始源文件名称
  • 仅使用android.util.Log输出日志
  • 不使用System.out
  • 每个异常都需要捕获处理,不直接打印异常e.printStackTrace
  • 合理选择日志级别:ERROR,WARN,INFO,DEBUG,VERBOSE
  • 日志仅在debug版本中打印,release中利用proguard删除日志代码(或者release版本仅包含ERROR,WARN级别日志)
-assumenosideeffects class android.util.Log {
public static boolean isLoggable(java.lang.String, int);
public static int v(...);
public static int i(...);
public static int w(...);
public static int d(...);
public static int e(...);
}
  • Only log actionable messages to the console
    • 仅打印操作记录和错误信息,不要打印数据信息
    • 每个业务模块设置日志开关

If the logs contain messages that the user can safely ignore, then they will do so, and eventually their logs will be so chatty and verbose that they will miss the critical messages. Therefore, only log actual errors and actionable warnings (warnings that can always be dealt with and fixed).

Never log “informational” messages by default. It is possible that it may be useful to have messages on certain topics while debugging those topics. To deal with that, have debug flags you can enable that enable extra logging for particular topics. For example, setting debugPrintLayouts to true enables logging of layouts.

This also applies to our unopt builds. It’s annoying for other people on the team to have to wade through messages that aren’t directly relevant to their work. Rely on feature flags, not verbosity levels, when deciding to output messages. The one exception to this is reporting useful milestones; for example, the flutter tool in verbose mode (-v) reports meaningful steps that it is executing because those are almost always useful.

写入日志

Log.e(String, String)(错误)
Log.w(String, String)(警告)
Log.i(String, String)(信息)
Log.d(String, String)(调试)
Log.v(String, String)(详细)
  • Verbose:显示所有日志消息(默认值)。
  • Debug:显示仅在开发期间有用的调试日志消息,以及此列表中较低的消息级别。
  • Info:显示常规使用情况的预期日志消息,以及此列表中较低的消息级别。
  • Warn:显示尚不是错误的潜在问题,以及此列表中较低的消息级别。
  • Error:显示已经引发错误的问题,以及此列表中较低的消息级别。
  • Assert:显示开发者预计绝不会发生的问题。

除开发期间外,其他任何时候都绝不应将详细日志编译到您的应用中。虽然会编译调试日志,但会在运行时将其去掉,而错误、警告和信息日志会始终保留。

其他

可以使用warn日志级别来记录用户输入参数错误的情况,避免用户投诉时,无所适从。注意日志输出的级别,error级别只记录系统逻辑出错、异常。

what是日志的主体内容,应该简明扼要的描述发生的什么事情。要求可以通过日志本身,而不是重新阅读产生日志的代码,来大致搞清楚发生了什么事情。

log when u think something never happen

吐槽

  1. 日志是无法排查出问题的,日志是帮助从n多可能产生的问题场景中确认是哪个问题
  2. 意见反馈是给用户反馈问题的,不是用来上传debug日志处理bug用的。

参考