Tipsi-stripe: PCI DSS Compliance

Created on 17 Oct 2017  路  33Comments  路  Source: tipsi/tipsi-stripe

Since the messages between JavaScriptCore thread and native threads can easily be intercepted and read, I was wondering if this library is compliant to PCI DSS Requirements 6.3-6.5 regarding the development of secure applications.

Most helpful comment

Hi,
I understand this is old thread but great information on tipsi stripe for React Native and PCI compliance.
We are trying to use tipsi-stripe to integrate with Stripe and had same question. Whether to use 1) paymentRequestWithCardForm or 2) PaymentCardTextField component and pass card information to createTokenWithCard . We want to be PCI compliance with SAQ-A.

In this whole conversation I have noticed that everyone was inclined toward approach #1 which is to use paymentRequestWithCardForm. However, we reviewed Tipsi code for paymentRequestWithCardForm and its doing the same as approach #2 which is read card information in plain text and calling Stripe api to get token
https://github.com/tipsi/tipsi-stripe/blob/master/android/src/main/java/com/gettipsi/stripe/dialog/AddCardDialogFragment.java#L192

Now, since thread has been almost few years old, I am looking forward feedback from your past experience if using option #2 is viable option for PCI compliance. We are more interested to use option #2 because it gives us more flexibility and good integration with our application flow.

Look forward to hear your experience.

All 33 comments

@alibarisoztekin would you like to help us with it?

@isnifer Company I'm working at is currently in talks with Stripe to concretize requirements and feasibility for a compliant react native implementation of their mobile SDKs. I can keep you posted, and help out once a roadmap is set.

hey @alibarisoztekin
could you please give more details about:

messages between JavaScriptCore thread and native threads can easily be intercepted and read

I'm not sure what do you mean here.

hey @cybergrind, I'd be happy to
React Native architecture relies on RCTBridge to serialize and deserialize JS to Obj-C messages.
During runtime there are several ways, this bridge can be observed. The easiest way is using JWTs, which are already used by Expo, Chrome Dev tools etc...
The harder way is if someone gets a hold of the .ipa and decompiles it, unlikely but possible (and not PCI compliant).
This means any native code that is exposed to React Native, in this case Stripe native UI properties, are considered vulnerable to attack. Hence, Stripe requires additional requirements on top of SAQ A if any of the SDK UI is exposed. More on security of Stripe here.

@alibarisoztekin

1st variant requires SPY_FLAG enabled

React Native comes out of the box with a special SPY_MODE flag that you can turn on in its MessageQueue component

So it seems that you can disable this flag for your production builds

2nd you cannot get credit card info or secret key by decompiling application, you can only get public stripe key, but it isn't secret and you can get it without problem in browser too.

So I'm not definitely sure what is the vector of attack: you need to modify an application to be able to steal anything or you need root level access to the device, but with this level of access you can just install a fake application and get everything quite easy, without bridges.

Hello @cybergrind Have you shared your library with the team at Stripe? It would be good to know that Stripe is aware of your RN library. I share the concern of the user above. PCI Compliance is so sensitive, I would want to make sure that our team is doing everything correctly. With Stripe.js, we know we are good. With any SDK listed on Stripe's site, we know we are good. Are there any plans to get this reviewed by Stripe's team so that you can be included on this page: https://stripe.com/docs/libraries#third-party

@jterps08 hey. No, we haven't done it yet. We will do that.

As far as I can see from your link, sending the link doesn't mean that Stripe's team will do 'PCI Compliance certification' or anything like that. So you shouldn't consider these libs as 'PCI compliant' just because they're on this page.

I've tried to understand what is PCI DSS about and it seems that if you don't store credit card information yourself (we don't need that because Stripe is doing everything themselves) and don't use not encrypted network connection - you're ok. We're just calling underlying stripe sdk with native calls and it must be as compatible with PCI and that sdk is.

Ok I believe no one has more things to discuss

To qualify for the most simplest compliant level (SAQ-A), the app has to check all the boxes from the screenshot below I believe.
This means you have to use Stripe Checkout & Element, or Stripe Mobile SDK.
screen shot 2018-06-26 at 3 29 59 pm

With tipsi-stripe, card info are transmitted within the app in plain text, so I don't think it qualifies for SAQ-A.
Even if you don't store the info, the app is PCI compliant, but you have to fill in a MUCH more complicated form with Stripe which can be like 90 pages long (SAQ-D or SAQ-CVT, etc.)
See here: https://stripe.com/docs/security#pci-dss-guidelines

But this is just my deduction after suffering thru reading all the PCI docs Stripe provided, I yet have to find an official source of truth for all this, so anyone with more accurate answers please correct me if I'm wrong.

@isnifer @cybergrind, do you guys have any definitive answers to what level of PCI compliance tipsi lib really entails and whether you have any plans to partner with Stripe for better compliance?

@shan-du hi. Not so easy question here =)

Somehow you have to pass cardholder info from your application to SDK API and honestly, I don't see any possibility to not pass this info, because Stripe SDK requires you do that. I read that as I've described above: you shouldn't intentionally store, send or log private information, so there should be no way to steal this information. If you have direct access to the device or its memory - you can steal this information quite easy on each level of processing - memory, network layer, any kind of code injection. So it isn't not library's problem how to protect information on system's level.

If you don't think that this meets SAQ-A requirements - you can write your own component for CC inputs and pass this information directly to the SDK, this cannot be more secure than that. The only question is Stripe SDK SAQ-A compliant itself or not because you should pass data as non-encrypted and so on. I believe tipsi-stripe should have the same level of compliance as Stripe SDK (if there are no security bugs).

@shan-du so it looks like somehow tipsi-stripe should meet PCI DSS 6.3-6.5 and I can see only common recommendations how to test it, but I'm not sure how this compliance should be confirmed (exact procedure, who is a responsible person for that). At least you are free to do all tests from 6.3-6.5 points yourself because it is open-sourced library.

Hi @cybergrind !! w000t, thank you for the super fast response!! 鈿★笍

I think your full modal implementation from tipsi is OK because all card info is black-boxed within the tipsi component, our app only gets back the non-sensitive info like Stripe token upon submission.

The inline form however (i.e. <PaymentCardTextField>) passes back all the card info in the onParamsChange method, this is where the PCI compliance fails for us due to the card info being made visible within our app.

It seems that this was OK before, as long as apps don't store card info, but I think the PCI rules have changed recently and just not storing card info is no longer enough to be compliant. 馃槥
Maybe <PaymentCardTextField> component can be deprecated eventually or updated so that card info can be black-boxed.

In the end, we have to give up custom styling & use the full modal implementation because of this, but it's OK, better that than to deal with compliance issues. Hope that makes sense!

Thank you again though!

@shan-du that makes sense never thought about it in such way.

Maybe in that way, we cannot provide enough isolation in js environment: devs still have a possibility to mock/hook/intercept any function call without much pain. And I'm not sure if this can be prevented with native code on all platforms, I can imagine full blackboxed native approach when you just use forms provided within stripe SDK and only callbacks with results were provided back.

So, in terms of usage: devs still able to intercept private credentials and I'm not sure how to prevent it totally (eg. they can make and use their own clone of stripe-like forms + stripe SDK call that accepts this credentials) so you will need to examine resulting implementation anyway.

@cybergrind or maybe just make the full modal component a bit more customizable so more people can use it.
Like the style & text for cancel & done buttons and the style of the form itself, not sure if it's an easy change on your end though.

Hi,
On the Stripe Dashboard I have this message :

Once you've processed around 20 live transactions we'll review your integration patterns and determine what PCI validation documentation you'll need to use.

Do anyone of you know what will happen after those 20 live transactions ?
I have built an App using tipsi-stripe for card tokenization and I only have about 5 live transactions...

In their docs they still use variables
https://stripe.com/docs/mobile/android

