Wenn Sie Teil eines Android-Projekts mit mehreren Teams sind, wird es relativ schwierig, ein gemeinsames Verständnis darüber zu haben, wie Komponenten verwendet werden sollten. An dieser Stelle kann Android Lint Ihnen helfen! In diesem Blog zeigen wir Ihnen, wie Sie Ihre eigenen Lint-Regeln schreiben und sie testen können. Als Beispiel erstellen wir einen Lint Detector, mit dem Sie feststellen können, ob Sie die "geheimen Daten" in Ihrer Anwendung von dem in Android Marshmallow eingeführten Android Authobackup ausgeschlossen haben.
Eine kurze Einführung in Android Lint
Bevor wir uns dem Beispiel zuwenden, lassen Sie uns kurz Android Lint besuchen. Es hilft Ihnen grundsätzlich dabei, Ihren Code zu verbessern! In einer längeren Beschreibung: Android Lint ist ein Code-Scan-Tool als Teil von Android Studio. Es kann Ihnen helfen, Probleme mit der Qualität Ihres Anwendungscodes zu erkennen und zu korrigieren. Dies geschieht jedes Mal automatisch, wenn Sie einen (Release-)Build erstellen. Android Lint verfügt bereits über einige eigene Regeln: Es kann Ihnen helfen, Sicherheitsprobleme zu erkennen, die Korrektheit Ihres Codes, die Internationalisierung, die Leistung Ihrer Anwendung und vieles mehr zu verbessern. Lint verfügt bereits über eine ganze Reihe von Regeln, die Ihnen dabei helfen. Was aber, wenn Sie Lint verwenden möchten, um Ihre eigene Regel durchzusetzen? Hierfür müssen wir eine neue Lint-Regel hinzufügen. Besser gesagt: Wir müssen ein Problem hinzufügen, das Lint erkennen soll.
Unser Beispiel: Sicherstellen, dass wir Sicherungsdaten ausschließen
Die Grundeinstellung für Ihre eigenen Regeln
Um Lint zu erweitern, müssen Sie Folgendes tun:
- Erstellen Sie ein Java-Modul in Ihrem Projekt
- Importieren Sie die richtigen Module in Ihr Projekt
- Einen Detektor erstellen
- Erstellen Sie eine IssueRegistry
- Konfigurieren Sie Ihre build.gradle
- Fügen Sie Unit-Tests hinzu.
Schritt 1: Erstellen Sie ein Java-Modul in Ihrem Projekt
Verwenden Sie Android Studio, um ein neues Java-Modul in Ihrem Projekt zu erstellen. Nennen wir es sample-project-lint-extension.
Schritt 2: Importieren Sie die richtigen Module in Ihr Projekt
Im Stammverzeichnis Ihrer neuen sample-project-lint-Erweiterung finden Sie Ihre build.gradle. Fügen Sie die folgenden Abhängigkeiten hinzu: [code language="groovy"] Abhängigkeiten { //Lint-Abhängigkeiten: compile 'com.android.tools.lint:lint-api:24.3.1' compile 'com.android.tools.lint:lint-checks:24.3.1' testCompile 'com.android.tools.lint:lint-tests:24.3.1' } [/code]
Schritt 3: Erstellen Sie Ihre eigenen Detektoren
Jede neue Lint-Regel wird in einer Ausgabe erfasst. Ein Issue beschreibt das Problem, das Sie finden möchten. Die findende/suchende/entdeckende Instanz ist der so genannte Detector. Ein Problem wird wie folgt deklariert: [code language="Java"] public static final Issue BACKUP_ANDROID_6_EXCLUDE_IMPORTANT_DATA = Issue.create( "BackupAndroidSixExcludeImportantData", "Sie haben das Full-Backup-Content-Tag verwendet, aber nicht 'importantdata' ausgeschlossen", "Wenn Sie dem Android-Backup-System ab API lvl 23 erlauben, Backups durchzuführen, "
- ", dann sollten Sie 'importantdata' ausschließen. Die Regel kann wie folgt definiert werden: "
- "<exclude domain="sharedpref" path="importantdata">",
Category.SECURITY,
6,
Severity.WARNING,
new Implementation(Android6BackupDetector.class, Scope.ALL_RESOURCES_SCOPE));
[/code]
Die meisten Parameter sollten für sich selbst sprechen, sobald Sie sie getestet haben. Beachten Sie nur, dass "6" eine Priorität von 1 bis 10 ist, wobei 10 die wichtigste/schwerste ist.
Dieses Issue kann dann in einer Detector-Klasse platziert werden, die dazu dient, das eigentliche Problem zu erkennen.
Detectors können für alles in Ihrem Anwendungscode verwendet werden: Sie können sie verwenden, um Probleme in Ihrem Java-Code, Probleme in Ihrem res-Ordner, Probleme in Ihrer Android-Manifestdatei und mehr zu erkennen. Unser Beispiel-Detektor wird verwendet, um zu erkennen, ob Sie einige der Anwendungsdaten von dem in Android Marshmallow eingeführten Android-Autobackup ausgeschlossen haben.
Dazu müssen wir die XML-Ressourcen scannen und einen Detektor erstellen, der Ihre Backup-Definitionen analysieren kann. Die Detektor-Klasse selbst sieht wie folgt aus:
[code language="Java"]
public class Android6BackupDetector extends ResourceXmlDetector{
public static final Issue BACKUP_ANDROID_6_EXCLUDE_IMPORTANT_DATA =
Issue.create( "BackupAndroidSixExcludeImportantData",
"Sie haben das Full-Backup-Content-Tag verwendet, aber nicht 'importantdata' ausgeschlossen",
"Wenn Sie dem Android-Backup-System ab API lvl 23 erlauben, Backups durchzuführen, "
- ", dann sollten Sie 'importantdata' ausschließen. Die Regel kann wie folgt definiert werden: "
- "<exclude domain="sharedpref" path="importantdata">",
Kategorie.SICHERHEIT,
6, Schweregrad.WARNUNG,
new Implementation(Android6BackupDetector.class, Scope.ALL_RESOURCES_SCOPE)
);
@Override
public Collection <String> getApplicableElements() {
return Collections.singletonList("full-backup-content");
}
@Override
public void visitElement(@NonNull XmlContext context, @NonNull Element element) {
boolean hasSecretDataExcluded = false;
if (element.hasChildNodes()) {
NodeList children = element.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node child = children.item(i);
if (child.getNodeType() == Node.ELEMENT_NODE && child.getNodeName().equals("ausschließen")) {
if (child.getAttributes().getNamedItem("path").getNodeValue().equals("importantdata")
&& kind.getAttributes().getNamedItem("domain").getNodeValue().equals("sharedpref")) {
hasSecretDataExcluded = true;
}
}
}
}
if (!hasSecretDataExcluded ) {
context.report(BACKUP_ANDROID_6_EXCLUDE_IMPORTANT_DATA, element, context.getLocation(element),
"Sie sollten die sharedpref unserer App ausschließen!");
}
}
}
[/code]
mit der Methode "getApplicableElements" scannt Lint Ihre XML-Dateien, um Elemente zu finden, die den von Ihnen implementierten Kriterien entsprechen (in unserem Fall interessieren wir uns nur für das Full-Backup-Content-Tag). Anschließend können Sie das angegebene Element mit der Methode visitElement besuchen. Hier prüfen wir, ob der Ausschluss für unser Tag "importantdata" in den gemeinsamen Einstellungen definiert wurde.
Schritt 4: Erstellen Sie Ihre eigene IssueRegistry
Jedes benutzerdefinierte Lint-Jar muss über eine IssueRegistry verfügen. Dies ist eine Klasse, die alle Probleme enthält, die Sie erkennen möchten. Unsere IssueRegistry sieht wie folgt aus: [code language="Java"] public final class IssueRegistry extends com.android.tools.lint.client.api.IssueRegistry{ @Override public List <Issue> getIssues() { return Arrays.asList( Android6BackupDetector.BACKUP_ANDROID_6_EXCLUDE_IMPORTANT_DATA //....Sie können hier weitere Probleme hinzufügen [/code] ); } ... } Für eine ausführliche Erklärung von Schritt 3 & 4: besuchen Sie
https://tools.android.com/tips/lint/writing-a-lint-checkund tools.android.com/tips/lint-custom-rules.Schritt 5: Konfigurieren Sie Ihre build.gradle
Jetzt können Sie Ihre build.gradle so erweitern, dass sie wie folgt aussieht: [code language="groovy"] plugin anwenden: 'java' configurations { lintChecks } dependencies { // Lint dependencies compile 'com.android.tools.lint:lint-api:24.3.1' compile 'com.android.tools.lint:lint-checks:24.3.1' testCompile 'com.android.tools.lint:lint-tests:24.3.1' lintChecks files(jar) } jar { manifest { attributes('Lint-Registry': 'sample.xebia.lint.IssueRegistry') } } [/code] mit der jar-Klausel geben Sie an, wo man die IssueRegistry finden kann.
Schritt 6: Unit-Tests hinzufügen
Jetzt können wir anfangen, Unit-Tests hinzuzufügen. Sie können entweder den AbstractCheckTest erweitern, wenn Sie eine Menge zusätzlicher Tests haben, oder Sie können den LintDetectorTest direkt erweitern. Lassen Sie uns das für den Moment tun. Wir müssen mindestens drei verschiedene Szenarien testen:
- fehlen uns die wichtigen Daten als Ausschluss;
- wir nehmen die wichtigen Daten auf, vergessen aber, dass es sich um ein Shared Pref handelt (und nicht um eine Datenbank);
- haben wir ihn ordnungsgemäß in unseren Ausschlusspfad aufgenommen.
[code language="java"]
public class Android6BackupDetectorTest extends LintDetectorTest {
@Override
protected Detektor getDetector() {
return new Android6BackupDetector();
}
@Override
protected Liste <Issue> getIssues() {
return Collections.singletonList( Android6BackupDetector.BACKUP_ANDROID_6_EXCLUDE_IMPORTANT_DATA );
}
public void testOk() throws Exception {
assertEquals("Keine Warnungen.",
lintProject(xml("res/xml/backup.xml", ""
- "<?xml version="1.0" encoding="utf-8"?&>n"
- "<full-backup-content>n"
- "<exclude domain="sharedpref" path="importantdata">n"
- "<exclude domain="sharedpref" path="gcm">n"
- "</full-backup-content>n" ) ) ); } public void testBackupContainsSecretData() throws Exception { assertEquals("res/xml/backup.xml:2: Warnung: Sie sollten die sharedpref unserer App ausschließen! [BackupAndroidSixExcludeImportantData]n"
- "<full-backup-content>;"
- "n"
- "0 Fehler, 1 Warnungenn",
lintProject(xml("res/xml/backup.xml",
"<?xml version="1.0" encoding="utf-8"?>n"
- "<Volles-Backup-Inhalt>;n"
- "<exclude domain="database" path="importantdata">n"
- "<exclude domain="sharedpref" path="gcm"/>n"
- "</full-backup-content>n") ) ); } public void testMissing() throws Exception { assertEquals("res/xml/backup.xml:2: Warnung: Sie sollten die sharedpref unserer App ausschließen! [BackupAndroidSixExcludeImportantData]n"
- "<Volles-Backup-Inhalt>n"
- "^n" + "0 Fehler, 1 Warnungenn",
lintProject(xml("res/xml/backup.xml", ""
- "<?xml version="1.0" encoding="utf-8"?>n"
- "<Volles-Backup-Inhalt>n"
- "<exclude domain="sharedpref" path="gcm"/>n"
- "</full-backup-content>n" )
)
);
}
}
[/code]
Und schon sind Sie fertig!
Jetzt können Sie Ihre getesteten Lint-Steuerungen verwenden! Kopieren Sie die erstellte jar-Datei in Ihr Android(ANDROID_SDK_HOME)/lint-Verzeichnis und sie wird in Zukunft geprüft. Wenn Sie sicherstellen möchten, dass Sie die Datei korrekt exportiert haben, führen Sie "lint - - show BackupAndroidSixExcludeImportantData" aus.
Verfasst von

Jeroen Willemsen
Typical security jack-of-all-trades. Hands-on security architect with a nack for security, automation, and risk management. Jeroen has been involved in various OWASP projects. He enjoys a pentest every now and then, while helping organizations to get secure enough. Jeroen is often engaged in knowledge sharing through talks, blogs, projects at github, and trainings. Want to reach out? Check his allmylinks page.
Unsere Ideen
Weitere Blogs
Contact



