安卓應用安全指南4.8輸出到LogCat
4.8 輸出到 LogCat
原書:Android Application Secure Design/Secure Coding Guidebook
譯者:飛龍
在 Android 中有一種名為 LogCat 的日誌機制,不僅系統日誌資訊,還有應用日誌資訊也會輸出到 LogCat。 LogCat 中的日誌資訊可以從同一裝置中的其他應用中讀出 [17],因此向L ogcat 輸出敏感資訊的應用,被認為具有資訊洩露的漏洞。 敏感資訊不應輸出到 LogCat。
[17] 輸出到 LogCat 的日誌資訊,可以由宣告
READ_LOGS
許可權的應用讀取。 但是,在 Android 4.1 及更高版本中,無法讀取其他應用輸出的日誌資訊。 但智慧手機使用者可以通過 ADB ,閱讀輸出到 logcat 的每個日誌資訊。
從安全形度來看,在發行版應用中,最好不要輸出任何日誌。 但是,即使在發行版應用的情況下,在某些情況下也會出於某種原因輸出日誌。 在本章中,我們將介紹一些方法,以安全的方式將訊息輸出到 LogCat,即使在發行版應用中也是如此。 除此解釋外,請參考“4.8.3.1 發行版應用中日誌輸出的兩種思路”。
4.8.1 示例程式碼
接下來是在發行版應用中,通過 ProGuard 控制輸出到 LogCat 的日誌的方法。 ProGuard 是自動刪除不需要的程式碼(如未使用的方法等)的優化工具之一。
android.util.Log
類有五種型別的日誌輸出方法:Log.e()
, Log.w()
, Log.i()
, Log.d()
, Log.v()
。對於日誌資訊,有意輸出的日誌資訊(以下稱為“操作日誌資訊”)應該區分於不適合發行版應用的資訊(以下稱為開發日誌資訊),例如除錯日誌。建議使用Log.e()/w()/i()
輸出操作日誌資訊,並使用Log.d()/v()
輸出開發日誌。正確使用五種日誌輸出方法的詳細資訊,請參閱“4.8.3.2 日誌級別和日誌輸出方法的選擇標準”,另外請參考“4.8.3.3 除錯日誌和VERBOSE
日誌並不總是自動刪除”。
這是一個以安全方式使用 LogCat 的例子。此示例包括用於輸出除錯日誌的Log.d()
和Log.v()
。如果應用用於釋出,這兩種方法將被自動刪除。在此示例程式碼中,ProGuard 用於自動刪除呼叫Log.d()/v()
的程式碼塊。
要點:
1) 敏感資訊不能由Log.e()/w()/i()
,System.out / err
輸出。
2) 敏感資訊應由Log.d()/v()
在需要時輸出。
3) 不應使用Log.d()/v()
的返回值(以替換或比較為目的)。
4) 當你構建應用來發布時,你應該在程式碼中引入機制,自動刪除不合適的日誌記錄方法(如Log.d()
或Log.v()
)。
5) 必須使用發行版構建配置來建立用於(釋出)發行的 APK 檔案。
ProGuardActivity.java
package org.jssec.android.log.proguard;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
public class ProGuardActivity extends Activity {
final static String LOG_TAG = "ProGuardActivity";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_proguard);
// *** POINT 1 *** Sensitive information must not be output by Log.e()/w()/i(), System.out/err.
Log.e(LOG_TAG, "Not sensitive information (ERROR)");
Log.w(LOG_TAG, "Not sensitive information (WARN)");
Log.i(LOG_TAG, "Not sensitive information (INFO)");
// *** POINT 2 *** Sensitive information should be output by Log.d()/v() in case of need.
// *** POINT 3 *** The return value of Log.d()/v()should not be used (with the purpose of substitution or comparison).
Log.d(LOG_TAG, "sensitive information (DEBUG)");
Log.v(LOG_TAG, "sensitive information (VERBOSE)");
}
}
proguard-project.txt
# prevent from changing class name and method name etc.
-dontobfuscate
# *** POINT 4 *** In release build, the build configurations in which Log.d()/v() are deleted automatica
lly should be constructed.
-assumenosideeffects class android.util.Log {
public static int d(...);
public static int v(...);
}
要點 5:必須使用發行版構建配置來建立用於(釋出)發行的 APK 檔案。
開發版應用(除錯版本)和發行版應用(釋出版本)之間的LogCat 輸出差異如下圖 4.8-2 所示。
4.8.2 規則書
輸出訊息記錄時,遵循以下規則:
4.8.2.1 操作日誌資訊中不能包含敏感資訊(必需)
輸出到 LogCat 的日誌可以從其他應用中讀取,因此敏感資訊(如使用者的登入資訊)不應該由發行版應用輸出。 在開發過程中,不必編寫輸出敏感資訊的程式碼,或者在釋出之前需要刪除所有這些程式碼。
為了遵循這個規則,首先,不要在操作日誌資訊中包含敏感資訊。 此外,建議構建系統,在構建發行版時,刪除輸出敏感資訊的程式碼。 請參閱“4.8.2.2 構建生成系統,在構建發行版時,自動刪除輸出開發日誌資訊的程式碼(推薦)”。
4.8.2.2 構建生成系統,在構建發行版時,自動刪除輸出開發日誌資訊的程式碼(推薦)
開發應用時,有時最好將敏感資訊輸出到日誌中,來檢查過程內容和除錯,例如複雜邏輯過程中的臨時操作結果,程式內部狀態資訊,通訊協議的資料結構。在開發過程中,將敏感資訊作為除錯日誌輸出並不重要,在這種情況下,相應的日誌輸出程式碼應該在釋出之前刪除,如“4.8.2.1 操作日誌資訊中不能包含敏感資訊(必需)”所述。
為了在構建發行版時,確實刪除了輸出開發日誌資訊的程式碼,應該構建系統,使用某些工具自動執行程式碼刪除。 “4.8.1 示例程式碼”中介紹的 ProGuard 可以用於此方法。如下所述,用 ProGuard 刪除程式碼有一些值得注意的地方。這裡應該將系統用於一些應用,它通過Log.d()/ v()
輸出開發日誌資訊,根據“4.8.3.2 日誌級別和日誌輸出方法的選擇標準”。
ProGuard 會自動刪除不需要的程式碼,如未使用的方法。通過指定Log.d()/ v()
作為-assumenosideeffects
選項的引數,Log.d(),
Log.v()`的呼叫被視為不必要的程式碼,並且這些程式碼將被刪除。
-assumenosideeffects class android.util.Log {
public static int d(...);
public static int v(...);
}
如果使用這個自動刪除系統,請注意Log.d()
,Log.v()
程式碼在使用其返回值時不會被刪除,因此不應該使用Log.d()
,Log.v()
的返回值。 例如,下一個程式碼中的Log.v()
不會被刪除。
int i = android.util.Log.v("tag", "message");
System.out.println(String.format("Log.v() returned %d. ", i)); //Use the returned value of Log.v() for examination
如果你想重複使用原始碼,則應保持專案環境的一致性,包括 ProGuard 設定。 例如,預設Log.d()
和Log.v()
的原始碼將被上面的 ProGuard 設定自動刪除。 如果在未設定 ProGuard 的其他專案中使用此原始碼,則不會刪除Log.d()
和Log.v()
,因此可能會洩露敏感資訊。 重用原始碼時,應確保包括 ProGuard 設定在內的專案環境的一致性。
4.8.2.3 輸出Throwable
物件時,使用Log.d()/v()
(推薦)
如“4.8.1 示例程式碼”和“4.8.3.2 日誌級別和日誌輸出方法的選擇標準”中所述,輸出敏感資訊不應通過Log.e()/w()/i()
輸出來記錄。 另一方面,為了使開發者輸出程式異常的細節來記錄,當異常發生時,在某些情況下,堆疊蹤跡通過Log.e(..., Throwable tr)/w(..., Throwable tr)/i(..., Throwable
輸出到 LogCat。 但是,敏感資訊有時可能包含在堆疊蹤跡中,因為它顯示程式的詳細內部結構。 例如,當
tr)SQLiteException
按原樣輸出時,會輸出 SQL 語句的型別,因此可能會提供 SQL 注入攻擊的線索。 因此,建議在輸出Throwable
物件時,僅使用Log.d()/v()
方法。
4.8.2.4 僅僅將android.util.Log
類的方法用於日誌輸出(推薦)
在開發過程中,你可以通過System.out / err
輸出日誌,來驗證應用的行為是否按預期工作。 當然,日誌可以通過System.out / err
的print()/ println()
方法輸出到 LogCat,但強烈建議僅使用android.util.Log
類的方法,原因如下。
在輸出日誌時,一般根據資訊的緊急程度,正確使用最合適的輸出方法,並控制輸出。 例如,使用嚴重錯誤,注意,簡單應用的資訊通知等類別。 然而,在這種情況下,在釋出時需要輸出的資訊(操作日誌資訊),和可以包括敏感資訊(開發日誌資訊)的資訊,通過相同的方法輸出。 所以,當刪除輸出敏感資訊的程式碼時,可能會存在一些刪除操作被忽略掉的危險。
除此之外,當使用android.util.Log
和System.out / err
進行日誌輸出時,與僅使用android.util.Log
相比,需要考慮的因素會增加,因此可能會出現一些錯誤,比如 一些刪除被忽略掉了。
為了減少上述錯誤發生的風險,建議僅使用android.util.Log
類的方法。
4.8.3 高階話題
4.8.3.1 釋出版應用中日誌輸出的兩種思路
釋出版應用中有兩種思考日誌輸出的方式。一個是任何日誌都不應該輸出,另一個是用於以後分析的必要資訊應該作為日誌輸出。從安全形度來看,最好是,任何日誌都不應該在發行版應用中輸出,但有時候,即使在發行版本應用中,出於各種原因也會輸出日誌。每種思考方式按照以下描述。
前者是“任何日誌都不應該輸出”,這是因為,在發行版應用中輸出日誌沒有那麼重要,並且存在洩露敏感資訊的風險。這是因為開發人員沒有辦法在 Android 應用執行環境中收集發行版應用的日誌資訊,這與許多 Web 應用的執行環境不同。基於這種思想,日誌程式碼僅用於開發階段,並且在構建發行版應用時刪除所有日誌程式碼。
後者是“必要的資訊應作為日誌輸出,以供日後分析”,作為客戶支援中,分析應用錯誤的最終選項,以防你的客戶支援有任何疑問。 基於這個想法,如上所述,有必要準備系統來防止人為錯誤並將其引入到專案中,因為如果你沒有系統,則必須記住避免在發行版應用中記錄敏感資訊。
更多日誌方法的資訊,請參考下面的連結:
適用於貢獻者/日誌的程式碼風格指南
http://source.android.com/source/code-style.html#log-sparingly
4.8.3.2 日誌級別和日誌輸出方法的選擇標準
在 Android 中的android.util.Log
類中定義了五個日誌級別(ERROR
,WARN
,INFO
,DEBUG
,VERBOSE
)。 使用android.util.Log
類輸出日誌訊息時,應該選擇最合適的方法,如表 4.8-1 所示,它展示了日誌級別和方法的選擇標準。
表 4.8-1 日誌級別和方法的選擇標準
日誌級別 | 方法 | 要輸出的日誌資訊 |
---|---|---|
ERROR |
Log.e() |
應用處於錯誤狀態時,輸出的日誌資訊 |
WARN |
Log.w() |
應用面臨非預期嚴重情況時,輸出的日誌資訊 |
INFO |
Log.i() |
與上面不同,用於提示應用狀態中任何值得注意的更改或者結果 |
DEBUG |
Log.d() |
應用的內部狀態資訊,開發應用時,需要臨時輸出,用於分析特定 bug 的成因 |
VERBOSE |
Log.v() |
不屬於上面任何一個的日誌資訊。應用開發者以多種目的輸出。例如,輸出伺服器通訊資訊來轉儲。 |
發行版應用的注意事項:
e/w/i
:
日誌資訊可能由使用者參考,因此可以在開發版應用和發行版應用中輸出。 因此,敏感資訊不應該在這些級別輸出。
d/v
:
日誌資訊僅適用於應用開發人員。 因此,這種型別的資訊不應該在發行版的情況下輸出。
更多日誌方法的資訊,請參考下面的連結:
適用於貢獻者/日誌的程式碼風格指南
http://source.android.com/source/code-style.html#log-sparingly
4.8.3.3 DEBUG
和VERBOSE
日誌並不總是自動刪除
以下引用自android.util.Log
類 [18] 的開發人員參考。
[18] ttp://developer.android.com/reference/android/util/Log.html
按照囉嗦程度的順序排列,從最少到最多是ERROR
,WARN
,INFO
,DEBUG
,VERBOSE
。 除了在開發期間,絕不應該將VERBOSE
編譯進應用。 DEBUG
日誌被編譯但在執行時剝離。 始終保留ERROR
,WARN
,INFO
日誌。
在閱讀了上述文章之後,一些開發人員可能會誤解Log
類的行為,如下所示。
- 構建發行版時不編譯
Log.v()
呼叫,VERBOSE
日誌從不輸出。 - 編譯
Log.v()
呼叫,但執行時絕不輸出DEBUG
日誌。
但是,日誌記錄方法從來不會表現成這樣,並且無論使用除錯模式還是釋出模式編譯,都會輸出所有訊息。 如果仔細閱讀文件,你將能夠認識到,文件的要點與日誌方法的行為無關,而是日誌的基本策略。
在本章中,我們通過使用 ProGuard 引入了示例程式碼以獲得上述的預期結果。
4.8.3.4 從彙編中移除敏感資訊
如果為了刪除Log.d()
方法而使用 ProGuard 構建以下程式碼,有必要記住,ProGuard
會保留為日誌資訊構造字串的語句(程式碼的第一行),即使它刪除了 Log.d()
方法的呼叫(程式碼的第二行)。
String debug_info = String.format("%s:%s", "Sensitive information1", "Sensitive information2");
if (BuildConfig.DEBUG) android.util.Log.d(TAG, debug_info);
以下反彙編顯示了使用 ProGuard 釋出上述程式碼的結果。 實際上,沒有Log.d()
呼叫過程,但你可以看到字串一致性定義,例如Sensitive information1
,和String#format()
方法的呼叫過程,不會被刪除並仍然存在。
const-string v1, "%s:%s"
const/4 v2, 0x2
new-array v2, v2, [Ljava/lang/Object;
const/4 v3, 0x0
const-string v4, "Sensitive information 1"
aput-object v4, v2, v3
const/4 v3, 0x1
const-string v4, "Sensitive information 2"
aput-object v4, v2, v3
invoke-static {v1, v2}, Ljava/lang/String;->format(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang
/String;
move-result-object v0
實際上,找到反彙編 APK 檔案的組裝日誌輸出資訊特定部分並不容易。 但是,在某些處理機密資訊的應用中,這種型別的過程在某些情況下不應保留在 APK 檔案中。 你應該像下面那樣實現你的應用,來避免在位元組碼中保留敏感資訊的後果。 在發行版中,編譯器優化將完全刪除以下程式碼。
if (BuildConfig.DEBUG) {
String debug_info = String.format("%s:%s", " Snsitive information 1", "Sensitive information 2");
if (BuildConfig.DEBUG) android.util.Log.d(TAG, debug_info);
}
此外,ProGuard 無法刪除以下日誌訊息程式碼("result:" + value)
。
Log.d(TAG, "result:" + value);
在這種情況下,你可以通過以下方式解決問題。
if (BuildConfig.DEBUG) Log.d(TAG, "result:" + value);
4.8.3.5 意圖的內容輸出到了 LogCat
使用活動時,需要注意,因為ActivityManager
將意圖的內容輸出到 LogCat。 請參閱“4.1.3.5 使用活動時的日誌輸出”。
4.8.3.6 限制輸出到System.out / err
的日誌
System.out / err
方法將所有訊息輸出到 LogCat。 即使開發者沒有在他們的程式碼中使用這些方法,Android 也可以向System.out / err
傳送一些訊息,例如,在以下情況下,Android 會將堆疊蹤跡傳送到System.err
方法。
- 使用
Exception#printStackTrace()
時 - 隱式輸出到
System.err
時(當異常沒有被應用捕獲時,它會由系統提供給Exception#printStackTrace()
。)
你應該適當地處理錯誤和異常,因為堆疊蹤跡包含應用的獨特資訊。
我們介紹一種改變System.out / err
預設輸出目標的方法。 當你構建發行版應用時,以下程式碼將System.out / err
方法的輸出重定向到任何地方。 但是,你應該考慮此重定向是否會導致應用或系統故障,因為程式碼會暫時覆蓋System.out / err
方法的預設行為。 此外,這種重定向僅對你的應用有效,對系統程式毫無價值。
OutputRedirectApplication.java
package org.jssec.android.log.outputredirection;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import android.app.Application;
public class OutputRedirectApplication extends Application {
// PrintStream which is not output anywhere
private final PrintStream emptyStream = new PrintStream(new OutputStream() {
public void write(int oneByte) throws IOException {
// do nothing
}
});
@Override
public void onCreate() {
// Redirect System.out/err to PrintStream which doesn`t output anywhere, when release build.
// Save original stream of System.out/err
PrintStream savedOut = System.out;
PrintStream savedErr = System.err;
// Once, redirect System.out/err to PrintStream which doesn`t output anywhere
System.setOut(emptyStream);
System.setErr(emptyStream);
// Restore the original stream only when debugging. (In release build, the following 1 line is deleted byProGuard.)
resetStreams(savedOut, savedErr);
}
// All of the following methods are deleted byProGuard when release.
private void resetStreams(PrintStream savedOut, PrintStream savedErr) {
System.setOut(savedOut);
System.setErr(savedErr);
}
}
AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.jssec.android.log.outputredirection" >
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:name=".OutputRedirectApplication"
android:allowBackup="false" >
<activity
android:name=".LogActivity"
android:label="@string/app_name"
android:exported="true" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
proguard-project.txt
# Prevent from changing class name and method name, etc
-dontobfuscate
# In release build, delete call from Log.d()/v() automatically.
-assumenosideeffects class android.util.Log {
public static int d(...);
public static int v(...);
}
# In release build, delete resetStreams() automatically.
-assumenosideeffects class org.jssec.android.log.outputredirection.OutputRedirectApplication {
private void resetStreams(...);
}
開發版應用(除錯版)和釋出版應用(發行版)之間的 LogCat 輸出差異如下圖 4.8-3 所示。
相關文章
- 安卓應用安全指南翻譯完成安卓
- 安卓應用安全指南六、困難問題安卓
- 安卓應用安全指南4.5.2使用SQLite規則書安卓SQLite
- 安卓應用安全指南4.1.2建立/使用活動規則書安卓
- 安卓應用安全指南 5.6.1 密碼學 示例程式碼安卓密碼學
- 安卓應用安全指南4.7使用可瀏覽的意圖安卓
- 安卓應用安全指南4.1.1建立/使用活動示例程式碼安卓
- 安卓應用安全指南4.6.1處理檔案示例程式碼安卓
- 安卓應用安全指南4.3.1建立/使用內容供應器示例程式碼安卓
- 安卓應用安全指南5.5.2處理隱私資料規則書安卓
- 安卓應用安全指南5.4.1通過HTTPS的通訊示例程式碼安卓HTTP
- 安卓應用安全指南4.2.3建立/使用廣播接收器高階話題安卓
- appium 安卓應用指令碼APP安卓指令碼
- Flutter 如何釋出安卓應用?Flutter安卓
- 7款最佳安卓日曆應用安卓
- LogCat連線安卓手機拉取日誌到本地(Unity開發版)GC安卓Unity
- 用adb logcat抓取logGC
- 安卓移動應用程式碼安全加固系統設計及實現安卓
- 徹底理解安卓應用無響應機制安卓
- 安卓應用優化:使用反射測試安卓裝置是否使用“動態桌布”安卓優化反射
- 如何將Chrome本地安裝的擴充套件應用匯出到本地Chrome套件
- [譯] 如何在安卓應用中使用 TensorFlow Mobile安卓
- 安卓版 Google Sheets 表格應用支援滑鼠操作安卓Go
- .NET MAUI 安卓應用開發初體驗UI安卓
- 安卓 VS iOS,誰更安全?安卓iOS
- 在安卓上使用OpenCV的指南 - kdnuggets安卓OpenCV
- 構建現代Web應用的安全指南Web
- logstach 8.6.2輸出到mongo 6Go
- 為什麼我拒絕用Kotlin編寫安卓應用?Kotlin安卓
- 海雲安應用安全測試、移動應用安全、開發安全再次上榜
- 安卓10 禁止某個應用聯網(已root)安卓
- 如何在linux上下載各種常用安卓應用Linux安卓
- 安卓教育應用如何在linux上流暢執行安卓Linux
- 10個用於冥想和正念的最佳免費安卓應用安卓
- [譯]構建現代Web應用的安全指南Web
- 50多個惡意安卓應用繞過Google Play,感染了3000萬安卓使用者安卓Go
- 安卓檔案傳輸工具Android File Transfer安卓Android
- 機器人工程相關安卓應用資料2018機器人安卓