はじめに
はじめまして!
セキュリティに強くなるつもりが、強くなったのは筋力だった!
新卒入社2年目の脆弱性診断員、秋本です。
最近、診断業務でGo言語(以下Go)製のWebアプリに触れる機会がありました。Goについて全然知らなかったので調べてみたところ、Goのフレームワークは使いやすく整っておりコミュニティも活発みたいです。今後もGo製のWebアプリが出てくると思うので、脆弱性診断員としての知見を広めるという目的で、最近はGoの勉強をしています。
ただ、Goは比較的新しい言語ゆえに、日本語のドキュメントがそれほど豊富でないのが難点です。そんなわけで、今回は私がGoに触れるにあたってつまずいた点とその解決策を共有できればなと思いブログにしました。初学者レベルですがお付き合いいただけると幸いです。
Goのインストール
今回は以下の環境で進めていきます。
構成 | 詳細 |
---|---|
ホストOS | Windows10 |
VM | VirtualBox 6.1.6 |
Vagrant 2.2.7 | |
ゲストOS | Ubuntu18.04.4 LTS |
ダンベル | 10kg |
まずはGoをインストールします。
$ sudo apt install golang
Goのインストールが無事終わったので、とりあえずバージョンを確認しておきます。
$ go version go version go1.10.4 linux/amd64
Ginのインストール
Goはgo get <パッケージ名>
とすることで外部のパッケージを取得することができます。今回はGoのWebアプリケーションフレームワークであるGinに触れる予定なので、
$go get github.com/gin-gonic/gin
を実行します。
go get
で取得したパッケージはGOPATH/src/配下にインストールされ、ソースコード内からimportで呼び出せるようになります。
めっちゃ簡単!
Webサーバを立ち上げてみる
公式のサンプルを参考に、アクセスするとJSONを返すコードを動かしてみます。
example.go
package main import "github.com/gin-gonic/gin" func main() { r := gin.Default() r.GET("/ping", func(c *gin.Context) { c.JSON(200, gin.H{ "message": "pong", }) }) r.Run(":8000") }
Goはコンパイルが必要な言語ですが、go run
コマンドを使うことでコンパイルから実行まで一気にやってくれます。ひとまず実行してみます。
$ go run example.go # github.com/gin-gonic/gin go/src/github.com/gin-gonic/gin/context.go:77:11: undefined: http.SameSite
context.go:77:11:
という、触った覚えのない箇所でundefined: http.SameSite
と怒られました。
解決策の調べ方
以下、解決に至るまでに私が行った手順です。
- エラーメッセージをそのまま検索する
- 重要なワードを抜き取って検索する
- 検索結果から別の言語についての記事を無視する
- タイトルにメインの検索ワードが含まれている記事を片っ端から見る
- 公式のドキュメントと照らし合わせる
↑の手順でいろいろと調べたところ、直接解決策になるようなものは見つかりませんでした。しかし、検索結果の一部でこんな記述を見つけました。
Samesite flag only support on Golang v1.11
Ginの公式のドキュメントを見に行くと、
The first need Go installed (version 1.11+ is required)
と記載されていました。バージョン1.11未満のGoで定義されているCookieはhttp.SameSite
属性に対応していないみたいです。
バージョンを上げる
今回apt install
でインストールしたGoは1.10.4
でした。現在インストール済みのGoとはおさらばして、こちらから最新版をダウンロードしてくることにします。
$ wget https://dl.google.com/go/go1.14.2.linux-amd64.tar.gz $ tar -xzf go1.14.2.linux-amd64.tar.gz $ export PATH=$PATH:<go/binのパス> $ go version go version go1.14.2 linux/amd64
バージョン1.14.2のGoをインストールすることができました。もう一度Ginをgo get
しましょう。
$ go get github.com/gin-gonic/gin package github.com/gin-gonic/gin: cannot download, $GOPATH must not be set to $GOROOT. For more details see: 'go help gopath'
ダメみたいですね。
罰としてダンベルの重量も10から14に上げておきます。
GOPATHの沼
先ほどのエラーメッセージを見ると、GOPATHなるものをGOROOTと同じものに設定しているせいでGinがダウンロードできないということがわかります。それぞれについて調べてみると、GOROOTはgoを配置したディレクトリ、GOPATHは外部パッケージのリソースを保存するディレクトリとして扱われるということが分かりました。そこで、インストール済みのgoを/usr/local/goへ移し、以下のパスを通しておくことにしました。
export GOROOT="/usr/local/go" export GOPATH="/home/vagrant/go" export PATH="$PATH:$GOROOT/bin"
その後、$go get github.com/gin-gonic/gin
を実行すると…
$ ls $GOPATH/src/github.com/ gin-contrib gin-gonic go-playground golang leodido mattn ugorji
無事、Ginのインストールに成功しました。
Webサーバを立ち上げてみる(2回目)
ようやくGinを使ったコードを実行するところまで来ました。先ほどのexample.go
を改めて実行します。
$ go run example.go [GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached. [GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production. - using env: export GIN_MODE=release - using code: gin.SetMode(gin.ReleaseMode) [GIN-debug] GET /ping --> main.main.func1 (3 handlers) [GIN-debug] Listening and serving HTTP on :8000 [GIN] 2020/04/13 - 04:55:57 | 404 | 708ns | 192.168.33.1 | GET "/" [GIN] 2020/04/13 - 04:56:01 | 200 | 87.586µs | 192.168.33.1 | GET "/ping"
おっ…
つまずきのまとめ
Goをインストールして実際にプログラムを動かすまでに以下のポイントがありました 。
- Ginはバージョン1.11以上が必要
(1.11未満はSameSite属性に対応していないようなので他のフレームワークも同じかも) - GOPATHとGOROOTの設定
(パッケージ管理ツールでインストールした場合は不要っぽい)
公式のドキュメントをしっかり読んでおけば陥らなかった失敗ですが、おかげで日本語でまとめる機会ができました。同じようなポイントでつまずいたGo入門者の方のお役に立てれば幸いです。
おまけ
Cookie
過去の診断でCookieの属性不備を指摘する際、その対策方法について調査する機会がありました。Go (フレームワークなし)ではCookieの属性は明示的に宣言しない限り無効になっています。今のところセキュリティ会社っぽい要素がないので、おまけとしてCookieの操作方法を共有します。
ドキュメントより、"net/http"パッケージで
type Cookie struct { Name string Value string Path string // optional Domain string // optional Expires time.Time // optional RawExpires string // for reading cookies only // MaxAge=0 means no 'Max-Age' attribute specified. // MaxAge<0 means delete cookie now, equivalently 'Max-Age: 0' // MaxAge>0 means Max-Age attribute present and given in seconds MaxAge int Secure bool HttpOnly bool SameSite SameSite // Go 1.11 Raw string Unparsed []string // Raw text of unparsed attribute-value pairs }
が定義されているので、このCookieの構造体を宣言する際に以下のように初期化します。
test_cookie := &http.Cookie{ Name: "test", Value: "hoge", Secure: true, HttpOnly: true, }
これにより、test_cookieという名前のCookieのSecure属性とHttpOnly属性が有効になります。それぞれの意味については割愛しますが、HttpOnly属性を有効にする意味については先日投稿されたXSSについてのブログが参考になるかと思います。
techblog.securesky-tech.com
参考
go-tour-jp.appspot.com
Goの基本を勉強できるWebサイトです。
オススメ。
Documentation - The Go Programming Language
公式のドキュメントが正解なので、かなりお世話になります。
ドキュメント - Go 言語
↑が日本語に翻訳されてました。
まだよく見ていないので未翻訳の箇所があるかもしれません。
おわりに
今回はGoをインストールして簡単なコードを実行するところで終わってしまいました。機会があれば、Goでこんなことできるよ!という記事も共有していけたらと思います。