本教程介绍了 Go 中模糊测试的基础知识.通过模糊测试,随机数据会针对您的测试运行,以尝试找出漏洞或导致崩溃的输入.可以通过模糊测试发现的一些漏洞示例包括 SQL 注入、缓冲区溢出、拒绝服务和跨站点脚本攻击.
在本教程中,您将为一个简单的函数编写一个模糊测试,运行 go 命令,并调试和修复代码中的问题.
首先,为您要编写的代码创建一个文件夹.
①.、打开命令提示符并切换到您的主目录.
在 Linux 或 Mac 上:
在 Windows 上:
运行go mod init命令,为其提供新代码的模块路径.
此时此刻呢,您将添加一些简单的代码来反转字符串,稍后我们将对其进行模糊测试.
在此步骤中,您将添加一个函数来反转字符串.
独立程序(与库相反)始终位于 package 中main.
此函数将接受string,使用byte进行循环 ,并在最后返回反转的字符串.
此函数将运行一些Reverse操作,然后将输出打印到命令行.这有助于查看运行中的代码,并可能有助于调试.
e.该main函数使用 fmt 包,所以呢您需要导入它.
第一行代码应如下所示:
从包含 main.go 的目录中的命令行,运行代码.
可以看到原来的字符串,反转它的结果,然后再反转它的结果,就相当于原来的了.
现在代码正在运行,是时候测试它了.
在这一步中,您将为Reverse函数编写一个基本的单元测试.
b.将以下代码粘贴到 reverse_test.go 中.
这个简单的测试将断言列出的输入字符串将被正确反转.
使用运行单元测试go test
此时此刻呢,您将单元测试更改为模糊测试.
单元测试有局限性,即每个输入都必须由开发人员添加到测试中.模糊测试的一个好处是它可以为您的代码提供输入,并且可以识别您提出的测试用例没有达到的边缘用例.
在本节中,您将单元测试转换为模糊测试,这样您就可以用更少的工作生成更多的输入!
请注意,您可以将单元测试、基准测试和模糊测试保存在同一个 *_test.go 文件中,但对于本示例,您将单元测试转换为模糊测试.
Fuzzing 也有一些限制.在您的单元测试中,您可以预测Reverse函数的预期输出,并验证实际输出是否满足这些预期.
例如,在测试用例Reverse("Hello, world")中,单元测试将返回指定为"dlrow ,olleH".
模糊测试时,您无法预测预期输出,因为您无法控制输入.
但是,Reverse您可以在模糊测试中验证函数的一些属性.在这个模糊测试中检查的两个属性是:
(1)将字符串反转两次保留原始值
注意单元测试和模糊测试之间的语法差异:
随着单元测试转换为模糊测试,是时候再次运行测试了.
a.在不进行模糊测试的情况下运行模糊测试,以确保种子输入通过.
如果您在该文件中有其他测试,您也可以运行go test -run=FuzzReverse,并且您只想运行模糊测试.
b.运行FuzzReverse模糊测试,查看是否有任何随机生成的字符串输入会导致失败.这是使用go test新标志-fuzz执行的.
语料库文件的第一行表示编码版本.以下每一行代表构成语料库条目的每种类型的值.由于 fuzz target 只需要 1 个输入,所以呢版本之后只有 1 个值.
c.运行没有-fuzz标志的go test; 新的失败种子语料库条目将被使用:
由于我们的测试失败,是时候调试了.
不能吧,现在用C语言搞开发都是用在更加底层的开发中,更加底层的开发需要高运行效率,go语言的运行效率是比不上C语言的,所以go语言在这些方面还无法取代C语言.应该说是各有所长吧,go和C都有它们自己的适用环境,谁也不能取代谁.
学好一门编程语言是十分不容易的,但是如果学会了,它的实用性是很强的,下面我为大家整理了学好一门编程语言的办法,大家可以参考借鉴.
如何学好一门编程语言?
第一段:多总结
多总结才能加深理解、增强记忆.举例,Go 中有 slice、map、channal 类型,它们都可以用 make 生成实例,但 slice 和 map 还可以用以下形式初始化,也是编程规范中建议的初始化方式:
colors := map[string]string{}
slice := []int{}
但注意了,channal 则没有这样的语法:msg := chan string{}
上面两句是生成实例,表示空集合,但下面两句则表示实例不存在,值为 nil
var colors map[string]string
var slice []int
另外,结构体指针 slice 还可以象下面这样初始化,结构体实例不用明确地指定类型(使用了类型推导)、不用明确地取地址运算().
type Product struct {
name string
}
看到没有,如果不经常总结,这一圈学下来会把你整的稀里糊涂的.
第二段:多比较
学一门新语言一定要与你之前已经熟悉的语言经常作比较,找出它们的相同与不同,这样才能加深记忆和理解,否则学完之后脑子里会一片混乱,搞不清谁是谁非了.
就拿数组来说吧,在 Java、Scala、Go 中定义、实例化、赋值是不一样的.
//Java
int[] arr;//定义数组,不可以指定数组长度
//Scala
//Go
再比如 Map 在 Scala 与 Go 语言里定义、初始化、访问也是不同的,作了以下比较后印象会非常深刻,把它们记下来,这样以后使用就不会搞混了.
val capital = Map("France" - "Paris", "Japan" - "Tokyo")
println(capital.get("France"))
capital := map[string]string{"France": "Paris", "Japan": "Tokyo"}
fmt.Println(capital["France"])
Go 同时给多个变量赋值在 Scala 里可以用模式匹配做到,如下:
//Scala(使用样本类的模式匹配)
case class Tao(name: String, age: Int);
println(myName)
println(myAge)
fmt.Println(myName)
fmt.Println(myAge)
//Scala(使用元组的模式匹配)
println(myNumber)
println(myString)
fmt.Println(myNumber)
fmt.Println(myString)
以下是 Scala 和 Go 定义和实现函数的区别:
val increase: Int = Int = (x: Int) = x + 1
var increase func(int) int = func(x int) int { return x + 1 }
除了在 Scala 和 Go 里都可以类型推导外,在 Scala 里还可以这样定义函数:
val increase = (_: Int) + 1
为方便自己将来随时查阅,可以建立下面这样的对比表格,描述不一定要求规范,自己能看懂就行.
第三段:转变思维方式,
学会用这门语言去思考
学会用语言去思考是关键.如果你以前是学 C 的,转学 Java,你一定要改变以前面向过程的思维,学会用面向对象的思维去分析问题;以前学 Java 的,转学 Scala 则要学会用函数式的编程思维解决问题.
举一个函数式编程的例子,以下是 Java 语言常用的 for 循环,循环变量从 1 到 10 执行 10 次循环体:
// 命令式编程
for (int i = 1; i 10; i++) {
// 此处是循环体做10次
这被称为命令式编程 (Imperative Programming),但学了 Scala 的函数式编程 (Functional Programming) 后,解决同样的问题,我们可以换一种思维:构建 1 到 10 的列表序列,针对列表中的+每个元素分别执行函数,如下:
//函数式编程
val autoList = (1 to 10).map(i = /*此处是函数体,针对1到10的每一个分别调用 1次*/)
已经习惯了 Java 编程的,对 Scala 的函数式编程、样本类、模式匹配、不可变对象、隐式转换等需要一个逐步适应的过程,要渐渐学会用它们思考和解决问题.
再举个 Scala 与 Go 思维方式不同的例子,要实现对一个字符串里的每个字符加 1 的操作,Scala 里可以这样:
"abc".map(cc = cc + 1)
"abc"是一个字符串对象,调用它的方法 map,这是纯面向对象的思维,但在 Go 里就要转变为面向过程的思维:
name := "abc"
second := strings.Map(func(x rune) rune {
return x + 1
}, name)
注意,这里的 strings 是包 (package),调用它的公共函数 Map,被人操作的对象 name 字符串作为函数参数传入.Go 提供的函数 len、cap、append、 等其实都是面向过程的,虽然 Go 也提供有面向对象的支持,已经习惯了面向对象编程的,刚开始学 Go 语言需要特别留意这一点.
第四段:多看开源代码
func main() {
defer profile.Start().Stop()
...
这个意思是指刚进入程序时执行 Start( ) 函数,程序退出前调用 Stop( ) 函数,非常好的技巧啊!可以用于需要在程序执行前和程序完成后分别执行一段逻辑的场景.再看 Start( ) 函数是怎么实现的:
func Start(options ...func(*Profile)) interface {
Stop()
} {
return prof
该函数返回了一个实现了含有 Stop( ) 函数接口的对象,如此才能在调用 Start 调用后连调 Stop.
第五段:优先学会使用代码分析工具
代码分析的工具包括静态检查、测试、测试覆盖率分析、性能分析(内存、CPU)、调试工具等,工具的价值在于它可以有效帮我们发现代码问题,这在我们刚开始学一门编程语言时意义尤其重大.
例如,以下这句 Java 赋值语句估计没有哪本教科书会告诉你有性能问题:
String sb = new String("Hello World");
以下这段 Java 代码你也不一定能意识到有多线程问题:
synchronized public void send(authuserPacket pkt, Thread t, String flowNo) throws IOException
{
logger.info("start");
//连接不可用,直接抛出异常,等待接收线程连接服务器成功
if (!this.avaliable)
try
... ...
如果我们及时用 FindBugs 工具检查就会发现上面这些问题,进而你会去分析研究为什么,如此,你对这门语言的了解也会越来越多.
另外,Go 语言自带的 vet/test/cover/pprof/trace 都是非常有用的工具,一边学一边使用这些工具分析代码,能加深对语言的理解.
第六段:多练习、多实践
就象学自然语言一样,如果只知道语法不去练是没有任何效果的,只有反复地练习,慢慢才能变成自己的一项技能.书本上的例子代码最好能从头到尾亲自敲一遍,多运行、多尝试,另外再找一些题目来练习,如能有机会参与项目开发则更好啦,勤动手、勤实践是最好的学习方法.
其它的方法还有:
做好笔记,把学习中遇到的关键点和自己的思考记下来,便于后面复习和对比;
复习,学习一定要重复、重复、再重复;
学习贵在坚持,每天学一点(比如坚持每天学 1 小时),日积月累.
①.、学习曲线
它包含了类C语法、GC内置和工程工具.这一点非常重要,因为Go语言容易学习,所以一个普通的大学生花一个星期就能写出来可以上手的、高性能的应用.在国内大家都追求快,这也是为什么国内Go流行的原因之一.
Go拥有接近C的运行效率和接近PHP的开发效率,这就很有利的支撑了上面大家追求快速的需求.
Go语言可以说是开发效率和运行效率二者的完美融合,天生的并发编程支持.Go语言支持当前所有的编程范式,包括过程式编程、面向对象编程以及函数式编程.
这包括互联网应用、系统编程和网络编程.Go里面的标准库基本上已经是非常稳定,特别是我这里提到的三个,网络层、系统层的库非常实用.
我相信这一点是很多人选择Go的最大理由,因为部署太方便,所以现在也有很多人用Go开发运维程序.
它包含降低心智的并发和简易的数据同步,我觉得这是Go最大的特色.之所以写正确的并发、容错和可扩展的程序如此之难,是因为我们用了错误的工具和错误的抽象,Go可以说这一块做的相当简单.
Go拥有强大的编译检查、严格的编码规范和完整的软件生命周期工具,具有很强的稳定性,稳定压倒一切.那么为什么Go相比于其他程序会更稳定呢?这是因为Go提供了软件生命周期的各个环节的工具,如go
tool、gofmt、go test.
作为一个测试,作为一个测试开发, 全栈化+管理 是我们未来的发展方向.已经掌握了Java、Python、HTML的你,是不是也想了解下最近异常火爆的Go语言呢?来吧,让我们一起了解下.
Go 是一个开源的编程语言 ,它能让构造简单、可靠且高效的软件变得容易.
此时此刻呢说说 Go语言的特色 :
简洁、快速、安全
并行、有趣、开源
内存管理、数组安全、编译迅速
Go语言的用途 :
Go 语言被设计成一门应用于搭载 Web 服务器,存储集群或类似用途的巨型中央服务器的系统编程语言.
对于高性能分布式系统领域而言,Go 语言无疑比大多数其它语言有着更高的开发效率.它提供了海量并行的支持,这对于 游戏 服务端的开发而言是再好不过了.
Go语言的环境安装:
建议直接打开 官方地址因为墙的原因打不开
因为我用的是windows系统,这里主要讲下Windows系统上使用Go语言来编程.
默认情况下 .msi 文件会安装在 c:Go 目录下.你可以将 c:Gobin 目录添加到 Path 环境变量中.添加后你需要重启命令窗口才能生效.个人建议还是安装到 Program Files文件夹中.
使用什么开发工具来对Go语言进行编写:
建议参考网页:
解决vscode中golang插件安装失败方法
在学习go的过程中,使用的是vscode,但是一直提示安装相关插件失败,然后上网查方法,基本上是叫你建立golang.org目录什么的,结果全是错的,而且都是抄袭,很烦.无意之中看到一位博主分享的方法,他也是饱受上述的垃圾博文困扰,然后找到了解决方法,这里向他致敬,秉着让更多人看到正确解决方法的心,我写下正确的解决方法,希望对你有所帮助,也可以点开原博主链接参考:
Go有一个全球模块代理,设置代理再去安装golang的插件,就可以安装成功了.步骤有,首先Windows用户打开Powershell,一个蓝色的界面,注意不是cmd!不知道的直接打开window下面的搜索,然后输入powershell,搜索出来就可以了.
$env:GO111MODULE="on"
$env:GOPROXY=""
go env -w GOPROXY=
go env -w GOPRIVATE=*.corp.example.com
然后我们打开VsCode界面,下面会提示安装插件,我们选择Install ALL,就会安装成功
当你在运行Go语言程序时,提示所有的插件包都已经安装成功了时,就可以正常使用了,要不然一堆报错会让你非常心烦.
好了,今天先到这里,晚安、下班~
语料库文件以特殊格式编码.这是种子语料库和生成语料库的相同格式.
下面是一个语料库文件的例子:
第一行用于通知模糊引擎文件的编码版本.虽然目前没有计划未来版本的编码格式,但设计必须支持这种可能性.
下面的每一行都是构成语料库条目的值,如果需要,可以直接复制到 Go 代码中.
指定您自己的种子语料库值的最简单方法是使用该 (*testing.F).Add方法.在上面土嘎嘎给出的例子源码中,它看起来像这样:
要使用此工具:
语料库条目:语料库 中的一个输入,可以在模糊测试时使用.这可以是特殊格式的文件,也可以是对 (*testing.F).Add.
覆盖指导: 一种模糊测试方法,它使用代码覆盖范围的扩展来确定哪些语料库条目值得保留以备将来使用.
失败的输入:失败的输入是一个语料库条目,当针对 模糊目标运行时会导致错误或恐慌.
fuzz target: 模糊测试的目标功能,在模糊测试时对语料库条目和生成的值执行.它通过将函数传递给 (*testing.F).Fuzz实现.
fuzz test: 测试文件中的一个被命名为func FuzzXxx(*testing.F)的函数,可用于模糊测试.
fuzzing: 一种自动化测试,它不断地操纵程序的输入,以发现代码可能容易受到的错误或漏洞等问题.
fuzzing arguments: 将传递给 模糊测试目标的参数,并由mutator进行变异.
fuzzing engine: 一个管理fuzzing的工具,包括维护语料库、调用mutator、识别新的覆盖率和报告失败.
生成的语料库: 由模糊引擎随时间维护的语料库,同时模糊测试以跟踪进度.它存储在$GOCACHE/fuzz 中.这些条目仅在模糊测试时使用.
mutator: 一种在模糊测试时使用的工具,它在将语料库条目传递给模糊目标之前随机操作它们.
package: 同一目录下编译在一起的源文件的集合.
种子语料库: 用户提供的用于模糊测试的语料库,可用于指导模糊引擎.它由 f.Add 在模糊测试中调用提供的语料库条目以及包内 testdata/fuzz/{FuzzTestName} 目录中的文件组成.这些条目默认使用go test运行,无论是否进行模糊测试.
测试文件: 格式为 xxx_test.go 的文件,可能包含测试、基准、示例和模糊测试.
漏洞: 代码中的安全敏感漏洞,可以被攻击者利用.