Skip to content
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

Java: Add query for sensitive data exposed in text fields #15396

Merged
merged 11 commits into from
Feb 5, 2024

Conversation

joefarebrother
Copy link
Contributor

@joefarebrother joefarebrother commented Jan 22, 2024

This query checks for sensitive data being exposed to text fields.
Fields that have their visibility or input type set (dynamically or via xml) are considered safe and excluded.

Copy link
Contributor

QHelp previews:

java/ql/src/Security/CWE/CWE-200/AndroidSensitiveNotifications.qhelp

Exposure of sensitive information to notifications

Sensitive information such as passwords or two-factor authentication (2FA) codes should not be exposed in a system notification. Notifications should not be considered secure, as other untrusted applications may be able to use a NotificationListenerService to read the contents of notifications.

Recommendation

Do not expose sensitive data in notifications.

Example

In the following sample, the password is sent as part of a notification. This can allow another application to read this password.

// BAD: `password` is exposed in a notification.
void confirmPassword(String password) {
    NotificationManager manager = NotificationManager.from(this);
    manager.send(
        new Notification.Builder(this, CHANNEL_ID)
        .setContentText("Your password is: " + password)
        .build());
}

References

Copy link
Contributor

github-actions bot commented Jan 22, 2024

⚠️ The head of this PR and the base branch were compared for differences in the framework coverage reports. A recent commit removed the previously reported differences.

Copy link
Contributor

github-actions bot commented Jan 24, 2024

QHelp previews:

java/ql/src/Security/CWE/CWE-200/AndroidSensitiveTextField.qhelp

Exposure of sensitive information to UI text views.

Sensitive information such as passwords should not be displayed in UI components unless explicitly required, to mitigate shoulder-surfing attacks.

Recommendation

For editable text fields containing sensitive information, the inputType should be set to textPassword or similar to ensure it is properly masked. Otherwise, sensitive data that must be displayed should be hidden by default, and only revealed based on an explicit user action.

Example

In the following (bad) case, sensitive information in password is exposed to the TextView.

TextView pwView = getViewById(R.id.pw_text);
pwView.setText("Your password is: " + password);

In the following (good) case, the user must press a button to reveal sensitive information.

TextView pwView = findViewById(R.id.pw_text);
pwView.setVisibility(View.INVISIBLE);
pwView.setText("Your password is: " + password);

Button showButton = findViewById(R.id.show_pw_button);
showButton.setOnClickListener(new View.OnClickListener() {
    public void onClick(View v) {
      pwView.setVisibility(View.VISIBLE);
    }
});

References

@joefarebrother joefarebrother force-pushed the android-sensitive-ui-text branch from 40cff8c to 22d8269 Compare January 24, 2024 14:53
@joefarebrother joefarebrother force-pushed the android-sensitive-ui-text branch from 22d8269 to 3abd670 Compare January 29, 2024 16:33
@joefarebrother joefarebrother changed the title [Draft] Java: Add query for sensitive data exposed in text fields Java: Add query for sensitive data exposed in text fields Jan 29, 2024
@joefarebrother joefarebrother marked this pull request as ready for review January 29, 2024 22:45
@joefarebrother joefarebrother requested a review from a team as a code owner January 29, 2024 22:45
Copy link
Contributor

@atorralba atorralba left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks quite good to me! I like the introduction of the Layout.qll library and moving everything layout-related there.

I added some minor comments for your consideration. As always, DCA, MRVA and docs review will have the final say.

java/ql/lib/semmle/code/java/security/SensitiveUiQuery.qll Outdated Show resolved Hide resolved
Comment on lines +47 to +53
private class MaskCall extends MethodCall {
MaskCall() {
this.getMethod().getAnOverride*().hasQualifiedName("android.widget", "TextView", "setInputType")
or
this.getMethod().getAnOverride*().hasQualifiedName("android.view", "View", "setVisibility")
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it worth checking the arguments of the method call? Either that it's View.INVISIBLE or its literal value, or some InputType.*PASSWORD* variation, its literal value, or a binary OR operation with one of those as an operand.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a potential precision improvement to reduce FNs, but I'm not sure it's worthwhile. For setVisibility, it seems redundant; as the visibility is either being set from invisible to visible, or from visible to invisible; either implying it would have been invisible at some point. For setInputType, ignoring it yields a few more results in just one project, which set input type to values other than passwords so would appear with a more precise check here, but i'm not sure whether they are TPs.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, let's just roll with it and revisit this in the future if we start seeing FNs.

java/ql/lib/semmle/code/java/frameworks/android/Layout.qll Outdated Show resolved Hide resolved
}

/** Holds if data can flow from `e1` to `e2` with local flow steps as well as flow through fields containing `View`s */
private predicate localViewFieldExprFlow(Expr e1, Expr e2) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit unsure about how this will perform. If you see any indications of this performing badly in DCA let me know, there are a couple of tricks we can use as alternative to this kind of local flow (the modules SimpleGlobal and LocalTaintFlow), and we can also tweak the field read-write step to use the FieldValueNode instead. But only really needed if this predicate gives problems.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Performance does not seem to be much of an issue at the moment. DCA looks fine and MRVA runs take about 6 mins, the same as other queries like sql injection.

id_field.getDeclaringType() = r_id and
id_field.hasName(name)
|
DataFlow::localExprFlow(id_field.getAnAccess(), result.getArgument(0))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider whether SimpleGlobal flow may be more appropriate (if you think you need more than local flow for tracking the ID).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've not seen any cases where local flow is insufficient to find the id used by a call to findViewById

}

/** A local flow step that also flows through access to fields containing `View`s */
private predicate localViewFieldFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to confirm, this is for tracking something like the following, right?

EditText test15;

void test1() {
  test15 = findViewById(R.id.test15);
}

void test2(String password) {
  test15.setVisibility(View.INVISIBLE);
  test15.setText(password);
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed; it was a common FP in one of my MRVA runs for a view defined in xml as textPassword to be stored in a field this way.

@joefarebrother joefarebrother added the ready-for-doc-review This PR requires and is ready for review from the GitHub docs team. label Feb 2, 2024
@subatoi subatoi self-assigned this Feb 2, 2024
subatoi
subatoi previously approved these changes Feb 2, 2024
Copy link
Contributor

@subatoi subatoi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, thank you! Only two very small comments 👍

Co-authored-by: Ben Ahmady <32935794+subatoi@users.noreply.github.com>
@joefarebrother
Copy link
Contributor Author

@subatoi Those changes have been made - can you re-approve?

Copy link
Contributor

@subatoi subatoi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

@joefarebrother joefarebrother merged commit 525f271 into github:main Feb 5, 2024
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Java ready-for-doc-review This PR requires and is ready for review from the GitHub docs team.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants