Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Move to experimental and update qldoc
  • Loading branch information
luchua-bc committed Nov 26, 2020
commit 7ad031ca70c2dc1cb5ac27a5aebd824a94f80f03
22 changes: 0 additions & 22 deletions java/ql/src/Security/CWE/CWE-312/ClearTextStorageSharedPrefs.ql

This file was deleted.

115 changes: 0 additions & 115 deletions java/ql/src/Security/CWE/CWE-312/SensitiveStorage.qll
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import java
import semmle.code.java.frameworks.Properties
import semmle.code.java.frameworks.JAXB
import semmle.code.java.frameworks.android.SharedPreferences
import semmle.code.java.dataflow.TaintTracking
import semmle.code.java.dataflow.DataFlow3
import semmle.code.java.dataflow.DataFlow4
import semmle.code.java.dataflow.DataFlow5
import semmle.code.java.security.SensitiveActions

/** Test code filter. */
Expand All @@ -30,11 +28,6 @@ private class SensitiveSourceFlowConfig extends TaintTracking::Configuration {
m.getMethod() instanceof PropertiesSetPropertyMethod and sink.asExpr() = m.getArgument(1)
)
or
exists(MethodAccess m |
m.getMethod() instanceof SharedPreferences::SharedPreferencesSetMethod and
sink.asExpr() = m.getArgument(1)
)
or
sink.asExpr() = getInstanceInput(_, _)
}

Expand Down Expand Up @@ -250,111 +243,3 @@ class Marshallable extends ClassStore {
)
}
}

/* Holds if the method call is a setter method of `SharedPreferences`. */
private predicate sharedPreferencesInput(DataFlow::Node sharedPrefs, Expr input) {
exists(MethodAccess m |
m.getMethod() instanceof SharedPreferences::SharedPreferencesSetMethod and
input = m.getArgument(1) and
not exists(EncryptedValueFlowConfig conf | conf.hasFlow(_, DataFlow::exprNode(input))) and
sharedPrefs.asExpr() = m.getQualifier()
)
}

/* Holds if the method call is the store method of `SharedPreferences`. */
private predicate sharedPreferencesStore(DataFlow::Node sharedPrefs, Expr store) {
exists(MethodAccess m |
m.getMethod() instanceof SharedPreferences::SharedPreferencesStoreMethod and
store = m and
sharedPrefs.asExpr() = m.getQualifier()
)
}

/* Flow from `SharedPreferences` to either a setter or a store method. */
class SharedPreferencesFlowConfig extends TaintTracking::Configuration {
SharedPreferencesFlowConfig() { this = "SensitiveStorage::SharedPreferencesFlowConfig" }

override predicate isSource(DataFlow::Node src) {
src.asExpr() instanceof SharedPreferencesEditor
}

override predicate isSink(DataFlow::Node sink) {
sharedPreferencesInput(sink, _) or
sharedPreferencesStore(sink, _)
}
}

/**
* Method call of encrypting sensitive information.
* As there are various implementations of encryption (reversible and non-reversible) from both JDK and third parties, this class simply checks method name to take a best guess to reduce false positives.
*/
class EncryptedSensitiveMethodAccess extends MethodAccess {
EncryptedSensitiveMethodAccess() {
getMethod().getName().toLowerCase().matches(["%encrypt%", "%hash%"])
}
}

/* Flow configuration of encrypting sensitive information. */
class EncryptedValueFlowConfig extends DataFlow5::Configuration {
EncryptedValueFlowConfig() { this = "SensitiveStorage::EncryptedValueFlowConfig" }

override predicate isSource(DataFlow5::Node src) {
exists(EncryptedSensitiveMethodAccess ema | src.asExpr() = ema.getAnArgument())
}

override predicate isSink(DataFlow5::Node sink) {
exists(MethodAccess ma |
ma.getMethod() instanceof SharedPreferences::SharedPreferencesSetMethod and
sink.asExpr() = ma.getArgument(1)
)
}

override predicate isAdditionalFlowStep(DataFlow5::Node n1, DataFlow5::Node n2) {
exists(EncryptedSensitiveMethodAccess ema |
n1.asExpr() = ema.getAnArgument() and
n2.asExpr() = ema
)
}
}

