Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit a491604

Browse files
committed
Enhance the query and add more test cases
1 parent a311462 commit a491604

5 files changed

Lines changed: 166 additions & 57 deletions

File tree

java/ql/src/Security/CWE/CWE-312/ClearTextStorageSharedPrefs.qhelp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<qhelp>
33
<overview>
44
<p>
5-
<code>SharedPreferences</code> is an Android API that stores application preferences using simple sets of data values. Almost every Android application uses this API. It allows to easily save, alter, and retrieve the values stored in <code>SharedPreferences</code>. However, sensitive information shall not be saved in cleartext. Otherwise it can be accessed by any process or user on rooted devices, or can be disclosed through chained vulnerabilities e.g. unexpected access to its private storage through exposed components.
5+
<code>SharedPreferences</code> is an Android API that stores application preferences using simple sets of data values. Almost every Android application uses this API. It allows to easily save, alter, and retrieve the values stored in the user's profile. However, sensitive information should not be saved in cleartext. Otherwise it can be accessed by any process or user on rooted devices, or can be disclosed through chained vulnerabilities e.g. unexpected access to its private storage through exposed components.
66
</p>
77
</overview>
88

java/ql/src/Security/CWE/CWE-312/SensitiveStorage.qll

Lines changed: 53 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import semmle.code.java.frameworks.android.SharedPreferences
55
import semmle.code.java.dataflow.TaintTracking
66
import semmle.code.java.dataflow.DataFlow3
77
import semmle.code.java.dataflow.DataFlow4
8+
import semmle.code.java.dataflow.DataFlow5
89
import semmle.code.java.security.SensitiveActions
910

