-
Notifications
You must be signed in to change notification settings - Fork 5.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Remove tx.origin #683
Comments
SafeMarket currently has a vulnerability due to use of tx.origin. Thankfully, Peter explained this bug to me otherwise I would have fallen into the trap. |
I'm not sure removing is a good idea, but we can add a warning. |
Why not? |
Because it will break backwards-compatibility. If you are saying that a warning is not enough and will not be noticed, we should revisit how people compile their contracts. |
If someone wants to implement that, please take a look at #677 which is very similar. This one has to go into |
Fair enough. Is Solidity committed to backwards-compatibility in general? I would recommend at least adding it to the list of things to change when you do the next breaking change. Any code that would be broken by this needs to be broken, because it is almost certainly insecure in some way. |
Good point here 👆 |
I think @PeterBorah makes a good point here. I think backwards compatibility for this security flaw might be worth breaking just this time. |
So far the only times I've used tx.origin are:
|
I have contracts that ripple calls up a chain of contracts.. and I need to know which account was the originator. I use tx.origin for that. |
@slothbag Regardless of the outcome of this pull request, double and triple check that you're actually secure there. Chances are, there are ways for malicious contracts to do unexpected things by impersonating your users or your contracts. Is it ok if an attacker is able to call any of your functions at any time, with any arguments, while appearing to be a legitimate user? If not, Also consider whether it's ok that your users can't use multisig wallets, and can't be DAOs or anything like that. If you're interested, I'd be happy to take a look at your contracts and offer advice for structuring your contracts so you don't need |
Thanks @PeterBorah I will definitely revisit my usage of it. |
One of our contracts allows registering only non-contract address. We are checking this with |
@Nashatyrev: Why do you want to do this? It vastly reduces your user's security options, because they can't use multisig, key revocation, or other contract-based security systems. As a user, I would not appreciate being forced to use a dangerous method (a bare private/public keypair) for interacting with a contract. Additionally, this won't work the way you expect it to after EIP 101. |
E.g. you have a Token contract (address -> balance), and you want to get fees from transferring tokens between owners. If you allow a contract to own a token account then those token can be locked and transferred between owners without any fees (i.e. kind of derivative token contract). Non-contract only accounts prevent this. As a user you may grant any contract with access to your account (which still prevents token locking) for the benefits you've mentioned. Not sure why EIP 101 should break this? The transaction will still have a signer and the contract invocation will still have a caller. |
This isn't true. There will still be a private key somewhere with access to the tokens, which means there's no way to use any other form of security exclusively. If someone gets access to the private key, it's game over. (It's also just annoying that I have to generate a private key just for your one dapp, rather than using the security system I use for literally everything else.)
I don't know exactly what the semantics will be, but either |
You may generate a key, create an account, grant account access to any contract you like and immediately destroy the key. EIP 101: Do you mean this change is going to be VM backward incompatible? |
Yeah, that works I guess, though you have to hope that you didn't leak the key during the creation and signing phase. Not as secure as I'd like, but not terrible.
I don't have one set up yet, so I was referring to the hypothetical future. But any security system based on smart contracts, which will be pretty much any of them. For instance, anything with a multisig component, or anything where you can change keys or where you have "emergency override" keys. We're building this sort of system at Ownage for our users, and there will be lots more coming out in the near-to-medium-term future. The Mist multisig wallets are a simple example of this pattern.
Well, it's definitely backwards incompatible (until now all transactions had to be signed in a specific format, after EIP 101 any transaction is potentially valid regardless of signature). But in this specific case, it's not the backwards incompatibility that's the problem, but the general functionality implied by the EIP. Since you can have your "account" work based on any code you write, the "account" can become the sort of derivative you don't want. |
It would definitely make sense starting a list of possible breaking changes and have them in the next version. Since we're following semver, 0.4.x could have breaking changes. I would include not only security related changes, but remove any possible dead weight which could be a burden in the future evolution of the language. @chriseth would it make sense having a separate issue opened for it or a git issue tag is sufficient? Judging by a quick browse there are plenty of proposed breaking changes. |
Yes, such a list makes sense. I actually prefix all PRs that introduce breaking change with As far as tx.origin is concerned, I am still not convinced that banning it to inline assembly is a good thing. If you do access control, then every single example uses I'm fine with adding a static analysis check in browser-solidity to search for |
There are lots of things that Solidity doesn't allow you to do. It's a statically typed language! This is the same sort of thing: I'm sympathetic to the argument that power is more important than safety, but that doesn't seem to be Solidity's philosophy in any other situation. |
Actually I have opposite problem, I want to use tx.origin for personal account but it returns contract address in spoke and hub model :( |
@akrolak you’re getting |
@PeterBorah wouldn't one need tx.origin if they are delegating on behalf of the caller? at the moment it is not possible to keep the msg.sender for the original caller when using a proxy contract for example. |
No, you would not need that, even worse, it is really a bad idea. You should keep msg.sender by using a trusted proxy contract and then you can verify that msg.sender is approved proxy contract and then you have original sender as an argument to transaction. On the other hand, if you use tx.origin, you allow any contract deployed on the blockchain to be a proxy, meaning someone wants to buy a CryptoDog or play with some stupid game and that contract could act on behalf of the user, if you rely on tx.origin. Please, don't do that. +1 for removing tx.origin |
This is a valid use case, as I said before - I don't think it's justified to remove useful functionality solely to prevent people from shooting themselves in the foot. In reposte, I propose to remove |
I oppose the idea of removing tx.origin, Many users do utilize it for different checks. msg.sender couldn't be an alternative to tx.origin as both serve a different purpose. |
Instead use env::predecessor_account_id. Using signer_account_id has the same problems of using tx.origin in Ethereum. See link below for more details: ethereum/solidity#683
* fix: Don't use env::signer_account_id Instead use env::predecessor_account_id. Using signer_account_id has the same problems of using tx.origin in Ethereum. See link below for more details: ethereum/solidity#683 * fix: Use fixed near-sdk-version Need to use fixed near-sdk-version for compatibility with rainbow-token-connector. This restriction will be lifted once the special commit used by the connector lands on near-sdk-rs. * Add test to check near contracts are compiled correctly * Fix pipeline.yml syntax * Update near-sdk from eth-types * Recompile contracts * fix: Use new version of the connector. TODO: Don't use current pinned commit from token-connector, instead wait until it is merged into master * Update arguments to deploy locker using new interface * Use fixed amount of gas * Compile contracts * Update tests * Update cli/package.json Use commit on master.
Using tx.origin == msg.sender is extremely useful. For example lets say I want a function that can only be called by a private key and not a contract. For example, I'm writing a BitBay bridge for my coin with a dynamic matrix-like supply and want to list on Uniswap so I have a whitelist system for typical erc20 calls like transferfrom and want to detect deposits to the AMM. Thus I register it in my contract by detecting LP token shift on Uniswap and last user to interact with AMM and issue special LP coins that match the matrix(either that or convince every AMM we wanna list to change which is impossible). Therefore I want to deny new AMM that aren't registered because their misunderstanding of the dynamic supply can cause losses(supply shift causes array elements to be inappropriately understood by LP pool ratio and only array of LP ratios by our contract can fix it). Even more frustrating that anyone can open an AMM pair on another exchange not knowing risk. The typical ERC20 functions are therefore intentionally only allowed to be used by a user (tx.origin == msg.sender) OR a whitelisted contract. Contract can automatically be notified on new attempt to interact. Whitelisting can of course be voted in democratically (the users agree its not an AMM). This does not limit other contracts by the way. Thus the coin has its own methods for sending not breaking any potential custom contracts that understand the special matrix-like balances! As far as I know (please someone correct me if I'm mistaken) there is only one way to know if a user is not a contract and thats tx.origin==msg.sender. Removing tx.origin will break anything that needs to know that info and those contracts may not be able to easily figure out if user isn't a contract. Granted we can just let LPs know to tread with caution when attempting to deposit to other protocols. Or "let the buyer beware" that they didn't read the code. Well I would much rather limit this specific coins erc20 functionality and add a second way to access the coins functions(or ask to be whitelisted). Adding a way to bypass the check means users can of course still use all functionality with contracts but at least existing contracts can still be used not preventing listings on AMMs. Removing tx.origin will break this functionality. I don't see how removing it to "protect users from themselves" makes sense when you only need to issue a warning. Can't the warning detect that tx.origin is being used to see if its msg.sender or not?! Is there an alternative way to get tx.origin via assembly so if its removed my contract doesn't break? Or is there an alternative way to guarantee msg.sender isn't a contract? EDIT: |
I concur, malloc is really dangerous. As a Solidity developer, I think it is expected of you to know the pitfalls and the limitations of any function or code you write. In my contract I assume that "tx.origin == msg.sender" could be completely manipulated but it does not matter in my particular instance. |
* fix: Don't use env::signer_account_id Instead use env::predecessor_account_id. Using signer_account_id has the same problems of using tx.origin in Ethereum. See link below for more details: ethereum/solidity#683 * fix: Use fixed near-sdk-version Need to use fixed near-sdk-version for compatibility with rainbow-token-connector. This restriction will be lifted once the special commit used by the connector lands on near-sdk-rs. * Add test to check near contracts are compiled correctly * Fix pipeline.yml syntax * Update near-sdk from eth-types * Recompile contracts * fix: Use new version of the connector. TODO: Don't use current pinned commit from token-connector, instead wait until it is merged into master * Update arguments to deploy locker using new interface * Use fixed amount of gas * Compile contracts * Update tests * Update cli/package.json Use commit on master.
I have used it as I have seen it used too as Breaking contracts in a blockchain where code is immutable is just a mistake, is a NEVER do that. This is even more critical when this discussion is about personal taste. Making not expert users see their funds lock, because his transaction that previously worked and now after your quest is successful it won't ever succeed, is morally wrong. The logic behind that you believe that other programmers make mistakes in their code, makes |
I'm trying to come up with a way to produce a unique transaction ID for each transaction, in order to bind the lifecycle of some piece of state to a single transaction -- and the solution may require the use of |
I want to check if the user sending a transaction is a token holder before allowing certain operations in the contract. The flow goes like this User -> ContractA -> MainContract (or) In this case, the MainContract checks whether the user has the token. The only way it's currently possible is through the use of Imagine a case where the MainContract should only be accessible by pass holders(eg: NFT or ERC20 token). If I use msg.sender in MainContract instead of tx.origin, a ContractC can hold that pass and all users can use ContractC to call the MainContract, which defeats the purpose of the pass token. So I don't think tx.origin should be removed. |
Summary
tx.origin
is a security vulnerability, breaks compatibility with other contracts including security contracts, and is almost never useful. Removing it would make Solidity more user-friendly. If there are exceptional cases where access to the transaction origin is needed, a library using in-line assembly can provide it.The Problems
tx.origin
is a security vulnerability. As we recently saw with the Mist wallet, usingtx.origin
makes you vulnerable to attacks comparable to phishing or cross-site scripting. Once a user has interacted with a malicious contract, that contract can then impersonate the user to any contract relying ontx.origin
.tx.origin
breaks compatibility. Usingtx.origin
means that your contract cannot be used by another contract, because a contract can never be thetx.origin
. This breaks the general composability of Ethereum contracts, and makes them less useful. In addition, this is another security vulnerability, because it makes security-based contracts like multisig wallets incompatible with your contract.tx.origin
is almost never useful. This is the most subjective point, but I have yet to come across a use oftx.origin
that seemed legitimate to me. I welcome counter-examples, but I've written dozens or hundreds of smart contracts without needing it, and I have never heard of anyone else needing it either.Rationale for Removal
Solidity's design philosophy is to prioritize security and reliability over expressiveness. In other cases where behavior is unreliable, Solidity does not expose it. (For instance, there is no way to call an external contract and retrieve the return value if the signature is not known ahead of time.)
The "escape clause" is in-line assembly, which allows the creation of libraries to do anything expressible as EVM assembly. Behavior that is unsafe and unreliable is best kept in libraries, rather than given to all users as part of the core language.
The text was updated successfully, but these errors were encountered: