infusionsoft / infusionsoft-php

PHP client library for the Infusionsoft API.
https://developer.infusionsoft.com/
Other
130 stars 128 forks source link

Public Wordpress Plugin: oAuth risks #272

Open infused-kim opened 3 years ago

infused-kim commented 3 years ago

Hi,

I would like to create a wordpress plugin that connects to the Infusionsoft API. But I am having issues figuring out how to authenticate securely, since the plugin code will be available in the wordpress directory.

Option 1: Embed my client_id and client_secret in the WP code

This is the easiest option, but it seems extremely risky, since anyone would be able to authenticate any Infusionsoft app and tie that connection to my developer account.

If they do something mischievous with them I have the risk that my developer account gets banned.

If my developer account gets banned, then all my plugin users' API connections will stop working and that would lead to data loss.

This is not just a huge risk for me, but a huge risk for every single user.

Option 2: Create a proxy server that handles client_secret and client_id securely

The second option is to make my wordpress plugin communicate with a proxy app that stores the client_id and client_secret on my server and simply returns the access and refresh token to the WP app for further use.

This was suggested in issue https://github.com/infusionsoft/infusionsoft-php/issues/83 a few years ago.

This is a little better, but opens up two problems:

  1. They still have access to API tokens tied to my developer accounts Even though they don't have the client_secret or client_id, they still have access to the tokens that are tied to my developer account. It's still a huge risk for me and my users.

  2. I now have access to the tokens and access to their Infusionsoft data Even though my users would self-host the plugin, they would essentially expose all the content of their Infusionsoft app to me. This is a big risk for them and a huge responsibility for me.

In this case it's simply a level of access that I, as the developer, don't need and users shouldn't have to give to me.

Option 3: I ask the users to create their own developer accounts and use their own client_id and client_secret

This is the most secure option for everyone involved...

But it's a usability nightmare. Having to ask non-technical users to create developer accounts and juggle several keys is going to make adoption a lot more difficult.

Option 4: Stick with the legacy SDK and API keys

My final option would be to stick with the old SDK and continue using legacy API keys.

This option is simple and secure for everyone involved.

But the risk here is that the SDK is not being developed anymore and may in the future stop working with newer PHP versions.

Infusionsoft may also decide to kill off that authentication method completely in the future.

Am I missing something?

Am I overestimating the risks here? Or am I missing something?

I have read many other issues here and Infusionsoft developers have dismissed API keys as unnecessary, but clearly there is a need for it.

Wordpress is the most popular web platform and not having an API authentication scheme that works with it is unreasonable for your users and frankly damaging to Infusionsoft.

I noticed that there are countless WP plugins that integrate Mailchimp, Active Campaign and other marketing software, but very, very few that integrate with Infusionsoft. And I haven't seen a single plugin that used oAuth. They all used the legacy API keys.

I think this may one of the reasons for this.

Ultimater commented 3 years ago

I don't see the risks here. If you're storing the api credentials in a PHP file, it's not accessible to the web. There may be slight risks if the servers gets misconfigured and doesn't understand PHP, so the source would splash out. Or if you store the credentials in a file type which the serves serves as plain/text. You can use .htaccess to block it from web access, but this only addresses Apache, then there's IIS: web.config, and there's nginx and others... Ideally with a PHP file, the config can sit safely in a PHP file provided PHP is enabled and not serving as plain/text. It's also possible to move such files away from web access, but then it's easy to lose such information if the someone migrates to a new server if it's in a place they didn't know to look. You can do things like have users specify the info as constants in their wp-config.php file. But then with OAuth2, this info changes. The older legacy API key which doesn't use OAuth2 would stay the same, but keep in mind the OAuth2 system works differently with throttling: https://developer.infusionsoft.com/faqs/ Also keep in mind the XMLRPC is marked as deprecated, although won't be going away for a while: https://developer.infusionsoft.com/developer-guide/ While the XMLRPC is more powerful in terms of having access to the table schema and doing tricks like pulling 1,000 contacts at the same time by specifying their ids in an array, the REST api does have a few things which the XMLRPC doesn't, such as specifying a list of the merchants. Should try to build your application with the REST api first, which means storing OAuth2 tokens, refresh tokens, updating tokens, etc. Can fallback to the XMLRPC using the same OAuth2 token. I wouldn't use legacy api keys, but legacy api keys are easier to use in that you're not constantly updating your tokens. In production we have applications that use a mix of all three approaches. Not ideal though...

