Stripe-android: 3DS v1: isn't webview inherently insecure?

Created on 15 May 2020  路  18Comments  路  Source: stripe/stripe-android

If I understand correctly, for 3DS version 1, the Stripe android sdk displays the bank page inside a WebView. (I know this isn't an issue for version 3DS2, which uses a native activity instead of Webview)

Even though an app integrating this sdk may promise to behave well, and not capture data the user inputs, webviews are inherently insecure, from what I understand.

Here are a few examples/references:

  • Google dropped webview for oauth integration partly for security reasons:
    > The device browser also provides improved security as apps are able to inspect and modify content in a web-view, but not content shown in the browser.
  • Mastercard dropped webview also for security reasons:
    > Masterpass no longer supports webviews due to security concerns. Merchants must use Safari webviewcontroller or Chrome custom tabs for iOS and Android implementation respectively. This content can be found in Tutorials > Mobile App Checkout.
    (edited)
  • Plaid rejected webview in its link sdk, to interact with banks:
    > In order to run the Link web app in a WebView, Javascript has to be enabled. This opens the door for other apps to run malicious code, such as registering callbacks that try to intercept usernames and passwords. Additionally, a malicious app could open another web page that mimics the Link flow in a phishing attempt.
  • The IETF "OAuth 2.0 for Native Apps" document section 8.12 explains why webview (embedded user-agent) is insecure:
    > This best current practice requires that native apps MUST NOT use embedded user-agents to perform authorization requests, and allows that authorization endpoints MAY take steps to detect and block authorization requests in embedded user-agents. The security considerations for these requirements are detailed herein.
    >
    >Embedded user-agents are an alternative method for authorizing native apps. These embedded user agents are unsafe for use by third-parties to the authorization server by definition, as the app that hosts the embedded user-agent can access the user's full authentication credential, not just the OAuth authorization grant that was intended for the app.
    >
    >In typical web-view based implementations of embedded user-agents, the host application can: log every keystroke entered in the form to capture usernames and passwords; automatically submit forms and bypass user-consent; copy session cookies and use them to perform authenticated actions as the user.
    >
    >Even when used by trusted apps belonging to the same party as the authorization server, embedded user-agents violate the principle of least privilege by having access to more powerful credentials than they need, potentially increasing the attack surface.
    >
    >Encouraging users to enter credentials in an embedded user-agent without the usual address bar and visible certificate validation features that browsers have makes it impossible for the user to know if they are signing in to the legitimate site, and even when they are, it trains them that it's OK to enter credentials without validating the site first.

Is there any plan for the Stripe SDK to move to a more secure way for the user to enter the 3DS version 1 code, say with Chrome Custom Tabs?

Of course, if I've misunderstood anything, and somehow webview security is less of a problem for 3DS than for other sensitive information like oauth, please do let me know!

Also: I realize this is only a temporary issue, as 3DS2 will eventually replace 3DS1. But unfortunately, the rollout of v2 is very slow and v1 is still being used nearly everywhere, from what I understand.

triaged

All 18 comments

@calvarez-ov thanks for filing this detailed ticket. We'll investigate this issue and get back to you soon.

Any input on this? We are concerned about the security of our customers, and want them to be confident the payment system we use is secure, beyond just "take our word for it". :)

@calvarez-ov sorry for the delay in responding to you. We're continuing to investigate this issue internally, including exploring a solution using Chrome Custom Tabs.

For the time being, if you wish to avoid using WebViews for security concerns, you can handle the payment authentication by doing something like the code below. You would also need to set up your app to handle the return URL.

private suspend fun confirmPaymentIntent(
    paymentMethodId: String,
    clientSecret: String
) {
    val paymentIntent = stripe.confirmPaymentIntentSynchronous(
        ConfirmPaymentIntentParams.createWithPaymentMethodId(
            paymentMethodId = paymentMethodId,
            clientSecret = clientSecret,
            returnUrl = "app://return_url"
        )
    )

    withContext(Dispatchers.Main) {
        if (paymentIntent != null && paymentIntent.requiresAction()) {
            when (val nextActionData = paymentIntent.nextActionData) {
                is StripeIntent.NextActionData.RedirectToUrl -> {
                    // launch the payment authentication URL
                    nextActionData.url
                }
                is StripeIntent.NextActionData.SdkData.Use3DS1 -> {
                    // launch the payment authentication URL
                    nextActionData.url
                }
                else -> {
                    // ignore
                }
            }
        }
    }
}

Thanks for the reply @mshafrir !

I seem to missing a piece of the puzzle: once my app is reopened again thanks to the returnUrl, how can I obtain the PaymentResult?

@calvarez-ov the returnUrl will have query parameters including the client_secret. You can re-fetch the PaymentIntent using the client secret and check its status to determine if user actions were successfully resolved.

Let me know if that makes sense!

If it's not too much trouble, could you point me in the direction of the api used to refetch the PaymentIntent?

Thanks! This has been a big help to get started 馃憤

@calvarez-ov I'm happy to hear that. I'll close this issue but feel free to reopen if you need further assistance.

We've got a POC with custom tabs basically working.

It's a lot of code changes for us though. (We were previously using the basic integration.) So I'm not sure if it's worth it to continue in this direction, or to wait for the stripe sdk to have custom tabs support.

To help us decide, could you please provide whatever information you can at this point for the following questions?

  • When the stripe sdk eventually supports custom tabs, will using it require many changes to an app which is currently using the basic integration?
  • Any idea when this solution will be available?

Thanks!

@calvarez-ov we will investigate adding Custom Tabs support sometime in the future, but unfortunately I can't provide any timeline or guarantees at this time. I would recommend implementing this functionality yourself if you need it.

@calvarez-ov we are starting work on moving to Custom Tabs in #3596. Thanks for flagging this issue last year.

@calvarez-ov our next release, 16.7.0, adds support for 3DS1 authentication via Custom Tabs. Custom Tabs will be used when the customer's device supports Custom Tabs and when the PaymentIntent/SetupIntent is confirmed without a custom return_url (i.e. value is null). Otherwise, we'll fall back to WebView. In a future release we'll add support for authentication through the device's browser.

Let me know if you have any questions.

Great news! We'll be testing this out in the coming weeks. Thanks so much 馃檹

I can't seem to find test card numbers for 3DS v1. I tried a few sources:

In this PR I see screenshots with buttons to choose between v1 and v2.

But I don't see this option when using 4000 0000 0000 3063

Could you kindly point me to a document I may be have missed?

Thanks! 馃檹

The screenshots in that PR are from the example app.

Are you setting a return_url value? Custom Tabs requires no return_url set in ConfirmPaymentIntentParams.

Yes we were indeed 馃槃

If I remove this return url, I now see custom tabs with 4000 0000 0000 3063

Thanks!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

odedharth picture odedharth  路  4Comments

ahqmrf picture ahqmrf  路  8Comments

danielgomezrico picture danielgomezrico  路  6Comments

andrewmunn picture andrewmunn  路  4Comments

tudorsirbu picture tudorsirbu  路  8Comments