SideCI TechBlog

SideCIを作っているアクトキャットのエンジニアによる技術ブログです。


gas のルールを見ていこうの会

あけましておめでとうございます。 id:Pocke です。

GAS(Go AST Scanner) という、Go言語用のセキュリティ検査ツールがあります。
この記事では、そのツールに含まれるルールを洗い出してみました。

記事のルールは2017/01/05現在のものになります。
https://github.com/GoASTScanner/gas/tree/d4f9b88cbf6b9f190a2425120c9cecc466ec363b

コードナンバー

GASの各ルールにはG101の様なコードナンバーが付与されています。
この番号にはそれぞれ以下のような意味があります。

  • G1xx : misc
  • G2xx : injection
  • G3xx : filesystem
  • G4xx : crypto
  • G5xx : blacklist

では、このルールをひとつづつ見ていきましょう。

G101: Look for hardcoded credentials

クレデンシャル情報をハードコードしてあるものを検出します。

package samples
import "fmt"
func main() {
  username := "admin"
  password := "admin"
  fmt.Println("Doing something with: ", username, password)
}

passwd, pass, password, pwd, secret, token と言ういずれかの名前の変数への代入がある場合に警告を出します。

なお、このパターンは設定で変更できる模様です(未検証)。 See. https://github.com/GoASTScanner/gas/blob/d4f9b88cbf6b9f190a2425120c9cecc466ec363b/rules/hardcoded_credentials.go#L78-L80

G102: Bind to all interfaces

全てのネットワークインターフェイスに対してバインドしているコードを検出します。

package main

import (
    "log"
    "net"
)

func main() {
    l, err := net.Listen("tcp", ":2000")
    if err != nil {
        log.Fatal(err)
    }
    defer l.Close()
}

net.Listen("tcp", ":2000")net.Listen("tcp", "0.0.0.0:2000")と等価であり、この様に記述すると全てのネットワークインターフェイスからのアクセスを許容する様になります。

これにより同じネットワークに繋がった他のPCからのアクセスを許すことになってしまうため、特に公衆無線LANなどに繋いでいる場合は開発中のアプリケーションに外部からアクセスされてしまうため危険です。
net.Listen("tcp", "127.0.0.1")の様に明示的にローカルホストからのアクセスのみを許容する様に変更することで、これを防ぐことが可能です。

Rubyist ではれば Rack server の変更が記憶に新しいかも知れません。
https://github.com/rack/rack/commit/28b014484a8ac0bbb388e7eaeeef159598ec64fc

G103: Audit the use of unsafe block

unsafe package の関数の呼び出しを検出します。

G104: Audit errors not checked

エラーチェックをしていないコードを検出します。

os.Mkdir("/foo", 0700)  // os.Mkdir は error を返すが、この例ではそれをチェックしていないため警告が出る
fmt.Println("foo") // Println のようなエラーチェックを行わないのが一般的な関数に対しては警告は出ない

G201: SQL query construction using format string

SQLのクエリをfmt.Sprintfで組み立てているコードを検出します。

q := fmt.Sprintf("SELECT * FROM foo where name = '%s'", name)

なお、組み立てた文字列を実際にSQLとして使用しているかは見ていないため、上記のqfmt.Println(q)の様に使用している場合でもgasは警告を出すfalse positiveがあります。

G202: SQL query construction using string concatenation

SQLのクエリを、文字列を連結することで組み立てているコードを検出します。

q := "SELECT * FROM foo where name = '%s'" + name

先程のルールと似ており、また同様のfalse positiveがあります。

G203: Use of unescaped data in HTML templates

テンプレートでHTMLを組み立てる際、外部から渡ってきた値がエスケープされているかを検査します。

t := template.Must(template.New("ex").Parse(tmpl))
v := map[string]interface{}{
  "Title":    "Test <b>World</b>", // リテラルなので問題はない
  "Body":     template.HTML(a),    // 外部から渡ってきている値がエスケープされずにテンプレートに反映されてしまうため問題
}
t.Execute(os.Stdout, v)

G204: Audit use of command execution

外部プロセスの実行を検査します。

exec.Command("ls")

また、この検査にはconfidenceレベルが3段階あり、

  • コマンド及び引数に、リテラルでない値が使用されている
  • コマンドの実行パスが絶対パスでない
  • その他

の順で指摘の信頼度が高くなっています。

G301: Poor file permissions used when creating a directory

ディレクトリを作る際のパーミッションが余分でないかを検査します。

os.Mkdir("/tmp/mydir", 0777) // 不必要に権限が付いているため問題を指摘する
os.Mkdir("/tmp/mydir", 0700) // 権限が適切に付いているため問題とはしない

デフォルトでは0700より大きい権限が付いていた際に問題を指摘します。

G302: Poor file permisions used with chmod

G301のChmod, OpenFile版です。G301との相違点として、指摘を出すしきい値のデフォルトが0600であることがあります。

G303: Creating tempfile using a predictable path

テンポラリファイルを作る際に、os.Createを使用しているものを検出します。

file1, err := os.Create("/tmp/demo1")

テンポラリファイルを作成する場合は、os.Createではなくioutil.Tempfileを使用するべきです。

G401: Detect the usage of DES, RC4, or MD5

弱い暗号化アルゴリズムを使用しているコードを検出します。

// md5 は脆弱なため、SHA2などの使用を検討しましょう
md5.Sum(bytes)

// DESは脆弱なため、AESなどの使用を検討しましょう。
des.NewCipher([]byte("sekritz"))

G402: Look for bad TLS connection settings

セキュアではないTLSの設定をしているコードを検出します。

tr := &http.Transport{
  // Verify をスキップしてしまっていることを指摘する
  TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}

この他にもいくつかの検出項目があります。

G403: Ensure minimum RSA key length of 2048 bits

RSA鍵の鍵長が2048bit未満である場合を検出します。

pvk, err := rsa.GenerateKey(rand.Reader, 1024)

G404: Insecure random number source (rand)

math/randの使用を検出します。

import (
  crand "crypto/rand"
  mrand "math/rand"
)

crand.Rand(nil) // crypto/rand を使っているため問題はない
mrand.Rand(nil) // math/rand を使っているため警告が出る

Golang公式のドキュメントに

For random numbers suitable for security-sensitive work, see the crypto/rand package. https://golang.org/pkg/math/rand/

と記載があるため、乱数にセキュアさを求める場合(WebアプリケーションのセッションIDなど)であれば、crypto/randを使用するよう修正すべきでしょう。

G501: Import blacklist: crypto/md5
G502: Import blacklist: crypto/des
G503: Import blacklist: crypto/rc4
G504: Import blacklist: net/http/cgi

それぞれのパッケージが脆弱であるなどの理由で、これらのパッケージのインポートをしていないか検出をします。

import (
  "crypto/md5" // MD5の代わりにSHA2などを使用すべきでしょう
)

まとめ

以上がgasが持っているルールになります。
gasを使ってGoアプリケーションのセキュリティを担保してはいかがでしょうか。

また、今回紹介したルールで採用できない物がある場合、gas -exclude=G301,G104 main.go のように-excludeオプションで指定したルールのみを無効に出来るので、ぜひご活用下さい。