For example https://memberium.com/ integrates with infusionsoft and stores a lot of stuff in the database.

Disclaimer: I don't work at Infusionsoft/Keap, just use the api extensively at work.

infused-kim commented 3 years ago

Thank you for your response @Ultimater.

The risk is in the fact that oAuth API tokens are tied to developer accounts. Depending on the use case it can be fine or a risk.

For example...

If you develop integrations in-house for your own business... it's totally fine

You have your own developer account and only you have access to the API tokens.

As you said, there is no risk here unless you misconfigure your server and allow the tokens to leak out.

If you have a SaaS-style business... it's also fine

For example, Zapier allows users to integrate with Infusionsoft. The authentication is done through Zapier's developer account and only Zapier has access to the tokens.

Only their systems use the tokens and they can limit abuse and control the number of API calls to reduce throttling.

But if you want to publish a wordpress plugin... you run into significant risks

For oAuth you need an Infusionsoft developer account and an app with a client_id and client_secret. Both of these are required for the initial authentication and the refresh of tokens.

And they should be secret, since they give people access to the Infusionsoft API through your developer account.

The problem is that wordpress plugins are by nature open source.

If you embed the client_id and client_secret in the code, anyone can create applications that pretend to be your application.

So, the currently recommended way, to solve it is to create a proxy application that runs on the developer's server, handles authentication and token refreshes and doesn't expose the client_id and client_secret in the plugin code.

But this creates two risks...

There is a popular web and order form builder plugin for wordpress that uses this proxy authentication method. Let's use that as an example for the risks it creates.

The risk for the plugin developer's business

Even though the client_id and client_secret are not exposed to end-users, the oAuth API tokens are.

And the API tokens are tied to the plugin developer's Infusionsoft dev account. The throttling limits are also linked to the developer accounts.

