目录:
一、MySQL
二、Redis
本章节我们选取了关系型数据库的代表MySQL和NoSQL的代表Redis为例。
一、MySQL
对MySQL的操作用到了第三方包github.com/go-sql-driver/mysql
,我本地随便找了个dvwa的库作为演示,首先是数据库连接和查询功能,查询当前库中user表,预设了两个用户:
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
)
func main() {
db, err := sql.Open("mysql", "root:root@tcp(127.0.0.1:3306)/dvwa?charset=utf8")
if err!=nil {
fmt.Println(err.Error())
return
}
rows, err := db.Query("select *from user ")
var user string
var password string
if err!=nil {
fmt.Println(err.Error())
return
}
for rows.Next() {
rows.Scan(&user,&password)
fmt.Println(user,password)
}
}
#输出
rabbit rabbit
carrot carrot
这里顺便插一句,上面的代码虽然实现了功能,但是我们没有考虑到查询结果为空的情况,我们顺便对错误处理进行一下优化:
func main() {
db, err := sql.Open("mysql", "root:root@tcp(127.0.0.1:3306)/dvwa?charset=utf8")
checkErr(err)
rows, err := db.Query("select *from user ")
var user string
var password string
checkErr(err)
for rows.Next() {
err=rows.Scan(&user,&password)
checkErr(err)
fmt.Println(user,password)
}
}
func checkErr(err error) {
if err != nil {
fmt.Println(err.Error())
return
}
}
然后尝试进行单条查询:
func main() {
db, err := sql.Open("mysql", "root:root@tcp(127.0.0.1:3306)/dvwa?charset=utf8")
checkErr(err)
var user string
var password string
err=db.QueryRow("select * from user where user=?","rabbit").Scan(&user,&password)
checkErr(err)
fmt.Println(user,password)
}
#输出
rabbit rabbit
然后是新增用户,即数据插入操作。因为要用到主键,重新设计了下user表,其中用到的LastInsertId()
方法从字面上应该也好理解,最新插入的数据id。
func main() {
db, err := sql.Open("mysql", "root:root@tcp(127.0.0.1:3306)/dvwa?charset=utf8")
checkErr(err)
sqlStr := "insert into user(user, password) values (?,?)"
res,err := db.Exec(sqlStr, "rabbitmask", "123456")
checkErr(err)
newid,err:=res.LastInsertId()
checkErr(err)
fmt.Println("插入成功,新增ID为:",newid)
}
#输出
插入成功,新增ID为: 4
- 预处理
这里再补充一点,之前在python代码审计的文章中我提到过为了避免SQL注入大家可以尽可能多的使用参数化查询,其实上面的数据插入样例就是参数化查询。然而Go语言提供了预处理功能,安全性更高。
普通SQL语句执行过程:
客户端对SQL语句进行占位符替换得到完整的SQL语句。
客户端发送完整SQL语句到MySQL服务端
MySQL服务端执行完整的SQL语句并将结果返回给客户端。预处理执行过程:
把SQL语句分成两部分,命令部分与数据部分。
先把命令部分发送给MySQL服务端,MySQL服务端进行SQL预处理。
然后把数据部分发送给MySQL服务端,MySQL服务端对SQL语句进行占位符替换。
MySQL服务端执行完整的SQL语句并将结果返回给客户端。
Go语言中使用Prepare方法会先将sql语句发送给MySQL服务端,返回一个准备好的状态用于之后的查询和命令,返回值可以同时执行多个查询和命令。
我们接下来以数据更新为例加入预处理功能:
func main() {
db, err := sql.Open("mysql", "root:root@tcp(127.0.0.1:3306)/dvwa?charset=utf8")
checkErr(err)
sqlStr := "update user set password = ? where user = ?"
stmt, err := db.Prepare(sqlStr)
checkErr(err)
defer stmt.Close()
res,err := stmt.Exec("666666", "rabbitmask")
checkErr(err)
setid,err:=res.RowsAffected()
checkErr(err)
fmt.Println("影响的行数为:",setid)
}
#输出
影响的行数为: 1
最后!删除操作:
func main() {
db, err := sql.Open("mysql", "root:root@tcp(127.0.0.1:3306)/dvwa?charset=utf8")
checkErr(err)
sqlStr := "delete from user where user = ?"
stmt, err := db.Prepare(sqlStr)
checkErr(err)
res,err := stmt.Exec("rabbitmask")
checkErr(err)
setid,err:=res.RowsAffected()
checkErr(err)
fmt.Println("影响的行数为:",setid)
}
#输出
影响的行数为: 1
二、Redis
关于其第三方包的选择GitHub给的排行很清晰:github.com/gomodule/redigo
首先拉起redis服务后,对服务器进行连接:
import (
"fmt"
"github.com/gomodule/redigo/redis"
)
func main() {
conn,err := redis.Dial("tcp","127.0.0.1:6379")
checkErr(err)
fmt.Println("Redis 连接成功")
defer conn.Close()
}
func checkErr(err error) {
if err != nil {
fmt.Println(err.Error())
return
}
}
#输出
Redis 连接成功
然后尝试执行redis set/get命令:
func main() {
conn,err := redis.Dial("tcp","127.0.0.1:6379")
checkErr(err)
fmt.Println("Redis 连接成功")
defer conn.Close()
_, err = conn.Do("SET", "name", "rabbitmask","EX","600")
checkErr(err)
name, err := redis.String(conn.Do("GET", "name"))
checkErr(err)
fmt.Println(name)
}
#输出
Redis 连接成功
rabbitmask
再或者你想用到渗透工作中,执行redis系统命令:
func main() {
conn,err := redis.Dial("tcp","127.0.0.1:6379")
checkErr(err)
fmt.Println("Redis 连接成功")
defer conn.Close()
info, err := redis.String(conn.Do("info", "Replication"))
checkErr(err)
fmt.Println(info)
}
#输出
Redis 连接成功
# Replication
role:master
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
没有太多可拓展的,更多的操作参见redis相关学习。
网友评论