3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

eosio.code 権限の使い方とセキュリティリスク

Last updated at Posted at 2018-12-09

スマートコントラクトの処理の中、inline actionなどで他のスマートコントラクトのアクションを呼び出す時、スマートコントラクトの active 権限設定に eosio.code を追加する必要があります。

では、コントラクトにこの設定をしたら実際どういう動きになるかを確認してみます。

2つコントラクトを実装する

  • hello コントラクトから message コントラクトに送信し、メッセージを message コントラクトのマルチインデックステーブルに保持するようにしています

hello コントラクト

#include <eosiolib/eosio.hpp>

using namespace eosio;

class [[eosio::contract]] hello : public eosio::contract {
  public:
    hello(name receiver, name code, datastream<const char *> ds) : contract(receiver, code, ds){}

    [[eosio::action]]
    void hi(name from) {
      print("hello, from ", name{from});

      action(
        permission_level{from, "active"_n},
        "message"_n,
        "callme"_n,
        std::make_tuple(name{from})
      ).send();
    }
};

EOSIO_DISPATCH(hello, (hi))

メッセージを表示するコントラクト

#include <eosiolib/eosio.hpp>

using namespace eosio;

class [[eosio::contract]] message : public eosio::contract
{

  public:
    message(name receiver, name code, datastream<const char *> ds) : contract(receiver, code, ds){}

    [[eosio::action]] 
    void callme(name from)
    {
      require_auth(from);

      print("call me from :", name{from});
    }
};

EOSIO_DISPATCH(message, (callme))

動作確認

eosio.code 権限設定してない場合

上記2つのコントラクトをコンパイルしデプロイしただけの場合は、hellohi アクションを送信すると、下記のような、権限エラーになってしまいます

$ cleos push action hello hi '["bob"]' -p bob
Error 3090003: Provided keys, permissions, and delays do not satisfy declared authorizations
Ensure that you have the related private keys inside your wallet and your wallet is unlocked.

nodes 側のログを確認してみると、下記のエラーが表示されています。

2018-12-09T10:44:46.025078600Z debug 2018-12-09T10:44:46.024 thread-0  http_plugin.cpp:581           handle_exception     ] Exception Details: 3090003 unsatisfied_authorization: Provided keys, permissions, and delays do not satisfy declared authorizations
2018-12-09T10:44:46.025118600Z transaction declares authority '{"actor":"bob","permission":"active"}', but does not have signatures for it under a provided delay of 0 ms, provided permissions [{"actor":"hello","permission":"eosio.code"}], provided keys [], and a delay max limit of 3888000000 ms
2018-12-09T10:44:46.025143200Z     {"auth":{"actor":"bob","permission":"active"},"provided_delay":0,"provided_permissions":[{"actor":"hello","permission":"eosio.code"}],"provided_keys":[],"delay_max_limit_ms":3888000000}
2018-12-09T10:44:46.025163300Z     thread-0  authorization_manager.cpp:517 check_authorization
2018-12-09T10:44:46.025182100Z pending console output: hello, from bob
2018-12-09T10:44:46.025200900Z     {"console":"hello, from bob"}
2018-12-09T10:44:46.025219900Z     thread-0  apply_context.cpp:72 exec_one

eosio.code 権限を設定する場合

$ cleos set account permission bob active '{"threshold":1,"keys":[{"key":"EOS6tnL51zAKme8fQcFq7ivboqCsttq5sncH6CdETGJGFu9xqVmHF","weight":1}],"accounts":[{"permission":{"actor":"hello","permission":"eosio.code"},"weight":1}]}' -p bob@owner
executed transaction: de23a22469ed9c1c88430114f5e3023e8bf60f33d53597e9ba2fc9382c09bc09  184 bytes  365 us
#         eosio <= eosio::updateauth            {"account":"bob","permission":"active","parent":"owner","auth":{"threshold":1,"keys":[{"key":"EOS6tn...
warning: transaction executed locally, but may not be confirmed by the network yet         ]

再度実行してみます。

