LoginSignup
3
1

More than 5 years have passed since last update.

クッキーインジェクションを試したけど再現できなかった (2)

Last updated at Posted at 2017-03-20

以前、secureクッキーの上書きに成功したことがある、というタレコミをいただいたので、HTTPSで追試してみました。

実験

というわけでHTTPSで実験してみます。適当に証明書をつくっておいてください。

同一URLでのHTTPからHTTPSへの上書きを試みる(secureなし)

foo.example.com用のサーバー証明書と秘密鍵を作っておいて、それぞれexample.crt、example.keyという名前でローカルに保存しておきます。実験では間違ってexample.com用のを作ってしまって警告が出た(鍵を作る時のオレオレルートCAはキーチェーンに入れていたのだけどCNを間違った)けど、まあ結果には影響はないでしょう。同じハンドラをhttpとhttpsの両方で使います。httpsでつながれた時(プロトコルがHTTP/2かどうかで判定)はsecret、そうじゃないときはevilという値をクッキーに設定します。

package main

import (
    "fmt"
    "net/http"
    "net/http/httputil"
)

func main() {
    var httpServer http.Server
    var httpsServer http.Server
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        dump, err := httputil.DumpRequest(r, true)
        if err != nil {
            http.Error(w, fmt.Sprint(err), http.StatusInternalServerError)
            return
        }
        fmt.Println(string(dump))
        if r.ProtoMajor == 2 {
            http.SetCookie(w, &http.Cookie{Name: "test-cookie", Value: "secret", Path: "/", Domain: "example.com", MaxAge: 1000})
        } else {
            http.SetCookie(w, &http.Cookie{Name: "test-cookie", Value: "evil", Path: "/", Domain: "example.com", MaxAge: 1000})
        }
        fmt.Fprintf(w, "<html><body>hello</body></html>\n")
    })
    fmt.Println("start http listening :80 and :443")
    httpServer.Addr = ":80"
    httpsServer.Addr = ":443"
    go func() {
        fmt.Println(httpsServer.ListenAndServeTLS("example.crt", "example.key"))
    }()
    fmt.Println(httpServer.ListenAndServe())
}

期待する動作は次の通り。

  • https://foo.example.comにアクセス(test-cookieというキーがsecretになる)
  • http://foo.example.comにアクセス(test-cookieというキーがevilになる)
  • https://foo.example.comにアクセスしたときに、クッキーの値(httpで設定したevilがサーバーに送信される)

実験してみると、見事にevilになりました!

クロスドメインはダメでしたが、同一URLならHTTPからsecureなしのHTTPSのクッキーの上書きはできました。

同一URLでのHTTPからHTTPSへの上書きを試みる(secureあり)

実は↑の設定は本来つけるはずのsecureを付け忘れて実験してしまっていたので、secureつきでも実験してみます。

次にクッキー設定のところに Secure: true を追加するだけです。

http.SetCookie(w, &http.Cookie{Name: "test-cookie", Value: "secret", Path: "/", Domain: "example.com", MaxAge: 1000, Secure: true})

期待する動作は次の通り。

  • https://foo.example.comにアクセス(test-cookieというキーがsecretになる)
  • http://foo.example.comにアクセス(test-cookieというキーがevilになる)
  • https://foo.example.comにアクセスしたときに、クッキーの値(httpで設定したevilがサーバーに送信される)

で、動かしてみると、evilにならず。元のクッキーにsecureが付与されていると上書きが無視されるようです。開発者ツールで見てもクッキーの値が変わらず。設定できた上で送信時にフィルタリングされるわけではなく、設定自体が無視されているようなので、httpで何度繋いでも送信したクッキーが返ってこないという。なかなかおもしろいですね。

というわけで、secureさえ付けたらクッキーインジェクションは怖く無さそうです。

追記(3/21): この仕様がRFC化されつつあるらしい。 http://qiita.com/flano_yuki/items/b87b2c28db0b056665ef#deprecate-modification-of-secure-cookies-from-non-secure-origins

まとめ

ブラウザベンダーすっごーーい!君はセキュリティが得意なフレンズなんだね!

3
1
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
1