我为客户编写Python脚本,以便在货币/证券交易所网站上进行"algo交易“。我的客户通常在传统的个人台式电脑上运行我的脚本,通常也使用这些个人电脑进行网页浏览活动。环境总是Linux;通常是Debian。在行业中,Python是以这种方式进行algo交易的相当标准的;无论是在制度上还是在私下里。
但是,我不禁发现了安全模型中的一个缺陷。
每个交换都有一个稍微不同的身份验证方法,但简而言之,有:
USER INPUTS:
api['secret'] # private key from exchange
USER CONFIG FILE CONTAINS:
api['key'] # public key from exchange
api['exchange'] # name of exchange; ie "binance"
api['symbol'] # market pair symbol in format BTC:USD
api['uri'] # the url up to .com/
FROM USER INPUTS SCRIPT BUILDS REQUEST SPECIFIC:
api['nonce'] # time.time() at beginning of request
api['endpoint'] # path/to/server/resource
api['url'] # uri + endpoint
api['method'] # GET, POST, or DELETE
api['params'] # dict with request specific parameters
api['data'] # str with request specific parameters
api['headers'] # authentication signature specific to the request这些请求的类型如下:
POST BUY/SELL
DELETE BUY/SELL (CANCEL)
GET ACCOUNT BALANCES
GET OPEN ORDERS
WITHDRAW FUNDS下面是我为一些货币交易所编写的身份验证方法的一些示例;它们在外汇/股票/密码交易行业都是相当标准的。主要采用的格式如下:
api["header"] = {"signature": HMAC(SHA256(the_request_parameters))}例子:
def signed_request(api, signal):
"""
Remote procedure call for authenticated exchange operations
api : dict with keys for building external request
signal : multiprocessing completion relay
"""
api = lookup_url(api)
api["data"] = ""
if api["exchange"] == "coinbase":
api["data"] = json_dumps(api["params"]) if api["params"] else ""
api["params"] = None
message = (
str(api["nonce"]) + api["method"] + api["endpoint"] + api["data"]
).encode("ascii")
secret = b64decode(api["secret"])
signature = hmac.new(secret, message, hashlib.sha256).digest()
signature = b64encode(signature).decode("utf-8")
api["headers"] = {
"Content-Type": "Application/JSON",
"CB-ACCESS-SIGN": signature,
"CB-ACCESS-TIMESTAMP": str(api["nonce"]),
"CB-ACCESS-KEY": api["key"],
"CB-ACCESS-PASSPHRASE": api["passphrase"],
}
elif api["exchange"] == "poloniex":
api["params"]["nonce"] = int(api["nonce"] * 1000)
message = urlencode(api["params"]).encode("utf-8")
secret = api["secret"].encode("utf-8")
signature = hmac.new(secret, message, hashlib.sha512).hexdigest()
api["headers"] = {
"Content-Type": "application/x-www-form-urlencoded",
"Key": api["key"],
"Sign": signature,
}
elif api["exchange"] == "binance":
api["params"]["timestamp"] = int(api["nonce"] * 1000)
api["params"]["signature"] = signature
message = urlencode(api["params"].items()).encode("utf-8")
secret = bytes(api["secret"].encode("utf-8"))
signature = hmac.new(secret, message, hashlib.sha256).hexdigest()
api["headers"] = {"X-MBX-APIKEY": api["key"]}
elif api["exchange"] == "bittrex":
api["params"]["apikey"] = api["key"]
api["params"]["nonce"] = int(api["nonce"] * 1000)
message = api["url"] + api["endpoint"] + urlencode(api["params"])
message = bytearray(message, "ascii")
secret = bytearray(api["secret"], "ascii")
signature = hmac.new(secret, message, hashlib.sha512).hexdigest()
api["headers"] = {}
elif api["exchange"] == "kraken":
api["data"] = api["params"][:]
api["params"] = {}
data["nonce"] = int(1000 * api["nonce"])
api["endpoint"] = "/2.1.0/private/" + api["endpoint"]
message = (str(data["nonce"]) + urlencode(data)).encode("ascii")
message = api["endpoint"].encode("ascii") + hashlib.sha256(message).digest()
secret = b64decode(api["secret"])
signature = b64encode(hmac.new(secret, message, hashlib.sha512).digest())
api["headers"] = {
"User-Agent": "krakenex/2.1.0",
"API-Key": api["key"],
"API-Sign": signature,
}
elif api["exchange"] == "bitfinex":
nonce = str(int(api["nonce"] * 1000))
api["endpoint"] = path = "v2/auth/r/orders"
api["data"] = json.dumps(api["params"])
api["params"] = {}
message = ("/api/" + api["endpoint"] + nonce + api["data"]).encode("utf8")
secret = api["secret"].encode("utf8")
signature = hmac.new(secret, message, hashlib.sha384).hexdigest()
api["headers"] = {
"bfx-nonce": nonce,
"bfx-apikey": api["key"],
"bfx-signature": signature,
"content-type": "application/json",
}
url = api["url"] + api["endpoint"]
ret = requests.request(
method=api["method"],
url=url,
data=api["data"],
params=api["params"],
headers=api["headers"],
)
response = ret.json()我的客户经常问我,在哪里存储api["secret"]。在配置文件中?在环境变量中?在每次重新启动时手动输入并将其物理存储在纸上?我没有很好的答案任何建议..。我很快就看上去了。
我开始编写一个应用程序-在python中-来存储API键:
主要特征:
URL和key输入:secret写入剪贴板w/ xclip安全特点:
我的想法是,我的用户可以使用这个应用程序来保护他们的密钥,在脚本加密意义上.我已经点缀了我的I's和穿越了我的t.当它们被储存起来的时候,它们就被储存起来。句号。我很确信你不能破坏我的加密方案。
你可以找到我的应用程序这里或谷歌: litepresence/CypherVault
但我还是脸朝上。我打开了一个Reddit/r/security线程这里来讨论我的应用程序,我的facepalm很快就被验证了。
最后..。无论我如何处理api“秘密”,它最终都会被恶意软件转储并上传到攻击者的内存中。
当用户进入要加密的secret时.在内存里。当脚本解密secret以签署事务时.在内存里。
然后胡库斯..。“我的剧本”让人失去了钱,因为它不是“安全的”,我有责任感。
如何才能避免这种情况?如何安全地以脚本语言在桌面机器上存储用于合成金融事务的exchange API机密,而不会在某个时候将它们暴露在您的RAM中,甚至可能更糟……你的交换?
发布于 2019-12-30 18:17:28
无论我如何处理api“秘密”,它最终都会被恶意软件转储并上传到攻击者的内存中。..。如何才能避免这种情况?
这不可能。在基于计算机的身份验证步骤中使用密码的要求与计算机不访问密码之间存在着根本的矛盾。没有第三条路。
您可以在不使用时对其进行加密(听起来您已经这样做了)。不使用时,您可以在内存中覆盖它。但是当计算机需要使用它时,它就会在内存中。
取决于你的威胁模式,这可能不是什么大事。政府级别的人身攻击是记忆内容最常见的问题,但很少有人需要对此进行防御。
发布于 2019-12-31 00:28:59
这正是硬件安全模块(HSM)所要解决的问题。HSM有自己的嵌入式处理器,包含一个或多个私钥,这些私钥从不离开HSM。因此,HSM上的私钥对于运行在HSM所连接的机器上的程序是不可访问的。
通过证明HSM拥有与用户公钥相对应的私钥,可以使用HSM对服务器进行身份验证。HSM通过使用私钥及其机载处理器(如数字签名或共享秘密)计算值来实现这一点。数字签名或共享秘密基于服务器提供的短暂值,因此能够截获数字签名/共享秘密的攻击者不能重用该数字签名/共享秘密(即该解决方案可以抵抗重放攻击)。
当然,您使用的服务必须支持这种类型的身份验证。
https://security.stackexchange.com/questions/223457
复制相似问题