Here is a topic which, I think, everyone wants or needs to hear/read about, but few, if any, have touched on….
Security on M-Pesa and its APIs.
Am pretty sure most owners of Safaricom Shortcodes want to know how to secure their M-Pesa accounts and Internal Systems against malicious access or API calls by either insiders or external forces. And am pretty sure there are cases of organisations losing money to some scrupulous insiders, developers or tech-savvy people via API access e.g. peeps paying themselves or transferring cash to weird numbers using illegal B2C API calls, people transferring cash via B2B to their bank accounts, or people listening on C2B callbacks for shortcodes they do not own or even sending their own C2B validation/confirmation requests to a 3rd party system for malicious purposes. It is actually very easy to lose millions in a single moment if there is no thought paid to security on your systems.
This document will be a living document, changing to keep up with new ideas and feedback from fellow developers, shortcode owners and other parties. It shall seek to provide guidance as to the ways in which one can protect their systems, APIs and M-Pesa accounts from fraudsters and other possible malicious parties using existing API capabilities.
This article is not an expert’s opinion on how to achieve security on your system, for that, consult a Security Expert. And neither is it the one-stop-shop for all security issues one might face on their systems. The article will not go over common sense issues such as using SSL on your system. FYI if your system is not using SSL for all API requests, I don’t know what the heck you could be looking for here, you should be on Stack Overflow, somewhere else or even Google
This is simply a guide specific to M-Pesa showing the techniques one can use to secure their systems against those who would want to use M-Pesa to gain an unfair “monetary” advantage for themselves.
The document will be split into 3+ sections, covering security from the M-Pesa portal itself, to the developer side from the 3rd party’s perspective, down to the APIs themselves. Each section will have points showing some practises you, as the developer or the shortcode owner, can adopt on your end, whether you are tech-savvy or not, that can assist in keeping your virtual cash safe. The sections will probably be added to as more info comes in
Lastly, the article will probably be veeery long… and will continue growing 🙂
M-Pesa Portal
Charity begins at home… or so they say. So same applies to Security. It all starts with the Baba Yao (parent) of all those systems and how you work with it: the M-Pesa Portal
- For the Business Administrator, know the power you wield over every operator on your system. You can add and disable operators at will, reset their passwords, change or remove their roles, and generally have SU-like powers over them. You are the first gateway into the security realm on M-Pesa for your organisation.
- Before login into the M-Pesa portal (for all web operator roles), you have to apply for and receive a certificate to log into the site. That certificate is quite troublesome to move to another machine, if at all possible. Use that as a first line of defence to limit access to your Portal. Only apply for and install the certificate on a specific machine which you have full control over. For Business Administrators, only ONE person should ever have access to their credentials and machine. If anyone else touches the Business Admin’s machine, then there’s bound to be issues, which grow with the size of the organisation.
- For every user the Business Admin creates, they should aim to only have a single human being per Operator and if possible, to track the machine on which their respective certificates are installed, and keep a close eye on each. M-Pesa has great audit logs, which, combined with access control to the machines, can assist to track exactly who did what on the portal and where they were.
- To learn how to create Operators, use this document.
- Know the difference between a web operator and an API operator. a web operator is created to only access M-Pesa via the portal interface. An API operator is designed to only access M-Pesa via API calls. There is a reason for such a design.
- You should NOT assign permissions designed for an API operator to a WEB operator. This opens up the system to quite a sizeable can of worms. A single operator type will hardly perform any risky stuff on the API, but combined, they can do wonders (believe me). An example: imagine a single Business Manager (web operator) with API permissions overseeing an organisation’s finances. Lets first assume he’s on the good guys side. He can view all transactions, check all reversals initiated, and even see all outgoing B2C payments (for B2C accounts) or incoming C2B payments. Now let’s add some well known human greed, and let the shortcode be a B2C one. The guy’s operator also has API capabilities, thus employs a developer to make a secret separate system that can make API requests, using his credentials. Now, the guy begins sending separate 1 shilling requests for every transaction that goes out via B2C (stupid idea, but still shows my point). Assume the company has like 1,000 transactions per day. If that guy has the only visibility of the M-Pesa account in the organisation, he just earned himself a cool 1K just by sitting on his desk. If he’s even more daring, he could simply join up with random guys on the outside, add them to his “payroll”, and use the same technique, though now using bigger amounts and describing them to be the same as the legal payouts. Outside, the other guys simply give him his cut. Using the same 1000 transactions, each being given out at least 10 bob, and using like 20 separate guys, each with at least one active M-Pesa SIM card (probably opened using fake ID), that translates to around 1000*10*20 = KES. 200K. That means each guy gets KES. 10K, each within the M-Pesa limits, and depends on how good the guy’s network is. If the Business Manager gets 40% of each, he takes home 4K multiplied by the number of “employees” he recruited. He doesn’t even need to be in the company to do such. He can simply set up such a system, find an ideal time to leave the company, ensuring the credentials remain the same, and initiate the same plan when outside, leaving the blame to whoever will be running the show on the inside. And when outside, he can actually increase the per-person share to KES. 30… 50… 100… you get the point. Now imagine some of that innovativeness we have been seeing around (e.g. people digging underground tunnels to banks) applied to such a situation and tell me what that person cannot do. Basically, let API operators remain API operators, and the same to apply to Web Operators.
- If you can, employ the maker-checker capability. This is an M-Pesa capability that allows one web operator to only initiate a transaction, and another separate operator on the same or higher level to complete the transaction. This is used for mainly reversal operations, and ensures no single person has power over the whole transaction. All parties need to collude to actually pull anything off. But if you have API permissions, that bet is off the table.
- Business Admins, always check on the web operators regularly, and disable operators whose users are not around or are on leave. Someone out of office should not be allowed to still access the system yet they are supposed to be enjoying themselves elsewhere. Let them be enabled when they come back.
- For integrations, I would suggest creating specific operators for that, and another set of operators for the live system. This is going to be explained further in the next section
Developers
So, you got a business, and want people to pay you. Cash is one way, the other great option is M-Pesa, since almost everyone is on it. Then M-Pesa guys just give you a SIM card and tell you that you shall receive notifications via SMS. But your business is an online business.
They then inform you they got an API for automated notifications. Great, you can now automate your payments! But wait, you are not a software developer :/. So now you need to get a developer to work on your system.
Now the developer needs access to M-Pesa to integrate into your systems. Safaricom has provided a Sandbox to test with, that’s great! But sooner or later, the developer will need to integrate the live API into your system too. And that means somehow, they shall need live credentials. And that get’s your mental gears rolling… If they get access to your system, will they use it to pay themselves if I don’t pay them on time? Will they use the access to make their own little vampire system to suck the money out as it comes in (especially those who use a C2B/B2C combined shortcode)? Will a third party API provider like Proxy API use those credentials to pay themselves on the side? How will I know when something weird is happening on the system? Can I stop them if so?
Well, hopefully, following as many of the below tips as you can, you could be well into making your system’s M-Pesa security manageable, if not top notch.
- First, read the previous section if you haven’t, then come back here
- Next, for business owners and the non-tech-savvy team, in software engineering, we have a concept called Version Control, implemented by a Version Control System, VCS in short. It is basically a system that manages changes to pretty much any collection of information, but we shall focus on Software collections for this tutorial. That means you can use the system to manage multiple versions/copies of the software, its documentation and configuration. This, I believe, should be your first step: integrating VCS capability into your system during development. It should be one of the foremost goals, extending past security and going into the realm of disaster recovery and multi-version software management. From a security perspective, the single most important use for VCS is separation of configurations from actual source code. That means you can create a system which picks its secrets and credentials to use on a specific system from a separate file or location within the same server/node, or even a geographically separate server e.g. a developer can create code for you from Nairobi, test using their local machine’s Database, simply host or “push” the code on a shared server, ping you in Mandera, you fetch or “pull” the version that is selected to run, set the location in the config file to Mandera’s live DB (even better, no need to change it if you already had the service up and running), and run immediately, with no fuss. This centralization, combined with package management systems, minimizes and even in some cases eliminates those “it was working on my machine!” scenarios. Examples of popular VCS systems you can check on include Git, Mercurial and Subversion. From the above, first suggestion would be: design your system to have its configurations separate from its source code. The configurations will include anything from database URLs, usernames and passwords, API endpoints (because these can change e.g. moving from sandbox to staging then to prod), and API credentials e.g. consumer keys and secrets. Let there be a separate file on the system or separate system with an extremely low latency, which shall hold all those credentials. Then ensure only the minimum access to those resources e.g. only apply the 0600 permissions octal for all configuration files, and then set them outside the publicly available base folder. This will ensure that only the user with whose role the system is accessed can read the file, and no one else, except the SU. If on a separate system, lock down on network access to that machine holding credentials (more on that later). And that assumes the root user for Linux/Mac, or Administrator for Windows, is actually well protected too. Otherwise all the above is for naught. This will ensure that when code is being migrated between environments, there are no secrets hard-coded into the system, which can be used as weak points to bypass authentication or authorization, or be used to launch unknown transactions without the owner’s knowledge. It also ensures that any code being moved only carries its generic/universal logic to perform whatever it needs to do, and only fetches environment-specific variables from the external resource. Here’s an example of how to set up such on Linux. Also, if you can afford it, consider using Docker for your services, it comes with an inbuilt credentials management system that can be incredibly useful for such situations.
- So, your developer has managed to separate code from config, then what next? When integrating into M-Pesa APIs, a developer is first assigned test credentials to test their system with. This enables then to test out their setup and credential encryption/encoding/hashing schemes. Based on that, a developer should not be allowed to use static credentials on the system. The developer should learn to apply whatever encryptions/encoding/hashing/other actions necessary on the credentials and set up the system to always apply them dynamically during runtime. For. Every. Request! This enables the Administrator to change their system or operator credentials anytime without worrying about system breakdowns when old static credentials are still being used in the code, necessitating another “deployment”. This also means the codebase can be changed anytime without worrying about credentials to external systems. Static credentials also means the developer will most probably leave having set up the live credentials, and leave the project or company with that knowledge in mind. That also means there is a high risk the same developer can use those same credentials to make API requests on their own via another system, especially on e.g. Daraja, where any IP address is technically allowed to make API calls as long as you got the right Consumer Secrets and Credentials, and M-Pesa Operator credentials.
- Then since the developer has access to test credentials, the developer should not be allowed to use live credentials to test/integrate the system. A good developer should understand how the credentials are being applied, and develop the system to allow a “plug-in” kind of scenario for environment variables and secrets, meaning if the Admin decides to test on sandbox, the config location is simply modified to point to sandbox credentials, and when done, a quick pointer back to production credentials will be done, and the system should continue purring along, being none the wiser as to what just happened.
Here’s an example I use on Proxy API. I have 3 environments: “local” for a developer’s local machine, mainly for innovating, inventing, adding and testing new stuff, “staging” for an environment pointing to production, but only accessible by a limited set of people to test new changes on production; and the “production” environment itself. So, for each environment, I have an .env file, which carries all possible variables that change in between environments. These include API and Application URLs (including callback URLs), DB usernames, keyfile locations and passwords, and API credentials. A sample .env config file section is as shown below for PHP peeps on Laravel or SlimPHP (of course the credentials are fake :P):
... //other configs
//example on how one can set the base URL for staging and local dev.
//Uncomment only one
BASE_URL="https://staging.example.co.ke"
//BASE_URL="http://localhost"
API_VIEWS_PATH="/path/to/views/for/current/environment"
API_LOGS_PATH="/path/to/logs/"
API_OPENSSL_ID_KEY="opensslkey"
API_OPENSSL_KEY_FILE="/path/to/keys.csv"
API_OPENSSL_CIPHER="aes-256-gcm"
HTTP_VERSION="1.1"
RESPONSE_SIZE=4096
OUTPUT_BUFFERING="append"
DETERMINE_ROUTE_BEFORE_MIDDLEWARE=1
DISPLAY_ERROR_DETAILS=1
CONTENT_LENGTH_HEADER=0
ROUTER_CACHE_FILE=0
// DB for transactions and transaction processing
DB_HOST="example.co.ke"
DB_PORT=5456
DB_USERNAME="username"
DB_PASSWORD="password"
DB_NAME="staging"
// DB for sessions and logs
METADATA_DB_HOST="staging.example.co.ke"
METADATA_DB_PORT=5455
METADATA_DB_USERNAME="meta_username"
METADATA_DB_PASSWORD="meta_password"
METADATA_DB_NAME="metadata"
GOOGLE_CLIENT_ID="clientid.apps.googleusercontent.com"
GOOGLE_PROJECT_ID="google-project-id" GOOGLE_PUBSUB_LOGGER_TOPIC="my/topic"
With such a config above, I next use a framework that allows one to fetch external configs, in my case SlimPHP (also applicable to application.yml files + Environment variables for Spring Boot, dockerfiles + docker secrets for Docker e.t.c.), and load that config file into memory. Note: since almost all web servers or application servers have a base folder from which they serve a site’s or apps files e.g. public_html or www/html for well known PHP sites, the above file needs to be kept in a location outside that base folder and assigned permissions to only allow the user whose profile or role is used to access the site, to read the file (remember the 0600 octal?). That means, in case of a site breach not involving the SU user, only the files under the base folder will be affected, and any file outside the base folder will not be touched. That assumes file permissions are also well set in that host, a topic outside the scope of this article. With such a setup, a developer can create copies of their own local version of the above config file, have them given to the respective system admins of each environment, and let the Admins configure them appropriately according to the environment. Then you can simply have your code being pushed into a VCS repo, pulled into the respective environment, and simply continue running if they allow, or restart the service if configs are loaded into memory. That also means, the Admin can set their own credentials without the external developer knowing about them, and can change them anytime they wish.
Back to the points:
- If you really need to give the developer access to live credentials, I would suggest the following: since the aim of integrations is to test the credentials work as expected, and that the operator can actually make API requests, create a separate API operator specifically for integrations, and assign them non-transactional permissions e.g. let the operator have transaction query or account balance permissions only. With that, you as the owner/Admin can learn how the credentials are created, how they fit in with the developer Application being created, and confirm the operator credentials are being encrypted/encoded properly by actual API requests to prod. And when the need arises to use transactional API requests, you can have the developer use the above external config example to create a separate variables file with the necessary configs, show you or an Admin how to use them, and then set them personally after creating the operator specifically to perform transactional API requests. That leaves only operators who cannot perform any transactions on the outside, and whom can be disabled anytime, especially on Going Live, since even data leakage from one’s systems is not something anyone wants.
- After a system or application has been created, check the logging framework. Ensure no logs expose any secrets or sensitive information. A developer could have placed a simple “echo” to file to confirm what password is actually being passed into the system, and forget to remove it (either on purpose or just by mistake). With those logs visible to everyone with access to log processing systems, it will be quite easy to get an API operator’s credentials and use them as one wishes.
- Then, once a system is going or has gone live, and you have ensured everything works, prepare to fence the system against all external network attacks. A malicious developer could leave a service listening on a specific port in your Application, and if that App is not firewalled off, the developer could trigger API requests from remote locations to your app, and cause your app to fire off its own API requests to MPesa or other systems. This also means there is a probability a developer could have left a backdoor into your system to get in any time they wish. Once Go Live process is initiated, ensure that all network requests to the Application server to initiate API requests only come from machines within your intranet, or only from specific machines within the intranet. Also, for callbacks, ensure only a subset of machines are allowed to receive the callbacks via network segmentation and Firewall rules. This ensures no callbacks leave the Intranet in case the App is sent to proxy requests or leak some information to an offsite location, and only specific, well known and planned for nodes/servers receive the callbacks.
- After Go Live, ensure that all API operators used to integrate the system and Go Live are disabled, and apply the same for web operators if you can, since one can easily create new operators for the same. Then, assuming you used the above techniques, create a new set of credentials to use for production, apply it to your prod environment, firewall off unwanted access, and you are good to go.
Hopefully, using the above in combination with what your security experts suggest will provide a good first defence against unwanted access to the API.
API Requests
“How to secure API calls”. Quite a number of questions arise from that single phrase. How do I know it is Safaricom calling my endpoint…? How do I confirm that the callback’s data has not been tampered with…? How do I detect that API calls leaving my App or System are legit…? How do I know that the callbacks am getting are actually a result of my system sending valid requests…? and so on and so forth
Well, as of now, the current version of Daraja has limitations on how one can protect themselves from such. I shall put here suggestions which shall use the current iteration of Daraja and Proxy API to try and protect oneself from malicious external API tampering.First, from within your own system:
First, from within your own system:
- This is an idea supported by developers also working on securing their own systems from external attacks: for all 3rd party initiated requests, all requests should have a custom parameter appended to the request, whether in the body or in the callback URL, and, if possible, coming from a separate system other than the one actually making the API call to MPesa. E.g. in a financial system, the API developed to make API calls to MPesa should not be the same generating that custom parameter, it should be the financial system passing in the parameter to the API calling system. This will enable the following:
- Tracking which requests are actually valid, such that one can check the requests received by M-Pesa against the requests made by the API. This can be done using the Remarks parameter or using a query parameter in the callback URL e.g. https://example.com?uniqueid=fwkjefwizxkn. This enables all callbacks to have a reference to the initial request which can be tracked automatically on the callbacks
- Confirmation that the callback actually came from Safaricom by checking the request parameter or query parameter against valid parameters recorded before the request was made. Unless you are sending plain HTTP requests, or have systems in between that compromise the integrity of your requests (e.g. MITM scenarios) , you are sure the only two parties with access to that parameter are your system and M-Pesa.
- Proxy API allows the third party to actually have reference data as part of their requests, which are appended to the MPesa request as ReferenceData parameters in the final request to M-Pesa (well familiar to those using XML on Broker). These are also passed back to the user on the callback, allowing them to actually trace the origin or the characteristics of the initial request.
- Proxy API also allows the 3rd party to initiate requests using their own request IDs, thus, using the idea given above, one can be sure there shall be no replay of API requests to MPesa, as the same request ID from the third party will NOT be accepted by MPesa again. This should be applied with the suggestion to have a separate system creating the request IDs to enable end-to-end tracking of API requests for the organization.
- For C2B transactions, the main way available to confirm that the requests actually came from MPesa are limiting the callback URLs to only be accessible from Safaricom IP addresses. One can try to request them from the API support team, and place rules in their firewalls or web server configs to only allow callbacks from those specific IP addresses. But of course it does not protect against IP address spoofing, so put extra security measures to detect/prevent such.
A simple example on how to limit a URL to specific IP addresses using .htaccess is given below:
RewriteEngine on
RewriteCond %{REMOTE_ADDR} !123\.45\.67\.[8-9]
RewriteRule ^/transactions/c2b/callback - [F]
The second line is a regex that will match any incoming request from a host with an IP address outside the IP address range 123.46.67.8 – 123.46.67.9, and the third line is a command stating that any IP address matching the previous line trying to access the resource(s) at /transactions/c2b/callback should have their request denied. That can be used to limit the IP addresses accessing your callback URLs to a specific range. An even simpler config to achieve the same as above for latest Apache servers (>= v2.4) would be:
<RequireAll>
Require ip
123.45.67.8Require ip
123.45.67.8Require all denied
</RequireAll>
That can be placed in the specific path or folder /transactions/c2b/callback. I believe it’s self explanatory, but is much wider and needs some testing of course. You can Google how to add path specifiers to it.
- Once that is done, the Admin is advised put up an audit system that checks regularly the number of transactional API requests sent on their system vs the actual number and nature of transactions present on M-Pesa. This can be done by integrating into the SFTP dumps program which receives a file with a collection of all transactions made to or from the shortcode, or using the dump to CSV capability already available on the M-Pesa portal, and using those dumps to cross check against all API requests sent by the API application. This can actually be automated if the system is well designed. This will give them the capability to audit their system anytime and confirm no malicious requests were made nor invalid API calls received by the callbacks.
- Proxy API will soon implement signed C2B callbacks, where each request sent by the Proxy can be verified against a signature prepared by pre-shared credentials. Due to the high CPU usage nature of cryptographic operations, we are looking for the best way to enable fast verification of callbacks without wastage of node/server resources.
Using most, if not all, of the above tips, we hope you have all the information you might need to create a highly secure MPesa payments system. We also invite you to check out Proxy API and view the array of services available on it that make your work easier. The above document will change frequently depending on reader and developer feedback, so make sure to check in once in a while.
For all M-Pesa APIs consultation, contact Peter via consult@peternjeru.co.ke