status-desktop icon indicating copy to clipboard operation
status-desktop copied to clipboard

Add Mercuryo signer to proxy

Open dlipicar opened this issue 1 year ago • 9 comments

Description

We're currently hardcoding credentials in status-go that are used to generate a signature for Mercuryo URLs. For safety reasons, these should be hidden in our proxy, and the signature should be generated through an http request.

Specs: https://www.notion.so/Mercuryo-de1bd29b77054f4b865c8cba997bd490

dlipicar avatar Aug 06 '24 20:08 dlipicar

Why was that done in the first place? This means these signatures are already leaked.

jakubgs avatar Aug 19 '24 08:08 jakubgs

Is it possible to rotate those?

jakubgs avatar Aug 19 '24 09:08 jakubgs

@jakubgs

Why was that done in the first place? quickest way of making mercuryo work This means these signatures are already leaked the signature only allows you to create a URL with our widget-id, they cannot access our account or consume rate limits or anything like that. Is it possible to rotate those? yes, I'm planning to do that as soon as we've got a proxy signer. you can just change the secret key or create a new "widget" in our account

dlipicar avatar Aug 19 '24 12:08 dlipicar

So how is this supposed to work? Can you explain in more detail what paths you expect to be proxied? And how are credentials passed?

jakubgs avatar Aug 20 '24 17:08 jakubgs

hmm I'm thinking of:

  • We link the user to something like https://prod.api.status.im/mercuryo/?address={address}&(other parameters)
  • Then the proxy does a redirect appending the widget ID and its corresponding signature: https://exchange.mercuryo.io/?address={address}&(other parameters)&widget_id={widget-id}&signature={signature} The signature needs to be generated in the proxy using the logic that's currently in status-go (that is, SHA512 of the string AddressSecret). The Secret and the WidgetID need to be baked into the proxy like we currently bake provider API keys, that way we can freely rotate the two of them if we wish to.

Example:

User gets linked to: https://prod.api.status.im/mercuryo/?type=buy&network=ETHEREUM&currency=ETH&address=0x0ADB6CaA256A5375C638C00e2fF80A9Ac1b2d3A7&hide_address=false&fix_address=true

We redirect them to: https://exchange.mercuryo.io/?type=buy&network=ETHEREUM&currency=ETH&address=0x0ADB6CaA256A5375C638C00e2fF80A9Ac1b2d3A7&hide_address=false&fix_address=true&signature=817cf6a1461e42d676bbb0feae6fdb52c3c58d334ba0ab0001db5b2e897b7584f1adcad6bd385fb39e792178665803f63dd39cf3a4ece4d51dc2f106eef1b77b&widget_id=6a7eb330-2b09-49b7-8fd3-1c77cfb6cd47

Does that sound reasonable? If it does, I'll write proper requirements in Notion.

dlipicar avatar Aug 20 '24 17:08 dlipicar

The implementation seems pretty trivial:

// Should generate the SHA512 hash of the string "AddressKey"
func getMercuryoSignature(address common.Address, key string) string {
	addressString := address.Hex()

	hash := sha512.New()
	hash.Write([]byte(addressString[:] + key))
	return fmt.Sprintf("%x", hash.Sum(nil))
}

https://github.com/status-im/status-go/blob/e07182b3f36731a7e0dbf6ec59b28f0df56d6a43/services/wallet/onramp/provider_mercuryo.go#L105-L112

So it should be doable in Lua. But would we prefer to proxy requests or redirect them?

jakubgs avatar Aug 27 '24 17:08 jakubgs

Legal says we shouldn't link the user to a .status.im website in any part of the process, so we need to get the signature from the proxy and build the URL in the client.

Added doc with specs to the description

dlipicar avatar Aug 28 '24 20:08 dlipicar

This can be done by implementing this Lua script to proxy hosts:

local _M = {}
local cjson = require "cjson.safe"
local sha512 = require "resty.sha512"
local resty_string = require "resty.string"

-- Access Mercuryo constants from Nginx variables
local widget_id = ngx.var.widget_id
local widget_secret = ngx.var.widget_secret

-- Function to generate SHA512 signature
local function getMercuryoSignature(address, key)
    local hash = sha512:new()
    if not hash then
        ngx.log(ngx.ERR, "Failed to create SHA512 object")
        return nil
    end

    hash:update(address .. key)  -- Concatenate the address and key (widget_secret)
    local digest = hash:final()
    return resty_string.to_hex(digest)
end

-- Function to process incoming requests
function _M.process_request()
    -- Get the 'address' query parameter
    local address = ngx.var.arg_address  -- Retrieve the address from the query parameters

    -- Ensure the address is present
    if not address then
        ngx.status = ngx.HTTP_BAD_REQUEST
        ngx.say(cjson.encode({ error = "Missing address query parameter" }))
        return ngx.exit(ngx.HTTP_BAD_REQUEST)
    end

    -- Generate the signature
    local signature = getMercuryoSignature(address, widget_secret)

    if signature then
        -- Send the response with widget_id and generated signature
        ngx.say(cjson.encode({ widget_id = widget_id, signature = signature }))
        return ngx.exit(ngx.HTTP_OK)
    else
        ngx.status = ngx.HTTP_INTERNAL_SERVER_ERROR
        ngx.say(cjson.encode({ error = "Failed to generate signature" }))
        return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
    end
end

return _M

which by curl to proxy server:

curl -s https://test.api.status.im/mercuryo/sign/?address=0xXXXXXXXxxxxXxXXX-u $USER:$PASS

returns:

{"widget_id":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx","signature":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}

mendelskiv93 avatar Oct 17 '24 14:10 mendelskiv93

@dlipicar this is done on both test and prod. Let me know if anything needs to be modified.

mendelskiv93 avatar Oct 23 '24 10:10 mendelskiv93

@dlipicar unless you have any objections I'm closing this as done.

jakubgs avatar Feb 07 '25 10:02 jakubgs