teler-waf 是一个面向基于 Go 的 Web 应用程序的综合性安全解决方案。它作为一个 HTTP 中间件,提供了将 teler IDS 的 IDS 功能集成到现有 Go 应用程序中的易用接口。通过使用 teler-waf,您可以有效防御多种基于 Web 的攻击,例如跨站脚本(XSS)和 SQL 注入。
该包自带标准的 net/http.Handler,便于集成到应用程序的路由中。当客户端请求访问由 teler-waf 保护的路由时,请求会首先通过 teler IDS 进行检测,以识别已知的恶意模式。如果未检测到恶意模式,请求才会被放行进行后续处理。
除了提供针对 Web 攻击的防护外,teler-waf 还能提升应用程序的整体安全性和完整性。它具有高度可配置性,可根据应用程序的具体需求进行定制。
另请参阅:
teler-waf 提供了一系列强大的功能,旨在增强 Go Web 应用程序的安全性:
总的来说,teler-waf 为基于 Go 的 Web 应用程序提供了全面的安全解决方案,有助于防御基于 Web 的攻击,并提升应用程序的整体安全性和完整性。
依赖项:
要在 Go 应用程序中安装 teler-waf,请运行以下命令以下载并安装 teler-waf 包:```console go get github.com/teler-sh/teler-waf
## 使用方法
> [!WARNING]
> **弃用通知**:威胁排除(`Excludes`)将在即将发布的版本(**v2**)中弃用。请参见 [#73](https://github.com/teler-sh/teler-waf/discussions/73) 和 [#64](https://github.com/teler-sh/teler-waf/issues/64)。
以下是在 Go 应用程序中使用 teler-waf 的示例:
1. 在你的 Go 代码中导入 teler-waf 包:```go
import "github.com/teler-sh/teler-waf"
New 函数创建 Teler 类型的新实例。该函数接受多种可选参数,可用于配置 teler-waf 以适配应用的特定需求。```go
waf := teler.New()3. 使用 `Teler` 实例的 `Handler` 方法创建一个 `net/http.Handler`。该处理器随后可在应用程序的 HTTP 路由中使用,以便将 teler-waf 的安全措施应用于特定路由。```go
handler := waf.Handler(http.HandlerFunc(yourHandlerFunc))
handler,以便对特定路由应用 teler-waf 的安全措施。```go
http.Handle("/path", handler)就这样!你已经在你的Go应用中配置了teler-waf。
**选项:**
要获取可用于自定义teler-waf的选项列表,请参见[`teler.Options`](https://pkg.go.dev/github.com/teler-sh/teler-waf#Options)结构体。
### 示例
以下是一个如何自定义teler-waf的选项和规则的示例:```go
// main.go
package main
import (
"net/http"
"github.com/teler-sh/teler-waf"
"github.com/teler-sh/teler-waf/request"
"github.com/teler-sh/teler-waf/threat"
)
var myHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// This is the handler function for the route that we want to protect
// with teler-waf's security measures.
w.Write([]byte("hello world"))
})
var rejectHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// This is the handler function for the route that we want to be rejected
// if the teler-waf's security measures are triggered.
http.Error(w, "Sorry, your request has been denied for security reasons.", http.StatusForbidden)
})
func main() {
// Create a new instance of the Teler type using the New function
// and configure it using the Options struct.
telerMiddleware := teler.New(teler.Options{
// Exclude specific threats from being checked by the teler-waf.
Excludes: []threat.Threat{
threat.BadReferrer,
threat.BadCrawler,
},
// Specify whitelisted URIs (path & query parameters), headers,
// or IP addresses that will always be allowed by the teler-waf
// with DSL expressions.
Whitelists: []string{
`request.Headers matches "(curl|Go-http-client|okhttp)/*" && threat == BadCrawler`,
`request.URI startsWith "/wp-login.php"`,
`request.IP in ["127.0.0.1", "::1", "0.0.0.0"]`,
`request.Headers contains "authorization" && request.Method == "POST"`
},
// Specify file path or glob pattern of custom rule files.
CustomsFromRule: "/path/to/custom/rules/**/*.yaml",
// Specify custom rules for the teler-waf to follow.
Customs: []teler.Rule{
{
// Give the rule a name for easy identification.
Name: "Log4j Attack",
// Specify the logical operator to use when evaluating the rule's conditions.
Condition: "or",
// Specify the conditions that must be met for the rule to trigger.
Rules: []teler.Condition{
{
// Specify the HTTP method that the rule applies to.
Method: request.GET,
// Specify the element of the request that the rule applies to
// (e.g. URI, headers, body).
Element: request.URI,
// Specify the pattern to match against the element of the request.
Pattern: `\$\{.*:\/\/.*\/?\w+?\}`,
},
},
},
{
// Give the rule a name for easy identification.
Name: `Headers Contains "curl" String`,
// Specify the conditions that must be met for the rule to trigger.
Rules: []teler.Condition{
{
// Specify the DSL expression that the rule applies to.
DSL: `request.Headers contains "curl"`,
},
},
},
},
// Specify the file path to use for logging.
LogFile: "/tmp/teler.log",
})
// Set the rejectHandler as the handler for the telerMiddleware.
telerMiddleware.SetHandler(rejectHandler)
// Create a new handler using the handler method of the Teler instance
// and pass in the myHandler function for the route we want to protect.
app := telerMiddleware.Handler(myHandler)
// Use the app handler as the handler for the route.
http.ListenAndServe("127.0.0.1:3000", app)
}
关于如何使用 teler-waf 或将其与任何框架集成的更多示例,请查看 examples/ 目录。
[!TIP] 如果你想探索配置、深入定制规则和编写 DSL 表达式,可以使用 teler WAF 游乐场 进行练习并获得实践经验。在这里,你还可以模拟定制化的请求,以满足应用程序的特定需求。
要将自定义规则集成到 teler-waf 中间件中,你有两个选择:Customs 和 CustomsFromFile。这些选项提供了灵活性,可以创建你自己的安全检查,或覆盖 teler-waf 提供的默认检查。
Customs 选项你可以直接使用 Customs 选项定义自定义规则,如上方的示例所示。
在 Customs 选项中,你提供一个 teler.Rule 结构数组。每个 teler.Rule 代表一个自定义规则,具有唯一的名称和一个指定规则中各条件如何评估(or 或 and)的条件。该规则由一个或多个 teler.Condition 结构组成,每个结构定义一个要检查的具体条件。条件可以基于 HTTP 方法、元素(headers、body、URI 或 any)以及要匹配的正则表达式模式或 DSL 表达式。
CustomsFromFile 选项或者,CustomsFromFile 选项允许你从外部文件加载自定义规则,提供更大的灵活性和可管理性。这些规则可以以 YAML 格式定义,每个文件包含一个或多个规则。以下是一个表示自定义规则的 YAML 结构示例:```yaml
> [!IMPORTANT]
> 请注意,`condition`、`method` 和 `element` 是可选参数。它们的默认值如下:`condition` 设置为 **or**,`method` 设置为 **ALL**,`element` 设置为 **ANY**。因此,如果需要,你可以将这些参数留空。`pattern` 参数是必填的,除非你指定了一个 `dsl` 表达式。在这种情况下,当提供了 `dsl` 表达式时,teler-waf 将忽略分配给 `method` 和 `element` 的任何值,即使它们已被定义。要查看一些示例,请参阅 [`tests/rules/`](https://github.com/teler-sh/teler-waf/tree/master/tests/rules/valid) 目录。
你可以通过指定实际文件路径或 glob 模式来设置 `CustomsFromFile` 选项,指向你的自定义规则文件的位置。例如:```go
// Create a new instance of the Teler middleware and
// specify custom rules with the CustomsFromFile option.
telerMiddleware := teler.New(teler.Options{
CustomsFromFile: "/path/to/custom/rules/**/*.yaml",
})
使用 CustomsFromFile 时,您需提供自定义规则文件所在的文件路径或 glob 模式。该模式可包含通配符以匹配多个文件,或匹配一个目录及其子目录。每个文件中应包含一个或多个以正确 YAML 格式定义的自定义规则。
通过使用 Customs、CustomsFromFile 或两者结合,您可以无缝地将自定义规则集成到 teler-waf 中间件中,从而增强其安全能力以满足特定需求。
DSL(领域特定语言)表达式提供了一种强大的方式来定义条件,用于在自定义规则或白名单的上下文中评估传入请求。借助 DSL 表达式,您可以基于传入请求的不同属性创建复杂且有针对性的条件。以下是一些 DSL 表达式代码的示例:
检查传入请求的头部是否包含 "curl":```sql request.Headers contains "curl"
检查传入的请求方法是否为"GET":```sql
request.Method == "GET"
检查传入的请求方法是否为"GET"或"POST",使用正则表达式和matches操作符:```sql
request.Method matches "^(POS|GE)T$"
检查传入请求的IP地址是否来自localhost:```sql
request.IP in ["127.0.0.1", "::1", "0.0.0.0"]
检查请求中的任何元素是否包含字符串"foo":```console one(request.ALL, # contains "foo")
检查传入请求体是否包含"foo":```sql
request.Body contains "foo"
检查当前正在分析的威胁类别是否为恶意爬虫或目录暴力枚举:```sql threat in [BadCrawler, DirectoryBruteforce]
这些示例展示了 DSL 表达式的表达能力,使你能够基于各种请求属性定义复杂的条件。通过利用这些表达式,你可以有效定义评估传入请求的标准,并据此定制自定义规则或白名单,从而实现对应用程序行为的精细控制。
#### 可用变量
使用 DSL 表达式时,你可以访问多种变量,这些变量提供了关于传入请求和被分析威胁类别的宝贵信息。以下是可用变量的详细说明:
- **威胁类别**
所有 `threat.Threat` 类型的常量标识符都可以作为有效变量使用。这些标识符代表与分析相关的不同威胁类别。
- **`request`**
`request` 变量表示传入的请求,并提供对其字段及对应值的访问。`request` 变量下包含以下子变量:
- `request.URI`:表示传入请求的 URI,包括路径、查询参数、参数和片段。
- `request.Headers`:表示传入请求的标头,以多行形式呈现。
- `request.Body`:表示传入请求的正文。
- `request.Method`:表示传入请求的方法。
- `request.IP`:表示与传入请求关联的客户端 IP 地址。
- `request.ALL`:以切片形式表示上述请求字段中的所有字符串值。
- **`threat`**
`threat` 变量表示正在分析的威胁类别。它属于 `threat.Threat` 类型,允许你基于与请求相关的具体威胁类别进行评估和决策。
通过在 DSL 表达式中利用这些变量,你可以有效访问和操作传入请求的属性,并评估相关的威胁类别。这使得你能够创建针对特定用例定制的自定义规则条件<!--以及白名单-->。
#### 可用函数
此外,你还可以使用多种函数。这些函数既包括 expr 包提供的[内置函数](https://expr.medv.io/docs/Language-Definition#built-in-functions),也包括 DSL 包中专门定义的函数。这些函数利用了 Go 内置 `strings` 包提供的功能。以下是可用函数的详细列表:
- `cidr`:获取指定 CIDR 范围内的所有 IP 地址。
- `clone`:创建字符串的副本。
- `containsAny`:检查字符串是否包含任何指定的子串。
- `equalFold`:以不区分大小写的方式比较两个字符串。
- `hasPrefix`:检查字符串是否具有指定的前缀。
- `hasSuffix`:检查字符串是否具有指定的后缀。
- `join`:使用指定的分隔符连接多个字符串。
- `repeat`:将字符串重复指定的次数。
- `replace`:替换字符串中的子串。
- `replaceAll`:替换字符串中所有出现的子串。
- `request`:在 DSL 表达式中访问请求特定信息。
- `threat`:访问与被分析威胁类别相关的信息。
- `title`:将字符串转换为标题大小写。
- `toLower`:将字符串转换为小写。
- `toTitle`:将字符串转换为标题大小写。
- `toUpper`:将字符串转换为大写。
- `toValidUTF8`:将字符串转换为有效的 UTF-8 编码字符串。
- `trim`:去除字符串首尾的空白字符。
- `trimLeft`:去除字符串开头的空白字符。
- `trimPrefix`:去除字符串的指定前缀。
- `trimRight`:去除字符串末尾的空白字符。
- `trimSpace`:去除字符串首尾空白,并合并字符串内部的连续空白。
- `trimSuffix`:去除字符串的指定后缀。
关于运算符和内置函数的更多详细信息,请参阅 [Expr 文档](https://expr.medv.io/docs/Getting-Started)。该文档提供了关于在 DSL 表达式中使用运算符和探索可用内置函数的全面指南。
### 简化配置管理
为了有效配置,需要定义一系列设置,包括白名单、自定义规则定义、日志偏好及其他参数。[`option`](https://pkg.go.dev/github.com/teler-sh/teler-waf/option) 包简化了这一配置工作流,使你能够高效地将 JSON 和 YAML 格式的配置数据解组或加载为 teler-waf 能够理解并应用的格式。```go
// Load configuration from a YAML file.
opt, err := option.LoadFromYAMLFile("/path/to/teler-waf.conf.yaml")
if err != nil {
panic(err)
}
// Create a new instance of the Teler type with
// the loaded options.
telerMiddleware := teler.New(opt)
默认情况下,teler-waf 会缓存所有传入请求 15 分钟,并且每 20 分钟清除一次缓存,以提升性能。然而,如果你仍在自定义设置以匹配应用程序的需求,可以在开发期间通过将开发模式选项设置为 true 来禁用缓存。这将防止传入请求被缓存,并且有助于调试。```go
// Create a new instance of the Teler type using
// the New function & enable development mode option.
telerMiddleware := teler.New(teler.Options{
Development: true,
})
### 日志
以下是 teler-waf 在请求中检测到威胁时日志行的大致示例:```json
{"level":"warn","ts":1672261174.5995026,"msg":"bad crawler","id":"654b85325e1b2911258a","category":"BadCrawler","caller":"teler-waf","listen_addr":"127.0.0.1:36267","request":{"method":"GET","path":"/","ip_addr":"127.0.0.1:37702","headers":{"Accept":["*/*"],"User-Agent":["curl/7.81.0"]},"body":""}}
{"level":"warn","ts":1672261175.9567692,"msg":"directory bruteforce","id":"b29546945276ed6b1fba","category":"DirectoryBruteforce","caller":"teler-waf","listen_addr":"127.0.0.1:36267","request":{"method":"GET","path":"/.git","ip_addr":"127.0.0.1:37716","headers":{"Accept":["*/*"],"User-Agent":["X"]},"body":""}}
{"level":"warn","ts":1672261177.1487508,"msg":"Detects common comment types","id":"75412f2cc0ec1cf79efd","category":"CommonWebAttack","caller":"teler-waf","listen_addr":"127.0.0.1:36267","request":{"method":"GET","path":"/?id=1%27%20or%201%3D1%23","ip_addr":"127.0.0.1:37728","headers":{"Accept":["*/*"],"User-Agent":["X"]},"body":""}}
id 是当请求被 teler-waf 拒绝时生成的唯一标识符。它包含在请求的 HTTP 响应头(X-Teler-Req-Id)中,可用于排查发往网站的请求所遇到的问题。
例如,如果对网站的请求返回 HTTP 错误状态码(例如 403 Forbidden),则可以使用 teler 请求 ID 来标识导致该错误的特定请求,并帮助排查问题。
teler 请求 ID 由 teler-waf 用于跟踪对其 Web 应用程序发出的请求,并且对于调试和分析网站的流量模式非常有用。
默认情况下,teler-waf 使用 DefaultHTMLResponse 作为请求被拒绝或阻止时的标准响应。然而,teler-waf 提供了高度的自定义能力,使您能够根据特定需求定制响应。这种自定义可以通过使用 Status、HTML 或 HTMLFile 选项来实现,这些选项都属于 Response 接口。
以下是在代码中使用这些选项的方法:```go // Create a new instance of the Teler middleware telerMiddleware := teler.New(teler.Options{ // Customize the response for rejected requests Response: teler.Response{ Status: 403, HTML: "Your request has been denied for security reasons. Ref ID: {{ID}}.", // Alternatively, you can use HTMLFile to point to a custom HTML file HTMLFile: "/path/to/custom-403.html", }, })
通过这种自定义程度,您可以构建个性化且信息丰富的响应,并在 teler-waf 阻止或拒绝请求时显示这些响应。`HTML` 选项允许您直接以字符串形式指定所需的 HTML 内容,而 `HTMLFile` 选项则允许您引用包含自定义 HTML 响应的外部文件。
此外,为提升用户体验,您可以在 HTML 内容中利用占位符生成动态元素。在运行时,这些占位符将被替换为实际值,从而生成更具上下文相关性的响应。目前支持和可用的占位符包括:
* `{{ID}}`:请求 ID,可用于唯一标识每个被拒绝的请求。
* `{{message}}`:拒绝消息,说明请求被阻止的原因。
* `{{threat}}`:威胁类别,提供有关检测到的安全威胁的信息。
通过使用这些占位符,您可以创建详细且全面的响应,有效传达请求被拒绝或阻止的理由。
### Falco Sidekick
[Falco Sidekick](https://github.com/falcosecurity/falcosidekick) 是一款工具,用于接收来自 Falco(一个开源的云原生运行时安全项目)的事件,并将这些事件发送到不同的输出通道。它允许您将安全告警转发到各种第三方系统,例如 Slack、Elasticsearch、Loki、Grafana、Datadog 以及[更多](https://github.com/falcosecurity/falcosidekick#outputs)。这使安全团队能够实时高效地监控和响应安全威胁及事件。
将 Falco Sidekick 与 teler-waf 集成也是可行的。通过使用 `FalcoSidekickURL` 选项,您可以配置 teler-waf 将事件发送到 Falco Sidekick,后者会为您接收并处理这些事件。为此,只需使用 `New` 函数创建一个新的 `Teler` 类型实例,并提供 `FalcoSidekickURL` 选项,填入您的 Falco Sidekick 实例 URL。例如:```go
// Create a new instance of the Teler type using
// the New function & integrate Falco Sidekick.
telerMiddleware := teler.New(teler.Options{
FalcoSidekickURL: "http://localhost:2801",
})
一旦您完成此集成设置,teler-waf 检测到的任何威胁都将发送到 Falco Sidekick,然后 Falco Sidekick 可根据您配置的设置执行相应操作。例如,您可以配置 Falco Sidekick 自动向事件响应团队发送警报。
转发到 Falco Sidekick 实例的事件包含以下信息:
output:表示警报消息。priority:优先级始终标记为 warning,暗示与安全事件相关的紧急性。rule:指示匹配关联请求的具体规则(消息)。time:事件的生成时间戳。teler.caller:标识调用 teler-waf 的应用程序源。teler.id:表示被拒绝请求的唯一标识符。teler.threat:指定威胁的类别。teler.listen_addr:指示 teler-waf 监听传入请求的网络地址。request.body:包含关联请求的请求体。request.headers:列出关联请求的请求头。request.ip_addr:显示关联请求的 IP 地址。request.method:说明关联请求使用的 HTTP 方法。总之,Falco Sidekick 是一款多功能工具,可帮助您自动化安全响应流程,提升整体安全态势。通过利用其能力,您可以确保云原生应用程序的安全并防范潜在威胁。
您可以通过将 teler WAF 日志集成到 Wazuh 来增强安全监控。为此,请使用 extras/ 目录中提供的自定义规则。
在本地配置文件的 ossec_config 元素内添加以下 localfile 元素块:```xml
<ossec_config>
通过这样做,Wazuh 将能够读取和分析 teler WAF 日志,从而增强你的网络防护并提供更好的洞察。
teler-waf 包利用一个威胁数据集来识别和分析每个传入请求,以发现潜在的安全威胁。该数据集每天更新,这意味着你将始终拥有最新的资源。数据集在首次启动时存储在用户级缓存目录中 (在 Unix 系统上,如果 $XDG_CACHE_HOME/teler-waf 非空,则返回该路径,如 XDG 基础目录规范 所指定;否则返回 $HOME/.cache/teler-waf。在 Darwin 上,返回 $HOME/Library/Caches/teler-waf。在 Windows 上,返回 %LocalAppData%/teler-waf。在 Plan 9 上,返回 $home/lib/cache/teler-waf)。后续启动将使用缓存的 dataset,而不是再次下载。
[!NOTE] 威胁数据集来源于 teler-sh/teler-resources 仓库。
然而,可能存在你希望禁用威胁数据集自动更新的情况。例如,你可能网络连接缓慢或有限,或者使用的机器对文件访问有限制。在这些情况下,你可以将名为 NoUpdateCheck 的选项设置为 true,这将阻止 teler-waf 自动更新数据集。
[!CAUTION] 启用
InMemory具有优先权,并确保自动更新保持启用状态。```go // Create a new instance of the Teler type using the New // function & disable automatic updates to the threat dataset. telerMiddleware := teler.New(teler.Options{ NoUpdateCheck: true, })
最后,在某些情况下,可能需要将威胁数据集加载到内存中,而非保存到用户级缓存目录。当你运行的应用或服务部署在无发行版或运行时镜像上,且文件访问可能受限或缓慢时,这一点尤为实用。在此场景下,你可以设置一个名为 **InMemory** 的选项为 `true`,从而将威胁数据集加载到内存中,实现更快的访问速度。```go
// Create a new instance of the Teler type using the
// New function & enable in-memory threat datasets store.
telerMiddleware := teler.New(teler.Options{
InMemory: true,
})
[!CAUTION] 这可能会消耗更多系统资源,因此在做出此决定前权衡利弊是值得的。
如果您发现安全问题,请立即告知他们,我们非常重视安全!
如果您有关于安全问题的信息,或 teler-waf 软件包中的漏洞,并且/或者您能够成功执行跨站脚本(XSS)并在我们的演示网站上弹出警报(请参阅资源),请不要提交公开问题——相反,请通过漏洞报告表单私下发送您的报告。
以下是使用 teler-waf 的一些局限性:
> [!NOTE]
> 基准测试结果可能有所不同,且可能不一致。自那时起,[teler-resources](https://github.com/teler-sh/teler-resources) 数据集可能已有所增加,这可能会影响结果。
- **配置复杂性**:配置 teler-waf 以适应应用程序的特定需求可能较为复杂,且可能需要一定的 Web 安全专业知识。这可能会使不熟悉应用防火墙和 IDS 系统的人难以正确设置和使用 teler-waf。
- **保护有限**:teler-waf 并非完美的安全解决方案,可能无法防御所有可能的攻击类型。与任何安全系统一样,定期监控和维护 teler-waf 以确保其提供所需保护级别至关重要。
#### 已知问题
要查看 teler-waf 的已知问题列表,请通过 [“已知问题”标签](https://github.com/teler-sh/teler-waf/issues?q=is%3Aopen+is%3Aissue+label%3Aknown-issue) 筛选问题。
## 社区
我们使用 Google Groups 作为专用邮件列表。订阅 [teler-announce](https://groups.google.com/g/teler-announce)(通过 [[email protected]](mailto:[email protected]))以获取重要公告,例如新版本的发布。订阅后,您将及时了解与 [teler IDS](https://github.com/teler-sh/teler)、[teler WAF](https://github.com/teler-sh/teler-waf)、[teler Proxy](https://github.com/teler-sh/teler-proxy)、[teler Caddy](https://github.com/teler-sh/teler-caddy) 和 [teler Resources](https://github.com/teler-sh/teler-resources) 相关的重大进展。
任何 [咨询](https://github.com/teler-sh/teler-waf/discussions/categories/q-a)、[讨论](https://github.com/teler-sh/teler-waf/discussions) 或 [问题](https://github.com/teler-sh/teler-waf/issues) 均在 GitHub 上跟踪。我们在此积极管理和处理社区参与的相关事宜。
## 许可协议
本软件包采用双重许可:[Apache License 2.0](https://github.com/teler-sh/teler-waf/blob/HEAD/LICENSE-APACHE) 和 [Elastic License 2.0 (ELv2)](https://github.com/teler-sh/teler-waf/blob/HEAD/LICENSE-ELASTIC)(适用于主包 **teler**)。
您可以在组织内部自由使用它来保护您的应用程序。但是,您不得使用主包创建云服务、托管服务或管理服务,也不得用于任何商业目的,除非您为此获得商业许可——但目前尚不提供此选项。如果您有兴趣获取此商业许可,用于 [ELv2](https://github.com/teler-sh/teler-waf/blob/HEAD/LICENSE-ELASTIC) 未授权的用途,请联系 **@dwisiswant0**。
teler-waf 及其所有贡献版权 © 归 Dwi Siswanto 2022-2024 所有。
request.path:指关联请求的路径。