Contract内のサインチェック厳密化

Contract内で、エンドユーザのサインのチェックをすることにより、代払いを実施しながらエンドユーザのサインに基づいた処理が実行できるようになりました。

参考 : Contract内のエンドユーザサイン確認

ただ、このサインには使いまわしの問題が残っています。

サイン自体はエンドユーザの端末内の鍵で行われるため他の人がサインすることはできませんが、エンドユーザの端末からContractに命令が届くまでにはインターネット回線を通るため、全く同一の命令であれば、サインを盗んで使いまわすことが可能になってしまいます。

これを防ぐ方法を紹介します。

  • Timestamp方式

    エンドユーザからの呼出時に、パラメータにタイムスタンプを含めて送り、Contract側で現在時刻との差異によって防ぐ方法です。

    一定期間の間は再利用が可能ですが、それを超えた場合に利用ができなくなります。

    Contract側のタイムスタンプは、ブロック生成時刻で単位は秒になるため注意が必要です。

    下記は、 sendTransaction の最初のパラメータにタイムスタンプを追加した例です。

    contract SomeContract is VersionContract {
    
        uint constant TIMESTAMP_RANGE_SEC = 600;
        mapping(address => bool) allowUsers;
    
        function SomeContract(ContractNameService _cns) VersionContract(_cns, "SomeContract") {}
    
        function someMethod(bytes _sign, uint _timestamp, uint _p1, bytes32 _p2) {
            // create all parameters hash
            bytes32 hash = calcEnvHash('someMethod');
            hash = sha3(hash, _timestamp);
            hash = sha3(hash, _p1);
            hash = sha3(hash, _p2);
            // get end user's addresss
            address endUserAddr = Utils.recoverAddress(hash, _sign);
            if (!allowUsers[endUserAddr]) throw;
    
            // check timestamp
            if (!checkTimestamp(_timestamp)) throw;
    
            // do something
        }
    
        function checkTimestamp(uint _timestamp) private constant returns(bool) {
            return (now - TIMESTAMP_RANGE_SEC< _timestamp && _timestamp < now + TIMESTAMP_RANGE_SEC);
        }
    }
    
  • トランザクションカウントの導入

    この方法はトランザクション発行処理にのみ有効となりますが、各エンドユーザ(アドレス)毎にシーケンス番号を記録する方式です。

    次のような、ユーザ毎のカウントを行う簡単なContractを用意します。

    contract TransactionCounter {
    
        mapping(address => uint) counter;
    
        function getCount(address _user) constant returns (uint) {
            return counter[_user];
        }
    
        function addCount(address _user) {
            counter[_user] += 1;
        }
    }
    

    各トランザクションの実行時に、パラメータにカウント値を追加して再利用を防ぎます。

    下記は、 sendTransaction の最初のパラメータにカウンタを追加した例です。

    contract SomeContract is VersionContract {
    
        TransactionCounter constant TX_COUNTER = TransactionCounter("0xffffffffff...");
        mapping(address => bool) allowUsers;
    
        function SomeContract(ContractNameService _cns) VersionContract(_cns, "SomeContract") {}
    
        function someMethod(bytes _sign, uint _txCount, uint _p1, bytes32 _p2) {
            // create all parameters hash
            bytes32 hash = calcEnvHash('someMethod');
            hash = sha3(hash, _txCount);
            hash = sha3(hash, _p1);
            hash = sha3(hash, _p2);
            // get end user's addresss
            address endUserAddr = Utils.recoverAddress(hash, _sign);
            if (!allowUsers[endUserAddr]) throw;
    
            // check txCount
            if (TX_COUNTER.getCount(endUserAddr) != _txCount) throw;
            TX_COUNTER.addCount(endUserAddr);
    
            // do something
        }
    }
    

    エンドユーザは、書込みが必要なトランザクションの発行前に call を使用して TransactionCounter#getCount を呼び出し、現在のトランザクション番号を取得してから、トランザクションを発行します。

results matching ""

    No results matching ""