cleos push action hello hi '["bob"]' -p bob
executed transaction: 21bea94d2e9e9bd3ea85aa93b84d838011dbc5810937055af32a799cac7d350a  104 bytes  7693 us
#         hello <= hello::hi                    {"from":"bob"}
>> hello, from bob
#       message <= message::callme              {"from":"bob"}
>> call me from :bob
warning: transaction executed locally, but may not be confirmed by the network yet         ]

正常に bob 権限で実行出来ました。

セキュリティリスク

注意しないと行けないのは、

  • bob@active 権限で hellohiアクションを呼び出しました
  • helloコントラクトのhiアクションの中で、messageコントラクトのcallmeアクションを呼び出しました
  • callmeアクションの中でrequire_auth(from)つまり、require_auth('bob'))を検証しています

require_auth('bob')は通ってます!!!

helloコントラクトがbobアカウントに代わりにmessageコントラクトのアクションを実行してもらったように見えます。

勿論、これができている前提は、bob アカウントの所有者が明示的にactive権限をhelloコントラクトのeosio.codeロールに付与したわけですが、helloコントラクトに悪用される恐れがあります。

上記はbobアカウントからhiアクションを実行したが、bobアカウントが知らないうちに helloコントラクトがの所有者が勝手にbobアカウントの代わりにmessageコントラクトのcallmeを実行できるかどうかを検証してみます。

$ cleos push action hello hi '["bob"]' -p hello@active
executed transaction: 7817d28da51d306d5523fa92b7301c1371f5cfed7bde1f65aa3c81d84a1aed08  104 bytes  8416 us
#         hello <= hello::hi                    {"from":"bob"}
>> hello, from bob
#       message <= message::callme              {"from":"bob"}
>> call me from :bob
warning: transaction executed locally, but may not be confirmed by the network yet         ]

出来てしまいました!!!

eosio.token の送金を呼び出してみます

コントラクトを更新することができるので、仮に hello アカウントを持っている開発者が、上記の権限を持っている時、こっそりコントラクトを下記のように、eosio.token の transfer処理を加えたとします。

#include <eosiolib/eosio.hpp>
#include <eosiolib/asset.hpp>

using namespace eosio;

class [[eosio::contract]] hello : public eosio::contract {
  public:
    hello(name receiver, name code, datastream<const char *> ds) : contract(receiver, code, ds){}

    [[eosio::action]]
    void hi(name from) {
      print("hello, from ", name{from});

      action(
        permission_level{from, "active"_n},
        "message"_n,
        "callme"_n,
        std::make_tuple(name{from})
      ).send();

      action(
        permission_level{from, "active"_n},
        "eosio.token"_n,
        "transfer"_n,
        std::make_tuple(from, get_self(), asset{10000, symbol{"EOS", 4}}, std::string("test"))
      ).send();
    }
};

EOSIO_DISPATCH(hello, (hi))

コントラクト更新した後、hello@active 権限で実行してみると、

$ cleos push action hello hi '["bob"]' -p hello
executed transaction: ff9447dad069c4a8a56d2235af7451c1bad26253fba7f8f26bbd93e68394de78  104 bytes  10477 us
#         hello <= hello::hi                    {"from":"bob"}
>> hello, from bob
#       message <= message::callme              {"from":"bob"}
>> call me from :bob
#   eosio.token <= eosio.token::transfer        {"from":"bob","to":"hello","quantity":"100.0000 EOS","memo":"test"}
#           bob <= eosio.token::transfer        {"from":"bob","to":"hello","quantity":"100.0000 EOS","memo":"test"}
#         hello <= eosio.token::transfer        {"from":"bob","to":"hello","quantity":"100.0000 EOS","memo":"test"}
warning: transaction executed locally, but may not be confirmed by the network yet         ]

bobから 100 EOS の送金処理が実行されてしまいました!!!

まとめ

eosio.code 権限は、明示的に このコントラクトを信頼していいよ 的な意味合いであって、付与すると、自分の代わりにいろいろできてしまうので、設定する時は、慎重にやりましょう。

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?