The thing about android and iOS is that the apps are sandboxed, in different ways but you can't intercept data between different apps, except probably if you are a root user in android. In that case you can just avoid processing the card if the phone is rooted or state in the legal agreement that you are not responsible of what ever happens if the app is used in a rooted phone.

So as long as you don't save the card info in a file or something crazy like that you should be fine.

I know some people think android apps are not sandboxed but here is the proof:

https://source.android.com/security/app-sandbox

I am not an expert but I guess this make it PCI compliant. If I am wrong please let me and everyone know. This is a very important topic that should be clarified before anyone use this library.

Also, knowing that the native and non native code runs in a sandbox there is no need to create special fields to black box the data. It is even possible to use any field as long as the sensitive data is not written in an file or sent over the network... This makes everything easier and reduces the effort to maintain this library while giving more possibilities for developers to customize the input field etc. If I end up being right I guess it would be a good idea to clarify this topic in the README.

Well I have good news, I'm way past 20 orders and I can tell you that stripe has considered it like if it was native iOS SDK. So this is the best level of PCI compliance and I have almost nothing to do.
Enjoy the library

Hi @WeberJulian! That's great news!

May I ask which of the tipsi-stripe methods you used for collecting and tokenizing card information?

https://tipsi.github.io/tipsi-stripe/docs/paymentrequestwithcardform.html

or

https://tipsi.github.io/tipsi-stripe/docs/createtokenwithcard.html

It seems like the second one only handles tokenization and not collection. If we use the second example, will not be eligible for the "Pre-filled SAQ A" label of PCI compliance from Stripe?

Hi @b-mcrae,
I used paymentRequestWithCardForm through the ExpoKit, but it should be the same.

I don't know about the other one but since you are in possession of the sensible information, you could very well send it somewhere before the tokenization and it does not sound very PCI complient to me.

If you only care about being eligible for the "Pre-filled SAQ A" from Stripe, try to see if the same card generate different tokens from the two methods. If not, I don't think Stripe has any means of knowing witch method you use for the tokenization and you should be ok.

Hi @WeberJulian! Thank you for fast reply! You rock!

I tried inputting the same card information into both of the methods, and it generates two different tokens. But, if you input the same card information into the same method twice, you also generate two different tokens.

I believe the difference in the two methods is the collection of the card information, rather than the tokenization.

It seems safer to allow Stripe to handle both collection and tokenization using https://tipsi.github.io/tipsi-stripe/docs/paymentrequestwithcardform.html. Thank you!

Hi Guys, (@b-mcrae, @WeberJulian @adrianmrit)

I'm trying to understand the limitations of this library for my use case.

Initially I have a UI that uses the <PaymentCardTextField /> to accept user input. And to request a token I use the data that the component sends back via the onParamsChange prop (this exposes the credit card values to my app). From all the reading i've done here in this repo and online, this practice does not seem to be PCI compliant.

Is it then the case the the only way to use this library and be PCI compliant is to simply fire off the paymentRequestWithCardForm() function call which brings up the native form?

The reason I would be interested in using the <PaymentCardTextField /> component (so long I am PCI compliant) is so I can have more control over the UI. However I do not see a clear way for us to use this component and be PCI compliant as the component exposes the CC info and the only other way I see to retrieve a token is to use createTokenWithCard() which requires my app to have knowledge of CC info which (to my understanding) is no longer a PCI compliant practice.

Cheers.

The apps in Android and iOS are sandboxed which means no external app can acces that info in memory. As long as you don't send the raw credit card info over the network or store it in the phone (because other app could then access that file) then it's PCI compliant.

By the way. You don't actually need the <PaymentCardTextField /> since again, the apps are sandboxed so the value on any field can't be accessed from other app. That said, there is no way your app can't be exposed to the credit card info. It is your responsaibility to not store or send it over the network (of course without using the library).

