Best Security practices using private key in a cloud function

Hello,

In the moralis docs it is specified that it’s not recommended to sign transactions in a cloud function with a private key. However my use case does require this, so I was wondering, what are some security measures that can be taken to prevent someone compromising the private key and to not exploit the smart contract.

My use case is as follows:
I’m implementing an erc-20 token that holders of an NFT earn each day. I want to handle the logic of computing the reward off chain to save on gas costs. The app would give the user a signature from a specific wallet which they can use in the smart contract to claim their tokens. The smart contract checks the signature as well as the signed data (for example that the msg.sender was used in the signed data) and then updates the users balance. Now the biggest risk would be that someone compromises the private key on the cloud and generates a signature to claim infinite tokens for themselves. Any idea how to secure this and makes this chance as low as possible?

The reason to handle the logic off chain is that it takes too much gas to claim the tokens by providing each nft id of a holder. A person holding 250 NFTs would need to provide 250 tokens which would make the transaction use between 0.3 and 0.7 eth in gas at worst.

you could put some limits in the smart contract maybe, so that even if the key is compromised, no more than x tokens can be claimed, also to have a way to remove that key from the smart contract if needed and also to revert some transactions.
As an extra security step you could use a separate server that only you control from where to generate the signature

1 Like

You could have two accounts, where account_1 holds the coin and account_2 distributes it with an ERC-20 transferFrom(). That way account_1 can limit how much account_2 can spend using allowance().

Also, for added security you can split the private key into chunks that can be individually supplied via execution parameters, database values (deleted after access), .env entries, externally retrieved data etc.

2 Likes

What do you think about just storing the PK as a moralis object with maximum restrictions (all CPL / ACL permissions turned off) and accessing it through a cloud function using the master key @cryptokid @RedCabbage

I don’t know if really it adds more security this way.

That would certainly be a pretty simple method but would only be as secure as the access to your Moralis server.

Ideally you would want to be in a situation where you wouldn’t lose your ETH account if someone were able to log on to your Moralis server.

Perhaps consider having a background job be the only thing that uses the PK. It can then be supplied the PK (or part of it) when it is started up and be fed instructions from the database for executing tranactions.

It’s a balance, though. If the account is used in a way that it isn’t a huge catastrophe if it is compromised then you can get away with less draconian security measures.

1 Like

I would design the app in such a way that the private key on the server is as useless as possible

for example - ensure the wallet never has a lot of funds - top it up all the time
ensure that in case something happens - you can create a new key and remove all privileges from all contracts from old key and give to new key etc

5 Likes

Thanks for the suggestions, really appreciate it!!

Regarding the background job, that seems like a clever way to do it. However that does assume that there is no way to get the code from a moralis job after it’s uploaded right? Then we could just hardcode the key in, upload the jobs and handle all interactions through database triggers.

Since the cloud code is accessible on the server it would be fairly easy to find a hardcoded key by just digging about the code (unless it is very cleverly obfuscated). There may be ways to extract data from a running background process in memory but I imagine it would require much more hacker skillz than reading the cloud code.

the cloud code is only accessible to the person owning the moralis account right?

I see, so one could have a background job read the pk from the database. Then retain it in memory but immediately delete it from the db

the problem would be that the server can restart from time to time, and that memory will be lost

I see,

Alright, so I have implemented what Ivan suggested, being to limit the transfers of the account tied to the pk and having the team topping it up each week / month. Most likely I will store the pk as a moralis object and just disable all accesses to it.

Finally I will make sure that the team ensures their account details are safe.

Could an encrypted PK on the client calling the Moralis Cloud Function be used? Whenever it is needed, the client will supply the encrypted PK to Moralis, meaning the decrypted PK is never stored on Moralis apart from program memory (maybe as a UInt8Array for mutability). Of course, since that increases the attack surface, we could perhaps store the encrypted PK in yet another location, which can be closely monitored, such as IPFS.

The account itself will always be left with just the required amount of tokens, and will itself approve some other account to take away all of its tokens or burn them off in case it’s compromised using OZ’s ER20Burnable* in the token contract.

I’d like to open another thread to discuss how to make a provider with a PK in Cloud Code, since I found no documentation online which tells me if that’s okay to do or not, and I’m still not sure if I should use ether.js or web3.js.

you could also have another server that makes the transactions and obtains somehow the information about what transactions should make