Skip to content

Create rule S7612: Passwords should not be stored on device (SONARSEC-6836) #5136

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
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
3 changes: 3 additions & 0 deletions rules/S7612/common/compliant.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Applications storing passwords locally often do so because they want users to stay logged in.
To keep users logged in, use or implement session management on the server side.
If not possible or applicable, use the Android Credential Manager
3 changes: 3 additions & 0 deletions rules/S7612/common/resources/docs.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
=== Documentation

* Android Documentation - https://developer.android.com/identity/sign-in/credential-manager[Sign in your user with Credential Manager]
7 changes: 7 additions & 0 deletions rules/S7612/common/resources/standards.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
=== Standards

* OWASP - https://owasp.org/Top10/A04_2021-Insecure_Design/[Top 10 2021 Category A4 - Insecure Design]
* OWASP - https://owasp.org/www-project-top-ten/2017/A3_2017-Sensitive_Data_Exposure[Top 10 2017 Category A3 - Sensitive Data Exposure]
* OWASP - https://owasp.org/www-project-mobile-top-10/2023-risks/m9-insecure-data-storage.html[Mobile Top 10 2024 Category M9 - Insecure Data Storage]
* CWE - https://cwe.mitre.org/data/definitions/256[CWE-256 - Plaintext Storage of a Password]
* STIG Viewer - https://stigviewer.com/stigs/application_security_and_development/2024-12-06/finding/V-222602[Application Security and Development: V-222542] - The application must only store cryptographic representations of passwords.
7 changes: 7 additions & 0 deletions rules/S7612/highlighting.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
=== Highlighting

"[varname]" is tainted (assignments and parameters)

this argument is tainted (method invocations)

the returned value is tainted (returns & method invocations results)
7 changes: 7 additions & 0 deletions rules/S7612/impact.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
=== What is the potential impact?

==== Account Compromise
The most direct problem is that an attacker can take over the user's account in your application. By reading the stored password from the device, the attacker can log in as the user. Once inside, they could potentially view private information, send messages while impersonating the user, or access other features of the account.

==== The Danger of Password Reuse
Using the same password for multiple websites and applications is a common behavior. If attackers find a password from one application, they will often try that same password on other, more valuable services, such as email, banking, or social media accounts. This is a technique known as credential stuffing. A security weakness in one application can therefore lead to a much broader compromise of a user's digital life.
71 changes: 71 additions & 0 deletions rules/S7612/java/how-to-fix-it/android.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
== How to fix it in Android

=== Code examples

==== Noncompliant code example

[source,java,diff-id=11,diff-type=noncompliant]
----
public class ExampleActivity extends AppCompatActivity {

private EditText passwordEditText;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_xml_layout);

SharedPreferences sharedPreferences = getSharedPreferences("example", Context.MODE_PRIVATE);

passwordEditText = findViewById(R.id.passwordEditText);
Button savePasswordButton = findViewById(R.id.savePasswordButton);

savePasswordButton.setOnClickListener(v -> {
String password = passwordEditText.getText().toString();
if (!password.isEmpty()) {
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("Password", password);
editor.apply();
}
});
}
}
----

==== Compliant solution

include::../../common/compliant.adoc[]

[source,java,diff-id=11,diff-type=compliant]
----
public class ExampleActivity extends AppCompatActivity {

private EditText passwordEditText;
private CredentialManagerCallback<CreateCredentialResponse, CreateCredentialException> callback;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_xml_layout);

SharedPreferences sharedPreferences = getSharedPreferences("example", Context.MODE_PRIVATE);

passwordEditText = findViewById(R.id.passwordEditText);
Button savePasswordButton = findViewById(R.id.savePasswordButton);

savePasswordButton.setOnClickListener(v -> {
String password = passwordEditText.getText().toString();
if (!password.isEmpty()) {
CredentialManager credentialManager = CredentialManager.create(this);
CreatePasswordRequest request = new CreatePasswordRequest(
"example",
password
);
credentialManager.createCredentialAsync (this, request,null, Runnable::run, callback);
}
});
}
}
----
2 changes: 2 additions & 0 deletions rules/S7612/java/metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{
}
29 changes: 29 additions & 0 deletions rules/S7612/java/rule.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
include::../summary.adoc[]

== Why is this an issue?

include::../rationale.adoc[]

include::../impact.adoc[]

// How to fix it section

include::how-to-fix-it/android.adoc[]

== Resources