@adrianmrit can you clarify for me if using the PaymentCardTextField component with the onParamsChange prop which gives the card number, expiry month and year and cvc, NOT storing that in app state BUT constructing a request with that information and sending that information via the createTokenWithCard({ number, expMonth, expYear, cvc }) function from tipsi to retrieve the token satisfies what you have stated.

I didn't end using this library but it sounds good to me. createTokenWithCard will return a token and not the credit card info. That token can then be sent to stripe to process the payment. Thus I believe it doesn't really matter if you store that token or if you send it somewhere else, it should work only once and shouldn't work for someone who doesn't have the secret token. As @b-mcrae said inputting the same card info two times will create two different tokens, thus I don't think the tokens can be reused.

I guess my point im trying to make is that using createTokenWithCard requires the app to have knowledge of the details of the card in order to make that request. I thought this was not PCI compliant hence why paymentRequestWithCardForm should really be the default use IMO

Hi @vTrip! I agree with you and shared the same concern. If you use createTokenWithCard, then it seems like you're still collecting data, which does not seem PCI compliant. Therefore, it feels safer to go with paymentRequestWithCardForm() where both the collection and tokenization are handled by Stripe.

We also wanted more control of the UI, but in the tradeoff between PCI compliance vs UI, we chose PCI compliance. FWIW, paymentRequestWithCardForm() does yield the SAQ A.

Hey @b-mcrae and @adrianmrit thanks so much so the prompt replys within the last 24 hours.

@b-mcrae could you clarify what you mean by "FWIW, paymentRequestWithCardForm() does yield the SAQ A."

@vTrip sure thing!

Regarding PCI Compliance, you can read more here: https://stripe.com/docs/security#validating-pci-compliance

Depending on which Stripe methods you use, the SAQ (Self-Assessment Questionnaire) you have to submit to Stripe on an annual basis to validate PCI compliance differs. For example, if you are calling the Stripe API directly, then you have to submit an SAQ D. If you use tipsi-stripe as a wrapper around the Stripe Mobile SDK's, then Stripe can help generate an SAQ A for you, which is much less work for you compared to the SAQ D.

My question initially with this module was what SAQ we would have to submit to Stripe if we used it. It likely varies depending on the method you use (ie. createTokenWithCard or paymentRequestWithCardForm()). I'm just letting you know what a possible outcome of paymentRequestWithCardForm() is if you have the same question.

@b-mcrae Awesome!

So I can be confident that paymentRequestWithCardForm classifies me for SAQ-A with Stripe?

Is that correct?

@vTrip yes! i believe so, as this is what happened in our case with using paymentRequestWithCardForm!

Hi,
I understand this is old thread but great information on tipsi stripe for React Native and PCI compliance.
We are trying to use tipsi-stripe to integrate with Stripe and had same question. Whether to use 1) paymentRequestWithCardForm or 2) PaymentCardTextField component and pass card information to createTokenWithCard . We want to be PCI compliance with SAQ-A.

In this whole conversation I have noticed that everyone was inclined toward approach #1 which is to use paymentRequestWithCardForm. However, we reviewed Tipsi code for paymentRequestWithCardForm and its doing the same as approach #2 which is read card information in plain text and calling Stripe api to get token
https://github.com/tipsi/tipsi-stripe/blob/master/android/src/main/java/com/gettipsi/stripe/dialog/AddCardDialogFragment.java#L192

Now, since thread has been almost few years old, I am looking forward feedback from your past experience if using option #2 is viable option for PCI compliance. We are more interested to use option #2 because it gives us more flexibility and good integration with our application flow.

Look forward to hear your experience.

Hey @WeberJulian, you mentioned using tipsi-stripe through ExpoKit. Fast forward a few years I am using the Payments module from ExpoKit that lives here: https://docs.expo.io/versions/v37.0.0/sdk/payments/. They do mention that the module is based off of tipsi-stripe but I don't know if this is different than how you were/are using it. Any thoughts on this?

I am using this module, specifically only apple pay and google pay, in my app and trying to do my due diligence to make sure I am compliant with the pci rules.

Was this page helpful?
0 / 5 - 0 ratings