なぜPHPでrequire("http://...")したらセキュリティホールなのに、Goならいいのか

go言語なんか import "http://github.com/mattn/xxx " だったりする。rubyもそういうの面白いかもしれない。require "http://github.com/mattn/xxx "みたいな。

mattn on Twitter: "go言語なんか import "http://t.co/9qiwho7OMZ" だったりする。rubyもそういうの面白いかもしれない。require "http://t.co/9qiwho7OMZ"みたいな。"

@mattn_jp それコンパイル型言語なら許されるけどスクリプト型だとセキュリティホールとされるものでは? (e.g. PHP)

Kazuho Oku on Twitter: "@mattn_jp それコンパイル型言語なら許されるけどスクリプト型だとセキュリティホールとされるものでは? (e.g. PHP)"

というやり取りについて、補足します。

まず、前提となる要請について。

  • 処理系は、ユーザが許可した以外のプログラムを実行してはならない(公理)
    • 許可の単位は、(コードの確定した)プログラム単位であってもいいし、それを管理する人(個人or組織)であってもいい
      • 後者の代表例がWindows等の自動アップデート
  • コンパイル型言語(Go)においては、プログラマコンパイルを行う際(go getコマンドを実行する際*1)にimport(require)が行われ、プログラムの内容が確定する
    • その確定したコードがユーザー毎回実行される
    • つまり、ユーザーが信頼するのはコンパイルを行ったプログラマ
      • プログラマがimport(require)されたコードの内容について責任を負う
        • この責任と引き換えにimport "http://..."が正当化される
  • インタプリタ型言語においては、実行時にどのようなコードが動くか(requireが行われることにより)確定される
    • つまり、リモートコードを取得して実行する場合は、そのコードが不変であるか、あるいは、その管理者が発行したものであることを検証できる必要がある*2

以上の要請に基づいて、インタプリタ型言語で URL を require する是非について考える。

  • httpは経路上で改ざんされる可能性があるので公理を満たせない
  • httpsは経路上で改ざんされる可能性はない
    • だが、ドメインは人(個人あるいは組織)と等価ではなく、転売あるいは失効するもの*3
    • したがって、httpsの場合も前提における「許可の単位」に関する要求を満たせない

よって、インタプリタ型言語にhttpあるいはhttpsのURLを指定してrequireする機能がある(使われる)とすれば、それは脆弱性である*4

という結論に至るというのが僕の理解です。

githubをrequire(import)するソースとして使えるかという問題については、githubが倒産せずgithub.comが未来永劫githubの所有物でありつづけるという点に加え、github.com/fooが他のユーザーのものとならない、という要求を満たせるのか、という点も検討する必要があると思います。

一方で、上記要請を満たせるような方式であれば、インタプリタ型言語において動的にコードをダウンロードして実行する機構を設けても構わないと僕は考えますし、実際そうすることにメリットも想定できる(たとえば、バグの修正されたライブラリを自動的にダウンロードして実行してくれる等)と思います。

*1:2013/03/24加筆。ありがとうございます → http://twitter.com/umitanuki/status/315503956358987776

*2:コード証明書

*3:コード証明書とサーバ証明書の違いとは何か、ということ

*4:ローカルアドレスを指定してる場合等の議論は省きます