/* Flow from the create method of `androidx.security.crypto.EncryptedSharedPreferences` to its instance. */
private class EncryptedSharedPrefFlowConfig extends DataFlow3::Configuration {
EncryptedSharedPrefFlowConfig() { this = "SensitiveStorage::EncryptedSharedPrefFlowConfig" }

override predicate isSource(DataFlow::Node src) {
src.asExpr().(MethodAccess).getMethod() instanceof
SharedPreferences::EncryptedSharedPrefsCreateMethod
}

override predicate isSink(DataFlow::Node sink) {
sink.asExpr().getType() instanceof SharedPreferences::TypeSharedPreferences
}
}

/** The call to get a `SharedPreferences.Editor` object, which can set shared preferences or be stored to device. */
class SharedPreferencesEditor extends MethodAccess {
SharedPreferencesEditor() {
this.getMethod() instanceof SharedPreferences::SharedPreferencesGetEditorMethod and
not exists(
EncryptedSharedPrefFlowConfig config // not exists `SharedPreferences sharedPreferences = EncryptedSharedPreferences.create(...)`
|
config.hasFlow(_, DataFlow::exprNode(this.getQualifier()))
)
}

/** Gets an input, for example `input` in `editor.putString("password", password);`. */
Expr getAnInput() {
exists(SharedPreferencesFlowConfig conf, DataFlow::Node n |
sharedPreferencesInput(n, result) and
conf.hasFlow(DataFlow::exprNode(this), n)
)
}

/** Gets a store, for example `editor.commit();`. */
Expr getAStore() {
exists(SharedPreferencesFlowConfig conf, DataFlow::Node n |
sharedPreferencesStore(n, result) and
conf.hasFlow(DataFlow::exprNode(this), n)
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
<p>
In the second and third examples, the code encrypts sensitive information before saving it to the device.
</p>
<sample src="ClearTextStorageSharedPrefs.java" />
<sample src="CleartextStorageSharedPrefs.java" />
</example>

<references>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
/**
* @name Cleartext storage of sensitive information using `SharedPreferences` on Android
* @description Cleartext Storage of Sensitive Information using SharedPreferences on Android allows access for users with root privileges or unexpected exposure from chained vulnerabilities.
* @kind path-problem
* @id java/android/cleartext-storage-shared-prefs
* @tags security
* external/cwe/cwe-312
*/

import java
import semmle.code.java.dataflow.DataFlow4
import semmle.code.java.dataflow.DataFlow5
import semmle.code.java.dataflow.TaintTracking
import semmle.code.java.frameworks.android.Intent
import semmle.code.java.frameworks.android.SharedPreferences
import semmle.code.java.security.SensitiveActions
import DataFlow::PathGraph

/** Holds if the method call is a setter method of `SharedPreferences`. */
private predicate sharedPreferencesInput(DataFlow::Node sharedPrefs, Expr input) {
exists(MethodAccess m |
m.getMethod() instanceof SharedPreferences::SetPreferenceMethod and
input = m.getArgument(1) and
not exists(EncryptedValueFlowConfig conf | conf.hasFlow(_, DataFlow::exprNode(input))) and
sharedPrefs.asExpr() = m.getQualifier()
)
}

/** Holds if the method call is the store method of `SharedPreferences`. */
private predicate sharedPreferencesStore(DataFlow::Node sharedPrefs, Expr store) {
exists(MethodAccess m |
m.getMethod() instanceof SharedPreferences::StorePreferenceMethod and
store = m and
sharedPrefs.asExpr() = m.getQualifier()
)
}

/** Flow from `SharedPreferences` to either a setter or a store method. */
class SharedPreferencesFlowConfig extends TaintTracking::Configuration {
Comment thread
luchua-bc marked this conversation as resolved.
Outdated
SharedPreferencesFlowConfig() {
this = "CleartextStorageSharedPrefs::SharedPreferencesFlowConfig"
}

override predicate isSource(DataFlow::Node src) {
src.asExpr() instanceof SharedPreferencesEditor
}

override predicate isSink(DataFlow::Node sink) {
sharedPreferencesInput(sink, _) or
sharedPreferencesStore(sink, _)
}
}

/**
* Method call of encrypting sensitive information.
* As there are various implementations of encryption (reversible and non-reversible) from both JDK and third parties, this class simply checks method name to take a best guess to reduce false positives.
*/
class EncryptedSensitiveMethodAccess extends MethodAccess {
EncryptedSensitiveMethodAccess() {
getMethod().getName().toLowerCase().matches(["%encrypt%", "%hash%"])
}
}

/** Flow configuration of encrypting sensitive information. */
class EncryptedValueFlowConfig extends DataFlow5::Configuration {
EncryptedValueFlowConfig() { this = "CleartextStorageSharedPrefs::EncryptedValueFlowConfig" }

override predicate isSource(DataFlow5::Node src) {
exists(EncryptedSensitiveMethodAccess ema | src.asExpr() = ema.getAnArgument())
Comment thread
luchua-bc marked this conversation as resolved.
Outdated
}

override predicate isSink(DataFlow5::Node sink) {
exists(MethodAccess ma |
ma.getMethod() instanceof SharedPreferences::SetPreferenceMethod and
sink.asExpr() = ma.getArgument(1)
)
}

override predicate isAdditionalFlowStep(DataFlow5::Node n1, DataFlow5::Node n2) {
exists(EncryptedSensitiveMethodAccess ema |
n1.asExpr() = ema.getAnArgument() and
n2.asExpr() = ema
)
}
}

/** Flow from the create method of `androidx.security.crypto.EncryptedSharedPreferences` to its instance. */
private class EncryptedSharedPrefFlowConfig extends DataFlow4::Configuration {
EncryptedSharedPrefFlowConfig() {
this = "CleartextStorageSharedPrefs::EncryptedSharedPrefFlowConfig"
}

override predicate isSource(DataFlow4::Node src) {
src.asExpr().(MethodAccess).getMethod() instanceof SharedPreferences::CreateEncryptedPrefsMethod
}

override predicate isSink(DataFlow4::Node sink) {
sink.asExpr().getType() instanceof SharedPreferences::TypePrefs
}
}

/** The call to get a `SharedPreferences.Editor` object, which can set shared preferences or be stored to device. */
class SharedPreferencesEditor extends MethodAccess {
SharedPreferencesEditor() {
this.getMethod() instanceof SharedPreferences::GetEditorMethod and
not exists(
EncryptedSharedPrefFlowConfig config // not exists `SharedPreferences sharedPreferences = EncryptedSharedPreferences.create(...)`
|
config.hasFlow(_, DataFlow::exprNode(this.getQualifier()))
)
}

/** Gets an input, for example `input` in `editor.putString("password", password);`. */
Comment thread
luchua-bc marked this conversation as resolved.
Outdated
Expr getAnInput() {
exists(SharedPreferencesFlowConfig conf, DataFlow::Node n |
sharedPreferencesInput(n, result) and
conf.hasFlow(DataFlow::exprNode(this), n)
)
}

/** Gets a store, for example `editor.commit();`. */
Expr getAStore() {
exists(SharedPreferencesFlowConfig conf, DataFlow::Node n |
sharedPreferencesStore(n, result) and
conf.hasFlow(DataFlow::exprNode(this), n)
)
}
}

private class SensitiveSharedPrefsFlowConfig extends TaintTracking::Configuration {
SensitiveSharedPrefsFlowConfig() {
this = "CleartextStorageSharedPrefs::SensitiveSharedPrefsFlowConfig"
}

override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof SensitiveExpr }

override predicate isSink(DataFlow::Node sink) {
exists(MethodAccess m |
m.getMethod() instanceof SharedPreferences::SetPreferenceMethod and
sink.asExpr() = m.getArgument(1)
)
}
}

/** Class for shared preferences that may contain 'sensitive' information */
class SensitiveSharedPrefsSource extends Expr {
SensitiveSharedPrefsSource() {
// SensitiveExpr is abstract, this lets us inherit from it without
// being a technical subclass
this instanceof SensitiveExpr
}

/** Holds if this source flows to the `sink`. */
predicate flowsTo(Expr sink) {
exists(SensitiveSharedPrefsFlowConfig conf |
conf.hasFlow(DataFlow::exprNode(this), DataFlow::exprNode(sink))
)
}
}

from SensitiveSharedPrefsSource data, SharedPreferencesEditor s, Expr input, Expr store
where
input = s.getAnInput() and
store = s.getAStore() and
data.flowsTo(input)
select store, "'SharedPreferences' class $@ containing $@ is stored here. Data was added $@.", s,
s.toString(), data, "sensitive data", input, "here"
Loading