我使用YAML和MySQL驱动程序编写了一个简单的go程序,旨在提供一个简单的实用程序来更新数据库,而不会将用户名和密码凭据暴露给执行程序的用户。< / p>
(我很清楚我也可以用Python或其他一些脚本语言编写它,并使用sudo管理权限授权,但我想在这里尝试不同的方法,我自己的启发。)
在构建程序之后,我使用了chgrp sys dbcreds.yaml && chmod 0640 dbcreds.yaml
和chgrp sys ./myprog && chmod g+s ./myprog
(作为root)......一切似乎都有效。 (我还测试了在setGID步骤之前访问被拒绝的情况。)
我还测试了strace
结果被拒绝(因为它应该是)。 (为了好玩,我还运行了ltrace -S
;这是在Linux下。正如预期的那样,我没有看到很多正常的libc函数调用...通过我很惊讶看到一些 pthread _.... ()和该列表中的一个 malloc()调用。我想GO运行时确实会链接到一些系统库函数。)
我的问题:这样安全吗?是否有任何已知的方法可以使Go程序(例如下面的内容)在读取这些私有凭据后进行核心转储或暴露其内存?在我读完凭证后,有没有办法放弃我的SGID权限? Go二进制文件上是否有任何SUID / SGID漏洞利用示例?有更好的方法吗?
另一个注意事项:我发现 gopkg.in/yaml.v2 语义有点令人不安。在我的YAML文件中,我有类似的内容:
---
user me
pw mypassword
但在我的代码中,我必须使用 用户 和 Pw (大写)而不是使用小写正如我所料。我认为这是Goyaml的作者的实施决定。是这样吗?
#!go
package main
import (
"fmt"
"database/sql"
_ "github.com/go-sql-driver/mysql"
"gopkg.in/yaml.v2"
"io/ioutil"
"os"
"strconv"
)
type Creds struct {
User string
Pw string
}
func main() {
filename := "./dbcreds.yaml"
var creds Creds
conf, err := ioutil.ReadFile(filename)
if err != nil {
panic(err)
}
err = yaml.Unmarshal(conf, &creds)
if err != nil {
panic(err)
}
var arg1 int
arg1, err = strconv.Atoi(os.Args[1])
if err != nil {
panic(err.Error()) // Just for example purpose. You should use proper error handling instead of panic
}
fmt.Println("arg1: ", arg1, "\n")
dsn := fmt.Sprintf("%s:%s@/mydatabase", creds.User, creds.Pw)
db, err := sql.Open("mysql", dsn)
if err != nil {
panic(err.Error())
}
defer db.Close()
err = db.Ping()
if err != nil {
panic(err.Error())
}
stmtOut, err := db.Prepare("SELECT quant FROM c WHERE id >= ?")
if err != nil {
panic(err.Error())
}
defer stmtOut.Close()
rows, err := stmtOut.Query(arg1)
if err != nil {
panic(err.Error())
}
defer rows.Close()
for rows.Next() {
var quant int
err = rows.Scan(&quant)
if err != nil {
panic(err.Error())
}
fmt.Println(quant)
}
}
答案 0 :(得分:4)
setuid / setgid Go程序是相当安全的,有一个主要的警告。 Go setuid / setgid程序通常不会比C / C ++ setuid / setgid程序更安全,也不会更安全。
你可以通过运行环境变量GOTRACEBACK = crash然后向它发送信号来强制Go程序转储核心。但是,这可以用于您的目的,因为Go程序将(尝试)通过发送自身SIGABRT信号来创建核心转储。内核不会为由信号杀死的setuid / setgid程序生成核心转储。
Go的主要警告是,在GNU / Linux系统上,您无法回退到原始用户ID。这是因为在GNU / Linux上为多线程程序实现了setuid(以及setgid,setgroups,setreuid,setregid,setresuid和setresgid)。详细信息位于http://golang.org/issue/1435。
在最后的注释中,Uw和Pw需要大写,因为标准反映包不允许写入未导出的字段。