status-im / status-desktop

Status Desktop client made in Nim & QML
https://status.app
Mozilla Public License 2.0
297 stars 79 forks source link

Add Mercuryo signer to proxy #16005

Open dlipicar opened 2 months ago

dlipicar commented 2 months ago

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

jakubgs commented 2 months ago

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

jakubgs commented 2 months ago

Is it possible to rotate those?

dlipicar commented 2 months ago

@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

jakubgs commented 2 months ago

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?

dlipicar commented 2 months ago

hmm I'm thinking of:

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.

jakubgs commented 2 months ago

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?

dlipicar commented 2 months ago

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

mendelskiv93 commented 2 weeks ago

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 commented 1 week ago

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