1011
/** Test code filter. */
@@ -30,7 +31,8 @@ private class SensitiveSourceFlowConfig extends TaintTracking::Configuration {
3031
)
3132
or
3233
exists(MethodAccess m |
33-
m.getMethod() instanceof SharedPreferencesSetMethod and sink.asExpr() = m.getArgument(1)
34+
m.getMethod() instanceof SharedPreferences::SharedPreferencesSetMethod and
35+
sink.asExpr() = m.getArgument(1)
3436
)
3537
or
3638
sink.asExpr() = getInstanceInput(_, _)
@@ -252,22 +254,23 @@ class Marshallable extends ClassStore {
252254
/* Holds if the method call is a setter method of `SharedPreferences`. */
253255
private predicate sharedPreferencesInput(DataFlow::Node sharedPrefs, Expr input) {
254256
exists(MethodAccess m |
255-
m.getMethod() instanceof SharedPreferencesSetMethod and
257+
m.getMethod() instanceof SharedPreferences::SharedPreferencesSetMethod and
256258
input = m.getArgument(1) and
259+
not exists(EncryptedValueFlowConfig conf | conf.hasFlow(_, DataFlow::exprNode(input))) and
257260
sharedPrefs.asExpr() = m.getQualifier()
258261
)
259262
}
260263

261264
/* Holds if the method call is the store method of `SharedPreferences`. */
262265
private predicate sharedPreferencesStore(DataFlow::Node sharedPrefs, Expr store) {
263266
exists(MethodAccess m |
264-
m.getMethod() instanceof SharedPreferencesStoreMethod and
267+
m.getMethod() instanceof SharedPreferences::SharedPreferencesStoreMethod and
265268
store = m and
266269
sharedPrefs.asExpr() = m.getQualifier()
267270
)
268271
}
269272

270-
/* Flow from `SharedPreferences` to the method call changing its value. */
273+
/* Flow from `SharedPreferences` to either a setter or a store method. */
271274
class SharedPreferencesFlowConfig extends TaintTracking::Configuration {
272275
SharedPreferencesFlowConfig() { this = "SensitiveStorage::SharedPreferencesFlowConfig" }
273276

@@ -279,25 +282,63 @@ class SharedPreferencesFlowConfig extends TaintTracking::Configuration {
279282
sharedPreferencesInput(sink, _) or
280283
sharedPreferencesStore(sink, _)
281284
}
285+
}
282286

283-
override predicate isSanitizer(DataFlow::Node n) {
287+
/**
288+
* Method call of encrypting sensitive information.
289+
* 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.
290+
*/
291+
class EncryptedSensitiveMethodAccess extends MethodAccess {
292+
EncryptedSensitiveMethodAccess() {
293+
getMethod().getName().toLowerCase().matches(["%encrypt%", "%hash%"])
294+
}
295+
}
296+
297+
/* Flow configuration of encrypting sensitive information. */
298+
class EncryptedValueFlowConfig extends DataFlow5::Configuration {
299+
EncryptedValueFlowConfig() { this = "SensitiveStorage::EncryptedValueFlowConfig" }
300+
301+
override predicate isSource(DataFlow5::Node src) {
302+
exists(EncryptedSensitiveMethodAccess ema | src.asExpr() = ema.getAnArgument())
303+
}
304+
305+
override predicate isSink(DataFlow5::Node sink) {
284306
exists(MethodAccess ma |
285-
ma.getMethod().getName().toLowerCase().matches("%encrypt%") and
286-
n.asExpr() = ma.getAnArgument()
307+
ma.getMethod() instanceof SharedPreferences::SharedPreferencesSetMethod and
308+
sink.asExpr() = ma.getArgument(1)
287309
)
288310
}
311+
312+
override predicate isAdditionalFlowStep(DataFlow5::Node n1, DataFlow5::Node n2) {
313+
exists(EncryptedSensitiveMethodAccess ema |
314+
n1.asExpr() = ema.getAnArgument() and
315+
n2.asExpr() = ema
316+
)
317+
}
318+
}
319+
320+
/* Flow from the create method of `androidx.security.crypto.EncryptedSharedPreferences` to its instance. */
321+
private class EncryptedSharedPrefFlowConfig extends DataFlow3::Configuration {
322+
EncryptedSharedPrefFlowConfig() { this = "SensitiveStorage::EncryptedSharedPrefFlowConfig" }
323+
324+
override predicate isSource(DataFlow::Node src) {
325+
src.asExpr().(MethodAccess).getMethod() instanceof
326+
SharedPreferences::EncryptedSharedPrefsCreateMethod
327+
}
328+
329+
override predicate isSink(DataFlow::Node sink) {
330+
sink.asExpr().getType() instanceof SharedPreferences::TypeSharedPreferences
331+
}
289332
}
290333

291334
/** The call to get a `SharedPreferences.Editor` object, which can set shared preferences or be stored to device. */
292335
class SharedPreferencesEditor extends MethodAccess {
293336
SharedPreferencesEditor() {
294-
this.getMethod() instanceof SharedPreferencesGetEditorMethod and
337+
this.getMethod() instanceof SharedPreferences::SharedPreferencesGetEditorMethod and
295338
not exists(
296-
MethodAccess cma // not exists `SharedPreferences sharedPreferences = EncryptedSharedPreferences.create(...)`
339+
EncryptedSharedPrefFlowConfig config // not exists `SharedPreferences sharedPreferences = EncryptedSharedPreferences.create(...)`
297340
|
298-
cma.getQualifier().getType() instanceof TypeEncryptedSharedPreferences and
299-
cma.getMethod().hasName("create") and
300-
cma.getParent().(VariableAssign).getDestVar().getAnAccess() = this.getQualifier()
341+
config.hasFlow(_, DataFlow::exprNode(this.getQualifier()))
301342
)
302343
}
303344

Lines changed: 50 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,64 @@
1-
/* Definitions related to `android.content.SharedPreferences`. */
21
import semmle.code.java.Type
32

4-
/* The interface `android.content.SharedPreferences` */
5-
library class TypeSharedPreferences extends Interface {
6-
TypeSharedPreferences() { hasQualifiedName("android.content", "SharedPreferences") }
7-
}
3+
/* Definitions related to `android.content.SharedPreferences`. */
4+
module SharedPreferences {
5+
/* The interface `android.content.SharedPreferences` */
6+
class TypeSharedPreferences extends Interface {
7+
TypeSharedPreferences() { hasQualifiedName("android.content", "SharedPreferences") }
8+
}
89

9-
/* The class `androidx.security.crypto.EncryptedSharedPreferences`, which implements `SharedPreferences` with encryption support. */
10-
library class TypeEncryptedSharedPreferences extends Class {
11-
TypeEncryptedSharedPreferences() {
12-
hasQualifiedName("androidx.security.crypto", "EncryptedSharedPreferences")
10+
/* The class `androidx.security.crypto.EncryptedSharedPreferences`, which implements `SharedPreferences` with encryption support. */
11+
class TypeEncryptedSharedPreferences extends Class {
12+
TypeEncryptedSharedPreferences() {
13+
hasQualifiedName("androidx.security.crypto", "EncryptedSharedPreferences")
14+
}
1315
}
14-
}
1516

16-
/* A getter method of `android.content.SharedPreferences`. */
17-
library class SharedPreferencesGetMethod extends Method {
18-
SharedPreferencesGetMethod() {
19-
getDeclaringType() instanceof TypeSharedPreferences and
20-
getName().matches("get%")
17+
/* The create method of `androidx.security.crypto.EncryptedSharedPreferences` */
18+
class EncryptedSharedPrefsCreateMethod extends Method {
19+
EncryptedSharedPrefsCreateMethod() {
20+
getDeclaringType() instanceof TypeEncryptedSharedPreferences and
21+
hasName("create")
22+
}
2123
}
22-
}
2324

24-
/* Returns `android.content.SharedPreferences.Editor` from the `edit` call of `android.content.SharedPreferences`. */
25-
library class SharedPreferencesGetEditorMethod extends Method {
26-
SharedPreferencesGetEditorMethod() {
27-
getDeclaringType() instanceof TypeSharedPreferences and
28-
hasName("edit") and
29-
getReturnType() instanceof TypeSharedPreferencesEditor
25+
/* A getter method of `android.content.SharedPreferences`. */
26+
class SharedPreferencesGetMethod extends Method {
27+
SharedPreferencesGetMethod() {
28+
getDeclaringType() instanceof TypeSharedPreferences and
29+
getName().matches("get%")
30+
}
3031
}
31-
}
3232

33-
/* Definitions related to `android.content.SharedPreferences.Editor`. */
34-
library class TypeSharedPreferencesEditor extends Interface {
35-
TypeSharedPreferencesEditor() { hasQualifiedName("android.content", "SharedPreferences$Editor") }
36-
}
33+
/* Returns `android.content.SharedPreferences.Editor` from the `edit` call of `android.content.SharedPreferences`. */
34+
class SharedPreferencesGetEditorMethod extends Method {
35+
SharedPreferencesGetEditorMethod() {
36+
getDeclaringType() instanceof TypeSharedPreferences and
37+
hasName("edit") and
38+
getReturnType() instanceof TypeSharedPreferencesEditor
39+
}
40+
}
3741

38-
/* A setter method for `android.content.SharedPreferences`. */
39-
library class SharedPreferencesSetMethod extends Method {
40-
SharedPreferencesSetMethod() {
41-
getDeclaringType() instanceof TypeSharedPreferencesEditor and
42-
getName().matches("put%")
42+
/* Definitions related to `android.content.SharedPreferences.Editor`. */
43+
class TypeSharedPreferencesEditor extends Interface {
44+
TypeSharedPreferencesEditor() {
45+
hasQualifiedName("android.content", "SharedPreferences$Editor")
46+
}
47+
}
48+
49+
/* A setter method for `android.content.SharedPreferences`. */
50+
class SharedPreferencesSetMethod extends Method {
51+
SharedPreferencesSetMethod() {
52+
getDeclaringType() instanceof TypeSharedPreferencesEditor and
53+
getName().matches("put%")
54+
}
4355
}
44-
}
4556

46-
/* A setter method for `android.content.SharedPreferences`. */
47-
library class SharedPreferencesStoreMethod extends Method {
48-
SharedPreferencesStoreMethod() {
49-
getDeclaringType() instanceof TypeSharedPreferencesEditor and
50-
hasName(["commit", "apply"])
57+
/* A setter method for `android.content.SharedPreferences`. */
58+
class SharedPreferencesStoreMethod extends Method {
59+
SharedPreferencesStoreMethod() {
60+
getDeclaringType() instanceof TypeSharedPreferencesEditor and
61+
hasName(["commit", "apply"])
62+
}
5163
}
5264
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
| ClearTextStorageSharedPrefs.java:16:3:16:17 | commit(...) | 'SharedPreferences' class $@ containing $@ is stored here. Data was added $@. | ClearTextStorageSharedPrefs.java:13:19:13:36 | edit(...) | edit(...) | ClearTextStorageSharedPrefs.java:15:32:15:39 | password | sensitive data | ClearTextStorageSharedPrefs.java:15:32:15:39 | password | here |
1+
| ClearTextStorageSharedPrefs.java:19:3:19:17 | commit(...) | 'SharedPreferences' class $@ containing $@ is stored here. Data was added $@. | ClearTextStorageSharedPrefs.java:16:19:16:36 | edit(...) | edit(...) | ClearTextStorageSharedPrefs.java:18:32:18:39 | password | sensitive data | ClearTextStorageSharedPrefs.java:18:32:18:39 | password | here |

java/ql/test/query-tests/security/CWE-312/semmle/tests/ClearTextStorageSharedPrefs.java

Lines changed: 61 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@
44
import android.content.SharedPreferences.Editor;
55
import androidx.security.crypto.MasterKey;
66
import androidx.security.crypto.EncryptedSharedPreferences;
7+
import java.nio.charset.StandardCharsets;
8+
import java.util.Base64;
9+
import java.security.MessageDigest;
710

8-
/** Android activity that tests saving sensitive information in `SharedPreferences` */
11+
/* Android activity that tests saving sensitive information in `SharedPreferences` */
912
public class ClearTextStorageSharedPrefs extends Activity {
1013
// BAD - save sensitive information in cleartext
1114
public void testSetSharedPrefs1(Context context, String name, String password) {
@@ -26,13 +29,27 @@ public void testSetSharedPrefs2(Context context, String name, String password) {
2629
}
2730

2831
private static String encrypt(String cleartext) {
29-
//Use an encryption or hashing algorithm in real world. The demo below just returns an arbitrary value.
30-
String cipher = "whatever_encrypted";
31-
return cipher;
32+
// Use an encryption or hashing algorithm in real world. The demo below just returns its hash.
33+
MessageDigest digest = MessageDigest.getInstance("SHA-256");
34+
byte[] hash = digest.digest(cleartext.getBytes(StandardCharsets.UTF_8));
35+
String encoded = Base64.getEncoder().encodeToString(hash);
36+
return encoded;
3237
}
3338

34-
// GOOD - save sensitive information using the built-in `EncryptedSharedPreferences` class in androidx.
39+
// GOOD - save sensitive information in encrypted format using separate variables
3540
public void testSetSharedPrefs3(Context context, String name, String password) {
41+
String encUsername = encrypt(name);
42+
String encPassword = encrypt(password);
43+
SharedPreferences sharedPrefs = context.getSharedPreferences("user_prefs", Context.MODE_PRIVATE);
44+
Editor editor = sharedPrefs.edit();
45+
editor.putString("name", encUsername);
46+
editor.putString("password", encPassword);
47+
editor.commit();
48+
}
49+
50+
51+
// GOOD - save sensitive information using the built-in `EncryptedSharedPreferences` class in androidx
52+
public void testSetSharedPrefs4(Context context, String name, String password) {
3653
MasterKey masterKey = new MasterKey.Builder(context, MasterKey.DEFAULT_MASTER_KEY_ALIAS)
3754
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
3855
.build();
@@ -50,4 +67,43 @@ public void testSetSharedPrefs3(Context context, String name, String password) {
5067
editor.putString("password", password);
5168
editor.commit();
5269
}
70+
71+
// GOOD - save sensitive information using the built-in `EncryptedSharedPreferences` class in androidx
72+
public void testSetSharedPrefs5(Context context, String name, String password) {
73+
MasterKey masterKey = new MasterKey.Builder(context, MasterKey.DEFAULT_MASTER_KEY_ALIAS)
74+
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
75+
.build();
76+
77+
SharedPreferences.Editor editor = EncryptedSharedPreferences.create(
78+
context,
79+
"secret_shared_prefs",
80+
masterKey,
81+
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
82+
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM)
83+
.edit();
84+
85+
// Use the shared preferences and editor as you normally would
86+
editor.putString("name", name);
87+
editor.putString("password", password);
88+
editor.commit();
89+
}
90+
91+
// GOOD - save sensitive information using the built-in `EncryptedSharedPreferences` class in androidx
92+
public void testSetSharedPrefs6(Context context, String name, String password) {
93+
MasterKey masterKey = new MasterKey.Builder(context, MasterKey.DEFAULT_MASTER_KEY_ALIAS)
94+
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
95+
.build();
96+
97+
SharedPreferences.Editor editor = EncryptedSharedPreferences.create(
98+
context,
99+
"secret_shared_prefs",
100+
masterKey,
101+
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
102+
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM)
103+
.edit()
104+
.putString("name", name) // Use the shared preferences and editor as you normally would
105+
.putString("password", password);
106+
107+
editor.commit();
108+
}
53109
}

0 commit comments

Comments
 (0)