include::../common/resources/docs.adoc[]

include::../common/resources/standards.adoc[]

ifdef::env-github,rspecator-view[]

'''
== Implementation Specification
(visible only on this page)

include::../message.adoc[]

include::../highlighting.adoc[]

endif::env-github,rspecator-view[]
73 changes: 73 additions & 0 deletions rules/S7612/kotlin/how-to-fix-it/android.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
== How to fix it in Android

=== Code examples

==== Noncompliant code example

[source,kotlin,diff-id=21,diff-type=noncompliant]
----
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "user_prefs_datastore")

@Composable
fun PasswordInputScreen(modifier: Modifier = Modifier, onSavePassword: (String) -> Unit) {
var password by remember { mutableStateOf("") }
val context = LocalContext.current
val coroutineScope = rememberCoroutineScope() // Add this line

Column() {
TextField(
value = password,
onValueChange = { password = it },
label = { Text("Password") },
visualTransformation = PasswordVisualTransformation(),
)
Button(onClick = {
coroutineScope.launch { // Launch a coroutine
context.dataStore.edit { preferences ->
preferences[stringPreferencesKey("saved_password")] = password
}
}
}) {
Text("Save Password")
}
}
}
----

==== Compliant solution

include::../../common/compliant.adoc[]

[source,kotlin,diff-id=21,diff-type=compliant]
----
@Composable
fun PasswordInputScreen(modifier: Modifier = Modifier, onSavePassword: (String) -> Unit) {
var password by remember { mutableStateOf("") }
val context = LocalContext.current
val coroutineScope = rememberCoroutineScope() // Add this line

Column() {
TextField(
value = password,
onValueChange = { password = it },
label = { Text("Password") },
visualTransformation = PasswordVisualTransformation(),
)
Button(onClick = {
coroutineScope.launch { // Launch a coroutine
val credentialManager = CredentialManager.create(context)
val request = CreatePasswordRequest(
id = "example",
password = password
)
credentialManager.createCredential(
request = request,
context = context
)
}
}) {
Text("Save Password")
}
}
}
----
2 changes: 2 additions & 0 deletions rules/S7612/kotlin/metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{
}
29 changes: 29 additions & 0 deletions rules/S7612/kotlin/rule.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
include::../summary.adoc[]

== Why is this an issue?

include::../rationale.adoc[]

include::../impact.adoc[]

// How to fix it section

include::how-to-fix-it/android.adoc[]

== Resources

include::../common/resources/docs.adoc[]

include::../common/resources/standards.adoc[]

ifdef::env-github,rspecator-view[]

'''
== Implementation Specification
(visible only on this page)

include::../message.adoc[]

include::../highlighting.adoc[]

endif::env-github,rspecator-view[]
3 changes: 3 additions & 0 deletions rules/S7612/message.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
=== Message

Change this code to not user's password on the device.
56 changes: 56 additions & 0 deletions rules/S7612/metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{
"title": "Passwords should not be stored on device",
"type": "VULNERABILITY",
"code": {
"impacts": {
"SECURITY": "HIGH"
},
"attribute": "COMPLETE"
},
"status": "ready",
"remediation": {
"func": "Constant/Issue",
"constantCost": "1d"
},
"tags": [
],
"extra": {
},
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-7612",
"sqKey": "S7612",
"scope": "All",
"securityStandards": {
"CWE": [
256
],
"OWASP": [
"A3"
],
"OWASP Top 10 2021": [
"A4"
],
"OWASP Mobile Top 10 2024": [
"M9"
],
"PCI DSS 3.2": [
"6.5.3"
],
"PCI DSS 4.0": [
"6.2.4"
],
"ASVS 4.0": [
"2.10.3",
"2.4.1",
"2.4.2",
"2.4.3",
"2.4.4",
"2.4.5"
],
"STIG ASD_V5R3": [
"V-222542"
]
},
"defaultQualityProfiles": ["Sonar way"],
"quickfix": "unknown"
}
1 change: 1 addition & 0 deletions rules/S7612/rationale.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
When an application stores a password without encryption, it creates a security risk for the user. Often, the password is not directly accessible by a third-parties thanks to OS and application level protections. Yet password leakage can happen if the device is lost or stolen, or if an attacker compromises the application.
2 changes: 2 additions & 0 deletions rules/S7612/summary.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
This vulnerability occurs when an application saves password, in plaintext on the device's storage.
Password is a risk of being leaked if the device or the application itself is compromised.