An attacker could extract the API keys from the wordpress database (since the plugin is running on the attacker's own web server) and then create a script that generates more than 1,500 API requests per minute.

This would be above the throttling limits and cause all API requests of legitimate plugin users to be throttled.

In this case it would lead to email addresses not being collected and orders not being placed. A huge issue for the users of the plugin.

The developer would have very limited options in fixing it. They could ask Infusionsoft to determine which Infusionsoft app is exceeding the limits and ban it. But the attacker could sign up for new accounts or even free sandbox accounts.

On top of that it could take days or weeks until Infusionsoft handles the problem. During this entire time the plugin developer would not be able to service their customers.

And the developer would have limited options to reach out to all his customers to help them with a workaround to the problem.

Needless to say the damage to the reputation of the developer could destroy the entire business.

The point is that since oAuth tokens are tied to Infusionsoft developer accounts they should be secret and not be exposed to untrusted parties.

This makes oAuth unsuitable for open source applications like wordpress.

The risk for plugin users

There is also an important risk for users of the plugins. One of the benefits of using wordpress and self-hosting an application is that you don't have to trust third parties. This is an important benefit and consideration that makes people choose wordpress over a SaaS.

I think a lot of people assume that since they are running the code on their own servers, it also means that no third party has access to their Infusionsoft account and data. And that's how it should be.

But if you use oAuth proxy authentication that's not the case. The authentication and token refresh are done on the developer's server. This gives them access to the access tokens, which gives them access to all the data in the Infusionsoft application.

This requires you to trust the developer, which may not be obvious to everyone.

A rogue developer could create a convenient and popular free plugin that many users install. Once they install it, the rogue developer could use the access to download customer lists and revenue data and try to sell it to competitors or blackmail the plugin user.

I understand that you run this risk whenever you allow someone to access your app through an API, but this is a risk Infusionsoft users are exposed to that is simply not necessary.

If we could continue using the legacy API key, the problem would be solved.

An even better solution would be if Infusionsoft users could generate an unlimited amount of API keys that can be revoked. Whenever you want to connect a new application, you generate a key and use it with the app.

When the app is not necessary anymore or you don't trust it, you just revoke the key.

This would make all server-side code significantly easier to maintain and solve all the risks I mentioned above for wordpress plugins.

I am not saying oAuth is bad. It has it's place. It's perfect to allow SaaS companies to provide integrations for Infusionsoft users. But it's not the right solution in all cases.

Ultimater commented 3 years ago

The problem is that wordpress plugins are by nature open source. If you embed the client_id and client_secret in the code, anyone can create applications that pretend to be your application.

The client_id and client_secret aren't enough by themselves to hijack someone's api. Need to also successfully log into infusionsoft to authenticate the application before the initial oauth2 access token and refresh token are generated.

My final option would be to stick with the old SDK and continue using legacy API keys. This option is simple and secure for everyone involved.

If anyone has your legacy API key, they can embed it in their own code. And they can sell it on the blackmarket until someone gets a hold of it and misuses it. At least with OAuth2 both the access token and refresh token would keep on changing and require constant access to the keys. It might be simpler without having to worry about storing an access token and a refresh, but in what manner do you think a legacy api key is more secure than oauth2?

If people will be installing your wordpress plugin, they will be running your code and need access to something... If you want to pay some extra bills, you can house that for people with AWS Lambda and RDS: https://aws.amazon.com/blogs/compute/introducing-the-new-serverless-lamp-stack/ Would you also run all infusionsoft api calls too in order to hide that away too and pay for that too, turning yourself into basically another zapier? That's a lot of work for a plugin... Whatever the case, you'll want to run code on people's servers and at least provide them some sort of access token.

Much easier to just trust people control their own servers. If their application gets hijacked, they can always take action afterwards, disabling the old credentials and use new credentials.

I don't see anything wrong with just storing it in the database. If you want to further protect the server, you can require a fairly recent wordpress version so you don't wind up with people on old wordpresses that are easy to compromise such as versions affected by xmlrpc bruteforce vulnerabilities.

Devs are privileged users and deserve access to such things on the server in order to work. If you need a temporary dev to work on an application which you don't fully trust, can create a separate application and oauth2 credentials for them during development. Then disable it when they are done and/or swap its credentials.

If people are running your plugin on an untrusted server, then the whole end-user's wordpress is already compromised...

The legacy api key is supposed to phase out at some point, but there's no date on it. Being on oauth2 helps future-proof you. Although the XMLRPC is also targeted. It's a bit early to move entirely to the rest api I would say. But if you do move to the rest api, then you can't use the legacy api key with it, it needs oauth2 tokens. Probably a good time to get into oauth2.

If OAuth2 isn't good enough for you due to your mistrust of the wordpress community, go ahead and create your own zapier clone and I suppose integrate with it from within your wordpress application...

Or just use something like memberium which already integrates with infusionsoft for your wordpress application... memberium does appear to use a legacy api key: https://memberium.com/videos/2-memberium-installation-wizard/

Maybe provide both options and go through the XMLRPC? idk... Up to you.

I'd personally go with OAuth2...

infused-kim commented 3 years ago

Thank you for your response, @Ultimater. It seems like there is no good solution to this.

The main issue I am trying to raise here is that the API authentication method that Infusionsoft provides is not suitable for wordpress plugins.

For years Infusionsoft insisted that oAuth is all that is needed, but this demonstrates very clearly that it is not enough and that there is a significant use case that requires another form of authentication.

My concern is not for the end-user's infusionsoft app or whether they can or cannot authenticate against other apps using my client_id and client_secret. That stuff is solid and secure.

My first concerns is that end-users can retrieve access tokens from wordpress that are tied to my Infusionsoft dev account. And a malicious user could use them to exceed the API throttling limits for ALL my plugin users. This means anyone (a hacker, a competitor) could make my plugin useless for all my users

It's a huge risk for my users since it could lead to data loss for them. Imagine I create an order form plugin, a competitor installs it and uses the API tokens tied to my dev account to exceed the throttling limits. It would cause all my customers order forms to stop working and could potentially create hundreds of thousands of dollars of damage.

It's a risk I don't want to expose my customers to. It's also a reputational risk I don't want expose myself too.

The fact that you suggest that I should proxy all requests and create my own Zapier is another sign that the API auth methods Infusionsoft provides are not sufficient. Nobody should have to do that just to create a Wordpress plugin.

There is no reason for a self-hosted wordpress plugin to give ANY access to the developer of the plugin. It shouldn't be necessary at all.

Users shouldn't have to entrust all their data to me just so that they can use a plugin that they host themselves.

The only reason it is necessary is that Infusionsoft doesn't provide an API authentication method that Infusionsoft users can use independently.

Ultimater commented 3 years ago

There's a discussion over here: https://community.infusionsoft.com/t/long-lived-token-support/49642

Proxies for Wordpress (and similar applications) This is a unique case in which embedding your client_id and client_secret is not secure. The source is usually available for anyone to see. Browser extensions fall into this category as well. We hope Personal Access Tokens will solve this problem. We are in the process of putting on the final touches and building out documentation. There will be a specification that will allow this to be pretty seamless for users.

Personal Access Tokens might be a slight step in the right direction. But not really a solution for this. I'd probably just put up a bunch of warnings regarding the risks of storing oauth2 tokens in worpdress, and to limit plugins to reputable sources, keep wordpress on a recent version and keep up with PCI Compliance security standards, and be done with it. "Use at your own risk" and some other legal stuff to guard you, and help the end-user understand the risks of storing oauth2 tokens in wordpress since other plugins can access it, or if the site gets compromised, etc. Maybe make some other suggestions like installing some sort of security plugin to help limit incidents. That's about the best you can do in wordpress.... then just get building...

infused-kim commented 3 years ago

I fully agree with the thread you linked to. And of course Infusionsoft promised that they have a solution that there is a solution "that they are putting the final touches on". And they said that in... 2018. Still nothing over 2 years later.

Thank you for your suggestions, but I think I still haven't explained the issue clearly enough.

it's not about the risk that other plugins could extract the token and download their Infusionsoft data. That's a whole other issue, but that's unrelated to Infusionsoft.

The issue is that if I offer proxy authentication, then I have access to their API tokens and I can get access to their data as a developer. Plugin users are now supposed to trust me. But I don't want that level of access, responsibility and trust. It should not be necessary for a Wordpress plugin in the first place.

They should be able to integrate with Infusionsoft without having to trust me.

But offering proxy authentication creates another risk. And it's a risk that affects both me and the end-users.

The API tokens they receive are tied to my Infusionsoft developer account. And all of my plugin users would be tied to the same infusionsoft developer account.

The API throttling limits of every plugin user are also tied to the same developer account and they are shared between all Infusionsoft instances that are linked to the dev account.

If one user authenticates with an Infusionsoft app, extracts the API access_token and then spams the API above throttling limits, EVERYONE'S API will stop working.

This means my users can lose data and my reputation can get damaged. Nobody is going to care that Infusionsoft's limited API auth options are at fault here.

All they will see is that they trusted my solution, it broke and I had no way to fix it in a timely manner.

There is no way to protect against this as far as I can see except asking users to sign up for their own developer account.

infused-kim commented 3 years ago

For anyone else wondering about this: I received a response on the Infusionsoft API forums.

Two new API auth methods will probably be released this quarter: https://community.infusionsoft.com/t/public-wordpress-plugin-oauth-risks/81864/2

MCKLtech commented 3 years ago

For what it's worth, this isn't exclusively an Infusionsoft problem. For all WordPress plugins, you either need a personal API Key (And most companies are moving away from this method) or a proxy. We've built proxies for a number of tools, which effectively handle the secret and keep it out of the public source code.