1

I am playing around with the idea of creating a website for cryptocurrencies, where a user can sign up on my website, enter his API details for one of the exchange markets that I will support, which allows him to trade on that exchange, but using my “more user friendly” web interface.

My main goal is to create a more user friendly interface than what most exchange websites offer. I am not hooking directly into any cryptocurrencies or wallets, all I do is use the API of existing exchange markets, relay the information to my website, where I have a more user friendly interface.

Since this is a very sensitive subject in regards to security, I am trying to figure out, what the best way would be to store the API details of the users. In general I don’t like the idea of storing the API details on my database server, nor on my server in general. The thought of having my website hacked and all the API details being exposed is terrifying. Of course each exchange website that supports APIs has their own security built in, such as API sessions with 2FA, IP restrictions, weekly generations of new API secret keys, daily trading limits via API, and not allowing withdrawals of wallets via API. But damage can still be done if those API details get stolen.

I would prefer if there would be a way where I would not need to store the API details on my server at all, but rather have the user save them locally on his PC. That way he is in charge of keeping the API details secure.

This thought brought me then to the idea of creating a desktop app using electron (https://electron.atom.io/). That way I can still create the website the way I want, but it’s wrapped into electron, so it always run locally. Before I pursue this idea, I would like to keep investigating my previous idea of a regular website, as I prefer to have my website cloud based, SaaS, to prevent piracy.

So I wonder, storing API details of a user, without saving them on the server, what other options would I have? Cookies? Probably not secure. What about localStorage? https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API

Are there other options or am I too paranoid about this? Is it generally accepted to store sensitive API details on a database server along with the rest of the users details?

2

How about storing the API tokens in a way that not even you can use them?

Encrypt the API tokens with a key derived from the users password. To avoid having to send the password to the server in plain text you could use a hash of it as the key - making sure to use a different salt on the client side to the normal server password storage process. On login the tokens can be pulled from the database and decrypted. If the user forgets their password then the API details can't be recovered from your copies - but that is OK because they can get them and reenter from the exchanges again.

If taking this approach you want to take steps to make sure the tokens are not paged out on your server. There is little point going to this much effort to then have them written to disk anyway. You also need to ensure they are flushed from memory on log out.

Another alternative would be to derive and use the keys / API tokens on the clients machine - this way you never have access to them in plaintext. This assumes the Exchange APIs have CORS policies which allow your application to call them from the clients machine. An example of key derivation / use using the WebCrypto API (More complete code here - https://pastebin.com/XzrarRha)-

var _algorithm = "AES-CBC";
var _key_size = 256;
var _pbkdf_iterations = 100;

// Do not change this once used in production
// If copying from StackExchange replace with a new random value for your app 
// or pass in a user specific value from the DB
var _defaultSalt = "af95a2b25229d227269a54fdec562d93";

// Derives a key from the given password.
// Salt is not required. If supplied should be a hex string.
function deriveKey(passphrase, salt)
{   
    if (typeof(salt) === 'undefined')
    {
        salt = _defaultSalt;
    }

    return passphrase == null || passphrase.length < 10 ?
        Promise.reject("Password must be at least 10 characters") :
        crypto.subtle.importKey(
            'raw',
            stringToUtf8ByteArray(passphrase),
            { name: 'PBKDF2'},
            false,
            ['deriveBits', 'deriveKey' ]
        ).then(function(passwordKey) {
            return crypto.subtle.deriveKey(
                {
                    "name": 'PBKDF2',
                    "salt": hexStringToByteArray(salt),
                    "iterations": _pbkdf_iterations,
                    "hash": 'SHA-256'
                },
                passwordKey,
                { "name": _algorithm, "length": _key_size },
                false, // Extractable is set to false so that underlying key details cannot be accessed.
                [ "encrypt", "decrypt" ]
            );
        });
}

function encryptData(keyObject, data)
{
    let iv = crypto.getRandomValues(new Uint8Array(16));

    return crypto.subtle.encrypt(
        {name: _algorithm, iv: iv},
        keyObject,
        data
    ).then(function(encryptedData) {
        return {
            iv:iv,
            data:encryptedData
        }
    });
}

function decryptData(keyObject, iv, encryptedData)
{   
    return crypto.subtle.decrypt(
        {name: _algorithm, iv: iv},
        keyObject,
        encryptedData
    );
}
  • can you show an example of using this? i get stringToUtf8ByteArray is undefined, and i'm not sure the output is serializable. my take on it is a bit more complicated, was wondering if it could be as simple as yours... – dandavis Nov 16 '17 at 9:00
  • @dandavis - Apologies. The code supplied was a cut down version of a small demo I through together really meant as helper code rarther than a full working example. I think this should be enough to run (with some very basic HTML for the named elements at the bottom) - pastebin.com/XzrarRha – Hector Nov 16 '17 at 9:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.