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

Skip to content

Commit 0bd6255

Browse files
committed
Query for cleartext storage using Android SharedPreferences
1 parent 09cfb24 commit 0bd6255

13 files changed

Lines changed: 1120 additions & 11 deletions

File tree

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
public void testSetSharedPrefs(Context context, String name, String password)
2+
{
3+
{
4+
// BAD - save sensitive information in cleartext
5+
SharedPreferences sharedPrefs = context.getSharedPreferences("user_prefs", Context.MODE_PRIVATE);
6+
Editor editor = sharedPrefs.edit();
7+
editor.putString("name", name);
8+
editor.putString("password", password);
9+
editor.commit();
10+
}
11+
12+
{
13+
// GOOD - save sensitive information in encrypted format
14+
SharedPreferences sharedPrefs = context.getSharedPreferences("user_prefs", Context.MODE_PRIVATE);
15+
Editor editor = sharedPrefs.edit();
16+
editor.putString("name", encrypt(name));
17+
editor.putString("password", encrypt(password));
18+
editor.commit();
19+
}
20+
21+
{
22+
// GOOD - save sensitive information using the built-in `EncryptedSharedPreferences` class in androidx.
23+
MasterKey masterKey = new MasterKey.Builder(context, MasterKey.DEFAULT_MASTER_KEY_ALIAS)
24+
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
25+
.build();
26+
27+
SharedPreferences sharedPreferences = EncryptedSharedPreferences.create(
28+
context,
29+
"secret_shared_prefs",
30+
masterKey,
31+
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
32+
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM);
33+
34+
SharedPreferences.Editor editor = sharedPreferences.edit();
35+
editor.putString("name", name);
36+
editor.putString("password", password);
37+
editor.commit();
38+
}
39+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
2+
<qhelp>
3+
<overview>
4+
<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.
6+
</p>
7+
</overview>
8+
9+
<recommendation>
10+
<p>
11+
Use the <code>EncryptedSharedPreferences</code> API or other encryption algorithms for storing sensitive information.
12+
</p>
13+
</recommendation>
14+
15+
<example>
16+
<p>
17+
In the first example, sensitive user information is stored in cleartext.
18+
</p>
19+
20+
<p>
21+
In the second and third examples, the code encrypts sensitive information before saving to the device.
22+
</p>
23+
<sample src="ClearTextStorageSharedPrefs.java" />
24+
</example>
25+
26+
<references>
27+
<li>
28+
CWE:
29+
<a href="https://cwe.mitre.org/data/definitions/312.html">CWE-312: Cleartext Storage of Sensitive Information</a>
30+
</li>
31+
<li>
32+
Android Developers:
33+
<a href="https://developer.android.com/topic/security/data">Work with data more securely</a>
34+
</li>
35+
<li>
36+
PRO ANDROID DEV:
37+
<a href="https://proandroiddev.com/encrypted-preferences-in-android-af57a89af7c8">Encrypted Preferences in Android</a>
38+
</li>
39+
</references>
40+
</qhelp>
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/**
2+
* @name Cleartext storage of sensitive information using `SharedPreferences` on Android
3+
* @description Cleartext Storage of Sensitive Information using SharedPreferences on Android allows user with root privileges to access or unexpected exposure from chained vulnerabilities.
4+
* @kind problem
5+
* @id java/android/cleartext-storage-shared-prefs
6+
* @tags security
7+
* external/cwe/cwe-312
8+
*/
9+
10+
import java
11+
import SensitiveStorage
12+
13+
from SensitiveSource data, SharedPreferencesEditor s, Expr input, Expr store
14+
where
15+
input = s.getAnInput() and
16+
store = s.getAStore() and
17+
data.flowsToCached(input) and
18+
// Exclude results in test code.
19+
not testMethod(store.getEnclosingCallable()) and
20+
not testMethod(data.getEnclosingCallable())
21+
select store, "'SharedPreferences' class $@ containing $@ is stored here. Data was added $@.", s,
22+
s.toString(), data, "sensitive data", input, "here"

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

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import java
22
import semmle.code.java.frameworks.Properties
33
import semmle.code.java.frameworks.JAXB
4+
import semmle.code.java.frameworks.android.SharedPreferences
45
import semmle.code.java.dataflow.TaintTracking
56
import semmle.code.java.dataflow.DataFlow3
67
import semmle.code.java.dataflow.DataFlow4
@@ -28,6 +29,10 @@ private class SensitiveSourceFlowConfig extends TaintTracking::Configuration {
2829
m.getMethod() instanceof PropertiesSetPropertyMethod and sink.asExpr() = m.getArgument(1)
2930
)
3031
or
32+
exists(MethodAccess m |
33+
m.getMethod() instanceof SharedPreferencesSetMethod and sink.asExpr() = m.getArgument(1)
34+
)
35+
or
3136
sink.asExpr() = getInstanceInput(_, _)
3237
}
3338

