LiskArchive / lisk-sdk

🔩 Lisk software development kit
https://lisk.com
Apache License 2.0
2.72k stars 454 forks source link

Transaction replay on different chains #1610

Closed hendrikhofstadt closed 5 years ago

hendrikhofstadt commented 6 years ago

Notice: This is theoretical and I have not yet tested this. If there's a logical flaw in my issue please comment and close

With the new 1.0 API transactions are signed on the client and then sent to the server.

The signature that is included in the signed transaction is created by hashing and signing the binary representation of the transaction, which contains:

Problem

The problem is that there is no reference to the Nethash of the current chain.

Transactions that are recorded by nodes can be replayed on other chains (possibly later sidechains) using the Lisk codebase (even with a different Nethash). If for example someone uses the same public key on the mainnet as on the testnet his transactions on the testnet can be replayed on the mainnet.

Example

Delegate Pool A tests their payout script on the testnet using the same addresses and publicKeys. A user receives more rewards on the testnet and replays the payouts on the mainnet therefore "stealing" rewards.

Recommended Mitigation

Explicitly advise people to use different publicKeys on different chains.

Include the nethash of the current chain in the binary transaction representation/signature so the signature would not be valid on another chain than the one the original transaction is intended for.

4miners commented 6 years ago

@Slamper Thank you for report, we're investigating it.

hendrikhofstadt commented 6 years ago

Additional information: I did some research and found out that on testnet and mainnet 106 non-virgin addresses with a total value of 394094.35 LSK are the same of which 77 with a total value of 13825.39 LSK are not using a second passphrase. 2 of them being active forging delegates (I informed both and asked them to register a second signature)

IkerAlus commented 6 years ago

The proposed solution for this issue can be divided in three steps:

First of all, the use of the same passphrase for mainnet and testnet must be totally avoided. This rule applies not only for Lisk but for any cryptocurrency and internet security related project. Testnet, by its nature, is meant to be used to test any feature or issue, with the consequent potential security breaches or leaks. We are aware there are some community members who have used the same passphrase for testnet too. In that way, we have informed these people with vulnerable accounts and it is our understanding that they have already secured their funds.

Secondly, as a preparation for the third and final part of this solution, the idea is to add a unique identifier for mainnet in the transaction signature. This identifier will be known for the whole network as a parameter/constant so that nodes will be able to verify that the transaction belongs to mainnet or other chains. The use of the nethash as this identifier will greatly mitigate the issue but does not solve the situation where there is a split in the network driven by a part of the community (i.e. Ethereum vs. Ethereum Classic situation). That is why, we propose the use of milestones as identifiers in the case of a hard-fork in the mainchain. For example, for the first milestone, this can be calculated as:

Milestone = hash(nethash + block height prior the fork)

This will uniquely define the LiskHQ Lisk mainchain in case of community forks. In the same way, it can be used a “version ID” containing the new version of the network as the the unique identifier added to the milestone hash.

This change is planned to be done after the 1.0 mainnet release, very likely to be implemented together with the fee-system release.

And finally, the long term proposed solution is related to the implementation of sidechains. Sidechains will need to have a unique identifier in the same way as mainchain to avoid potential transaction replays among the set of sidechains. However, here the situation will be more complex since a malicious sidechain developer may try to copy mainchain (or other sidechain) identifier to try to attack the network replaying transactions. In this way, the proposal we are working on would go as follows:

The signature process (for example, in Lisk Elements) would take as input an object which included the nethash of the relevant network (sidechain), so a user who calls that function knows exactly what will be hashed and signed. Note that the question of which fields are present on the object that is used as input for this function is a different question to the question of what information is transmitted over a network or stored in a database, where the nethash may be omitted for reasons of efficiency/scalability. With a general signature process for the whole Lisk ecosystem, we could demand that sidechains use exactly these functions (or at least functions which meet their specification), removing the opportunity for a sidechain developer to provide a malicious signature function. Users (or third party clients) would be free to use Lisk Elements directly when signing transaction objects, and their suspicions should be raised if the sidechain protocol does not allow this.

MaciejBaj commented 5 years ago

The in-depth description of that problem is available as a LIP form - https://github.com/LiskHQ/lips/blob/master/proposals/lip-0009.md.

Please provide the feedback following the LIP processes (https://github.com/LiskHQ/lips/blob/master/proposals/lip-0001.md#lip-comments), by posting it on the applicable development mailing list thread - http://lists.lisk.io/pipermail/lips_lists.lisk.io/2018-November/000002.html.

The issue is on Lisk Network Roadmap (https://lisk.io/roadmap) - "Security and Reliability- Mitigate transaction replay on different chains" and will be re-opened as a milestone.