Keeping your data secure - ensuring you stay its sole owner and reader - is a top priority for us at uFincs. This document will explain exactly how we make it happen.
Note that we're going to get a little technical here, but we'll try to explain the result of all these technicalities as succinctly as possible.
Also, note that we're not obligated to disclose any of this technical information. However, since uFincs operates in the spirit of transparency, we believe you have a right to know this information. Just be aware that the details of our security are constantly evolving and that this overview might get out-of-date (as is true of all things).
tl;dr Data in transit is encrypted with HTTPS/TLS, data at rest is encrypted with AES-256, financial data in our database/processed by our servers is encrypted with AES-256 using keys derived from your password, we hash passwords on both the client-side and server-side, and we have a very strict CSP.
We use 'bank-level' security
Many other services like to claim that they use 'bank-level' security. In reality, this is more of a marketing ploy than anything else. This isn't to say that it's bad or untruthful, just that it isn't as comprehensive as you might think it is.
Usually, when a service says they use 'bank-level' security, they mean to say that they do two main things:
- They use HTTPS/TLS to encrypt data in transit.
- They use AES-256 to encrypt data at rest.
We do use both these things. We just do a bit more than that (explained in a bit).
HTTPS/TLS for Data in Transit
You know a service is using HTTPS/TLS when you see the lock icon next to the URL in your browser.
This means that all the data sent between your browser and our servers (more precisely, our load balancer) is encrypted. The result is that, if someone were listening to your network activity (e.g. you've been 'bugged'), they wouldn't be able to tell what data it is that you're sending over the network.
However, once the data hits our servers/load balancer, it is then decrypted so that it can be processed by our application servers.
This is all fine and good for the average service. But we take it a step beyond that to make sure we really can't read your data. Again, explained in a bit.
AES-256 for Data at Rest
There are two things to break down here: what "AES-256" is and what "data at rest" is. Let's start with the latter.
"Data at rest" essentially means the data as it sits (at rest) on a 'hard drive'. This 'hard drive' sits inside a server, which sits inside a data center. As such, "data at rest" is the physical data stored somehow, somewhere, in someplace.
"AES-256" is a symmetric encryption algorithm that is widely used and accepted. It essentially works by using a secret 'key' to encrypt some amount of data such that you can't decrypt (or read) the data unless you have the secret key.
As such, using AES-256 for encrypting data at rest means that the data on the physical hard drives where the data is stored is encrypted.
The result is that, if someone were to walk into a data center and steal precisely the hard drive(s) that contained your data, they still wouldn't be able to read it without the secret AES-256 key.
So encryption for data at rest is essentially the final guard against physical theft of data.
For full transparency, we use Google managed keys for our encryption at rest as part of our usage of Google Cloud Platform. We could manage the keys ourselves, but we trust Google to not do anything too nefarious.
Not that it matters when we have the below.
But we do more than 'bank-level' security
For most services, this 'bank-level' of security is good enough. But since we're very privacy-focused, we wanted to go a step beyond. We don't want to be able to read your data at all.
Firstly, we use end-to-end encryption to encrypt your data.
Secondly, we use an enhanced password storage security scheme.
Note: uFincs' use of 'end-to-end encryption' might be more precisely construed as 'client-side encryption'.
Before I explain how we use end-to-end encryption, let me first state the benefit of using it. Firstly, it means that we can't read/access your data. Secondly, it adds a layer of protection on top of everything explained above that makes it so much harder for a determined attacker to read your data.
If we take data in transit, end-to-end encryption means that even if an attacker was sniffing your network activity and could decrypt it (i.e. a successful man-in-the-middle attack), then all they'd see on the network is the data that was encrypted from your device. In effect, there are two layers of encryption: the one afforded by HTTPS/TLS and the one afforded by our end-to-end encryption.
If we take data at rest, end-to-end encryption means that even if an attacker got their hands on the hard drive that contained your data and could decrypt (e.g. they stole the at-rest decryption key from Google), then all they'd see is the data that was encrypted from your device. Again, multiple layers of encryption.
Now, to explain how this 'end-to-end encryption' works.
Sign Up: Key Generation
When you sign up for uFincs, several cryptographic pieces are generated for you: a 'data encryption key' (DEK), a 'key encryption key' (KEK), a salt (or random piece of data) for the KEK, and finally the 'encrypted data encryption key' (EDEK).
The names are somewhat self-explanatory. The DEK is the AES-256 key that is used to encrypt your data. This is the most important key since it's what ultimately enables access to your data. This key is entirely randomly generated at sign-up. We never send this key (in plaintext) to our servers.
The KEK is interesting. It is another AES-256 key that is generated using (currently) one million rounds of PBKDF2 with your password as the key material, as well as the KEK salt. The KEK is used to encrypt the DEK (again, using AES-256) to generate the EDEK.
The KEK salt is a random piece of data that is used (in combination with the PBKDF2 output) to generate the KEK.
Upon completion of your sign up, the KEK salt and EDEK are transmitted to our servers and stored in our database. These two pieces of data (along with your provided password) are all that is required to re-derive your KEK and DEK whenever you log in to uFincs.
Additionally, a hashed version of your password is transmitted and stored in our database. That is, our servers never see the plaintext version of your password. See the below section on Enhanced Password Storage Security for more details.
Logging In: Data Decryption
First, you log in to uFincs. You enter your password and it gets checked against the hashed version in our database. If it matches, then your KEK salt and EDEK are transmitted to your device/browser.
Upon receipt of the KEK salt and EDEK, your KEK is re-generated using your plaintext password and the KEK salt. Then the EDEK is decrypted using your KEK to generate your DEK. Your device can now encrypt/decrypt your data using AES-256.
Once your DEK has been re-generated, your encrypted data is transferred from our servers to your device. It is then decrypted using your DEK and stored locally on your device/browser.
Now, let's take an example of encryption.
First, you enter (for example) a financial transaction as part of using uFincs. Maybe you decided to buy some avocado toast today. This data will get stored on your device/browser, locally, in plaintext (i.e. unencrypted).
Assuming you are connected to a network (i.e. you're not offline), uFincs will then attempt to save the data you entered to our servers so that you can access it from anywhere.
Before transmitting the data, it will look up your DEK that was re-generated during the login process and then encrypt your data. The encrypted data is then encrypted again as part of using HTTPS/TLS and sent off to our servers.
Once the data hits our load balancer, the HTTPS/TLS encryption is removed, leaving just the data that was encrypted by your DEK. This data is then received by our application servers and stored in our database.
Et voila! We have your data but we can't read it. Only when you log in on another device (or refresh your current device) will the encrypted data be transferred back to you and decrypted.
Enhanced Password Storage Security
The average web site will handle passwords as follows: a user enters it into a form on a web page, the plaintext password is then transmitted to the service's servers (hopefully using HTTPS), and then the servers will hash the plaintext password a bunch of times with a random salt using (hopefully) a purpose-built key derivation function.
This makes it so that if someone broke into their database and stole the hashed passwords, they would have to brute-force each password to get the credentials for each user. Usually good enough.
However, there is one key problem with this scheme: the servers do see the plaintext password.
So we decided to add one extra step: hash the password client-side in addition to hashing it server-side. We use SHA-512 to hash on the client and then bcrypt with an appropriate work factor to hash on the server.
This accomplishes two main things: first, our servers never see your plaintext password. As such, we can't derive your KEK (and thus your DEK) and then read your data.
Secondly, if someone were to man-in-the-middle your network connection and break through the HTTPS encryption, then they wouldn't directly see your password. This is still quite bad since the attacker could still authenticate to uFincs, but they wouldn't 1. be able to generate your KEK/DEK, and 2. wouldn't be able to re-use the 'password' on other services (i.e. this mitigates against the ever-so-common re-use of passwords by users).
Of course, if the attackers knew that it was a hash (which they would if they read this document), then they'd have to brute-force it to get your actual password, which is very non-trivial to do if you choose a strong enough password.
In conclusion, taking this one extra step (hashing passwords client-side) can protect against extra attacker scenarios while lending us greater credibility since we never see your password.
Aside: Key Storage
There are two main places where key storage happens: in our database and on your device/browser.
I want to explain the consequences of how we store your keys and what would happen if an attacker broke in.
If an attacker were to break into our database and steal everything, they would have the following: a (doubly) hashed version of your password, your KEK salt, your EDEK, and your encrypted data.
To decrypt your data, the easiest route (assuming they don't know about some secret side-channel attack or failure with AES) would probably be to start with the password. They would first have to brute-force the bcrypt hashing, which in itself would be difficult considering the input is an SHA-512 hash. Brute-forcing something of said length would be... very non-trivial.
If they somehow were able to brute-force through the bcrypt hash, they'd then have to brute-force the SHA-512 hash. Again, non-trivial. Only after they've done so do they get your password.
Then with your password, they can use the KEK salt to re-derive your KEK, then decrypt your EDEK to get your DEK, then finally use your DEK to decrypt your data.
That is the worst-case scenario.
If they (somehow) only stole your encrypted data (but none of the key or password data), then they'd have a hell of a time breaking the encryption. Effectively, impossible.
First of all, let's make something clear here that should be obvious: if someone physically gets access to your device while you are logged in, you are screwed. They can just access your data using the UI, so that's not something we can feasibly defend against.
Now, let's say an attacker has somehow gained the ability to run arbitrary code on your device/browser while you are using uFincs (e.g. the famous XSS attack).
The good news? They wouldn't be able to (trivially) steal your DEK since it is stored specially within the browser such that it can't be exported out of memory/storage.
The bad news is that, once again, it wouldn't matter whether or not they could steal your DEK since they could just steal your data. It's right there, in the browser, unencrypted.
Fortunately, we've gone to great lengths to try and mitigate such XSS attacks. The star of the show? Our Content Security Policy.
Content Security Policy
Content Security Policy (or CSP) is a web browser feature that acts as an allow-list for what scripts/images/network calls/everything else that a web page can make.
And we have a very strict CSP.
In essence, we only allow a certain set of scripts to run on our site, that are known beforehand. This comprises the main script that powers uFincs along with the scripts that are needed to use Stripe (our Payment Processor).
That's it. Everything else is blocked.
This greatly limits the ability of attackers to perform an XSS attack against uFincs. Which helps to prevent one of the most obvious ways to steal your data.
What happens when I change my password?
Contrary to popular belief, when you change your password, we do not actually re-encrypt all of your data. Instead, all we do is generate a new KEK based on your new password, re-encrypt your DEK to get a new EDEK, and update your EDEK in our database (along with the new KEK salt).
This way, the password change can be done seamlessly and quickly. Of course, just like all of our other key management procedures, this key change happens completely client-side (in your browser) so that we never have access to your KEK/DEK.
What happens if I forget my password?
Unfortunately, since your encryption keys (specifically, your KEK) are tied to your password, if you forget your password, all of your data will be lost (unless you have unencrypted backups). During the "Forgot my password" process, you will be warned that all of your (encrypted) data will be removed from our systems if you proceed with resetting your password.
As such, it is incredibly important that you make use of something like a password manager to store your passwords. No matter how much we might want to, we will not be able to help you recover any of your encrypted data in the event that you forget your password.
What data does all this security apply to?
Encryption in transit and encryption at rest applies to all of the account information and financial information that you provide us.
For more details on how Stripe secures your billing information, see their guide on the matter.
For your account information, we store your email in plaintext in our database, but your password is stored using our enhanced password storage scheme (as described above).
Finally, all of your financial information has the end-to-end encryption scheme applied (as described above).
What's the catch?
All of this security (especially the transparent levels at which we have presented it) can certainly seem like it's "too good to be true". Well, there is a catch. There is always a catch.
Before I tell you what our biggest weakness is, let me just reinforce the fact that we are in the business of selling software-as-a-service (i.e. the trade of money for services). We are not in the business of data collecting, data processing, or data selling. As such, it is in our best interests to make sure your data is as secure as it can be. That is one of the many reasons that we require you to pay for a plan before we'll even touch your (encrypted!) data.
But here's the catch: uFincs is not a trust-less system. What does that mean? It basically boils down to the fact that we (both "we" as in uFincs the company and "we" as in the people that run uFincs) could turn evil. We could change the app so that it isn't secure. So that it steals your encryption keys or all of your data. So that it does all the things that we say it doesn't do.
That means that you still have to have some trust in us. Now, this is true in general, but it's more relevant since uFincs is (primarily) a web app. This makes it that much easier for us to push malicious changes if we so chose. So that means you have to trust that the interests we have presented thus far are, in fact, our best interests. That we won't want to steal your data. And that we do our darndest to prevent others from doing the same.
That's it. That's the catch. If you think that this catch is still too much of risk (i.e. any potential possibility for a leak of your financial data would be utterly catastrophic), then don't use uFincs. If that's the case, then I suggest sticking to pencil and paper with a one-time pad :)
In summary, we've gone to quite the lengths to ensure that you remain the sole owner of your financial data. We obviously don't want it, but we also want no one else to have it.
Hopefully, everything explained in this document made it a bit more clear how we go about making sure that stays the case.
As usual, please do not hesitate to send us an email at firstname.lastname@example.org if you have any questions or concerns about our Security, any of our other policies, or anything about the operations of uFincs.