@@ -243,3 +248,72 @@ class Marshallable extends ClassStore {
243248
)
244249
}
245250
}
251+
252+
/* Holds if the method call is a setter method of `SharedPreferences`. */
253+
private predicate sharedPreferencesInput(DataFlow::Node sharedPrefs, Expr input) {
254+
exists(MethodAccess m |
255+
m.getMethod() instanceof SharedPreferencesSetMethod and
256+
input = m.getArgument(1) and
257+
sharedPrefs.asExpr() = m.getQualifier()
258+
)
259+
}
260+
261+
/* Holds if the method call is the save method of `SharedPreferences`. */
262+
private predicate sharedPreferencesStore(DataFlow::Node sharedPrefs, Expr store) {
263+
exists(MethodAccess m |
264+
m.getMethod() instanceof SharedPreferencesStoreMethod and
265+
store = m and
266+
sharedPrefs.asExpr() = m.getQualifier()
267+
)
268+
}
269+
270+
/* Flow from `SharedPreferences` to the method call changing its value. */
271+
class SharedPreferencesFlowConfig extends TaintTracking::Configuration {
272+
SharedPreferencesFlowConfig() { this = "SensitiveStorage::SharedPreferencesFlowConfig" }
273+
274+
override predicate isSource(DataFlow::Node src) {
275+
src.asExpr() instanceof SharedPreferencesEditor
276+
}
277+
278+
override predicate isSink(DataFlow::Node sink) {
279+
sharedPreferencesInput(sink, _) or
280+
sharedPreferencesStore(sink, _)
281+
}
282+
283+
override predicate isSanitizer(DataFlow::Node n) {
284+
exists(MethodAccess ma |
285+
ma.getMethod().getName().toLowerCase().matches("%encrypt%") and
286+
n.asExpr() = ma.getAnArgument()
287+
)
288+
}
289+
}
290+
291+
/** The call to get a `SharedPreferences.Editor` object, which can set shared preferences or be stored to device. */
292+
class SharedPreferencesEditor extends MethodAccess {
293+
SharedPreferencesEditor() {
294+
this.getMethod() instanceof SharedPreferencesGetEditorMethod and
295+
not exists(
296+
MethodAccess cma // not exists `SharedPreferences sharedPreferences = EncryptedSharedPreferences.create(...)`
297+
|
298+
cma.getQualifier().getType() instanceof TypeEncryptedSharedPreferences and
299+
cma.getMethod().hasName("create") and
300+
cma.getParent().(VariableAssign).getDestVar().getAnAccess() = this.getQualifier()
301+
)
302+
}
303+
304+
/** Gets an input, for example `input` in `editor.putString("password", password);`. */
305+
Expr getAnInput() {
306+
exists(SharedPreferencesFlowConfig conf, DataFlow::Node n |
307+
sharedPreferencesInput(n, result) and
308+
conf.hasFlow(DataFlow::exprNode(this), n)
309+
)
310+
}
311+
312+
/** Gets a store, for example `editor.commit();`. */
313+
Expr getAStore() {
314+
exists(SharedPreferencesFlowConfig conf, DataFlow::Node n |
315+
sharedPreferencesStore(n, result) and
316+
conf.hasFlow(DataFlow::exprNode(this), n)
317+
)
318+
}
319+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/* Definitions related to `android.content.SharedPreferences`. */
2+
import semmle.code.java.Type
3+
4+
/* The interface `android.content.SharedPreferences` */
5+
library class TypeSharedPreferences extends Interface {
6+
TypeSharedPreferences() { hasQualifiedName("android.content", "SharedPreferences") }
7+
}
8+
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")
13+
}
14+
}
15+
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%")
21+
}
22+
}
23+
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
30+
}
31+
}
32+
33+
/* Definitions related to `android.content.SharedPreferences.Editor`. */
34+
library class TypeSharedPreferencesEditor extends Interface {
35+
TypeSharedPreferencesEditor() { hasQualifiedName("android.content", "SharedPreferences$Editor") }
36+
}
37+
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%")
43+
}
44+
}
45+
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"])
51+
}
52+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +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 |
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import android.app.Activity;
2+
import android.content.Context;
3+
import android.content.SharedPreferences;
4+
import android.content.SharedPreferences.Editor;
5+
import androidx.security.crypto.MasterKey;
6+
import androidx.security.crypto.EncryptedSharedPreferences;
7+
8+
/** Android activity that tests saving sensitive information in `SharedPreferences` */
9+
public class ClearTextStorageSharedPrefs extends Activity {
10+
// BAD - save sensitive information in cleartext
11+
public void testSetSharedPrefs1(Context context, String name, String password) {
12+
SharedPreferences sharedPrefs = context.getSharedPreferences("user_prefs", Context.MODE_PRIVATE);
13+
Editor editor = sharedPrefs.edit();
14+
editor.putString("name", name);
15+
editor.putString("password", password);
16+
editor.commit();
17+
}
18+
19+
// GOOD - save sensitive information in encrypted format
20+
public void testSetSharedPrefs2(Context context, String name, String password) {
21+
SharedPreferences sharedPrefs = context.getSharedPreferences("user_prefs", Context.MODE_PRIVATE);
22+
Editor editor = sharedPrefs.edit();
23+
editor.putString("name", encrypt(name));
24+
editor.putString("password", encrypt(password));
25+
editor.commit();
26+
}
27+
28+
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+
}
33+
34+
// GOOD - save sensitive information using the built-in `EncryptedSharedPreferences` class in androidx.
35+
public void testSetSharedPrefs3(Context context, String name, String password) {
36+
MasterKey masterKey = new MasterKey.Builder(context, MasterKey.DEFAULT_MASTER_KEY_ALIAS)
37+
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
38+
.build();
39+
40+
SharedPreferences sharedPreferences = EncryptedSharedPreferences.create(
41+
context,
42+
"secret_shared_prefs",
43+
masterKey,
44+
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
45+
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM);
46+
47+
// Use the shared preferences and editor as you normally would
48+
SharedPreferences.Editor editor = sharedPreferences.edit();
49+
editor.putString("name", name);
50+
editor.putString("password", password);
51+
editor.commit();
52+
}
53+
54+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Security/CWE/CWE-312/ClearTextStorageSharedPrefs.ql
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
// semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/google-android-9.0.0

0 commit comments

Comments
 (0)