Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add replay protection plugin #1672

Open
wants to merge 26 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
d46723d
"feat: add replay protection plugin
yunmaoQu Jan 14, 2025
c0cff2b
Merge branch 'main' into feature/request-replay-protection
yunmaoQu Jan 14, 2025
d894e3d
update
yunmaoQu Jan 15, 2025
9a9e2e4
Merge branch 'alibaba:main' into feature/request-replay-protection
yunmaoQu Jan 15, 2025
9c0f1dc
Merge remote-tracking branch 'origin' into feature/request-replay-pro…
yunmaoQu Jan 15, 2025
506208f
Merge branch 'feature/request-replay-protection' of https://github.co…
yunmaoQu Jan 15, 2025
d7cec7f
update
yunmaoQu Jan 16, 2025
2762111
update
yunmaoQu Jan 16, 2025
ce47411
Merge branch 'main' into feature/request-replay-protection
yunmaoQu Jan 17, 2025
935d1c9
update
yunmaoQu Jan 20, 2025
a6a5af4
Merge branch 'feature/request-replay-protection' of https://github.co…
yunmaoQu Jan 20, 2025
b1e4ddb
Merge branch 'main' into feature/request-replay-protection
yunmaoQu Jan 20, 2025
1cfd691
update
yunmaoQu Jan 21, 2025
8dccbab
Merge branch 'feature/request-replay-protection' of https://github.co…
yunmaoQu Jan 21, 2025
7caad55
Merge branch 'main' into feature/request-replay-protection
yunmaoQu Jan 21, 2025
b694c48
update
yunmaoQu Jan 21, 2025
ed892cf
Merge branch 'feature/request-replay-protection' of https://github.co…
yunmaoQu Jan 21, 2025
50c088d
fix
yunmaoQu Jan 24, 2025
ed33204
fix
yunmaoQu Jan 24, 2025
88504b0
fix
yunmaoQu Jan 30, 2025
6ee8499
fix
yunmaoQu Jan 31, 2025
5707224
fix e2e test
hanxiantao Feb 9, 2025
d1775a1
fix e2e test
hanxiantao Feb 9, 2025
8792448
add TestCaseName
hanxiantao Feb 9, 2025
67d50ea
add TestCaseName
hanxiantao Feb 9, 2025
ab6a397
fix e2e test
yunmaoQu Feb 9, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
223 changes: 223 additions & 0 deletions plugins/wasm-go/extensions/replay-protection/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
---
title: 防重放攻击
keywords: [higress,nonce-protection]
description: 防重放攻击插件配置参考
---

## 简介

Nonce (Number used ONCE) 防重放插件通过验证请求中的一次性随机数来防止请求重放攻击。每个请求都需要携带一个唯一的 nonce 值,服务器会记录并校验这个值的唯一性,从而防止请求被恶意重放。

## 功能说明

- 强制或可选的 nonce 校验
- 基于 Redis 的 nonce 唯一性验证
- 可配置的 nonce 有效期
- nonce 格式和长度校验

## 配置说明

| 配置项 | 类型 | 必填 | 默认值 | 说明 |
|-------------------|------|------|--------|-----|
hanxiantao marked this conversation as resolved.
Show resolved Hide resolved
| force_nonce | bool | 否 | true | 是否强制要求 nonce |
| nonce_ttl | int | 否 | 900 | nonce 有效期(单位:秒) |
| nonce_min_length | int | 否 | 8 | nonce 最小长度 |
| nonce_max_length | int | 否 | 128 | nonce 最大长度 |
| reject_code | int | 否 | 429 | 拒绝请求时的状态码 |
| reject_msg | string | 否 | "Duplicate nonce" | 拒绝请求时的错误信息 |
| redis.serviceName | string | 是 | - | Redis 服务名称 |
| redis.servicePort | int | 否 | 6379 | Redis 服务端口 |
| redis.timeout | int | 否 | 1000 | Redis 操作超时时间(毫秒) |
| redis.keyPrefix | string | 否 | "replay-protection" | Redis key 前缀 |

## 配置示例

```yaml
apiVersion: extensions.higress.io/v1alpha1
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

文档里不用贴wasmPlugin的CR配置,只需要贴插件示例配置即可,因为有的用户不是直接用k8s yaml操作的

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

kind: WasmPlugin
metadata:
name: replay-protection
namespace: higress-system
spec:
defaultConfig:
force_nonce: true
nonce_ttl: 900
nonce_min_length: 8
nonce_max_length: 128
redis:
serviceName: "redis.higress"
servicePort: 6379
timeout: 1000
keyPrefix: "replay-protection"
url: oci://higress-registry.cn-hangzhou.cr.aliyuncs.com/replay-protection:v1.0.0
```

## 使用说明

### 请求头要求

| 请求头 | 是否必须 | 说明 |
|-------|---------|------|
| x-apigw-nonce | 由 force_nonce 配置决定 | 随机生成的 nonce 值,需符合 base64 编码格式 |


### 1. 测试环境配置
hanxiantao marked this conversation as resolved.
Show resolved Hide resolved

```yaml
# test-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: test-api
namespace: default
spec:
rules:
- host: test.example.com
http:
paths:
- path: /api/test
pathType: Prefix
backend:
service:
name: test-service
port:
number: 8080
---
# test-wasmplugin.yaml
apiVersion: extensions.higress.io/v1alpha1
kind: WasmPlugin
metadata:
name: replay-protection
namespace: higress-system
spec:
defaultConfig:
force_nonce: true
nonce_ttl: 60 # 测试时设置短一点,比如60秒
nonce_min_length: 8
nonce_max_length: 128
redis:
serviceName: "redis.higress" # 确保这个 Redis 服务可用
servicePort: 6379
timeout: 1000
keyPrefix: "test-replay-protection"
matchRules:
- ingress:
- default/test-api # 匹配我们的测试 Ingress
url: oci://higress-registry.cn-hangzhou.cr.aliyuncs.com/replay-protection:v1.0.0
```

### 2. 测试脚本

```bash
#!/bin/bash

# 测试 API 地址
API_URL="http://test.example.com/api/test"

# 测试用例1: 正常请求
test_normal_request() {
echo "测试用例1: 正常请求"
nonce=$(openssl rand -base64 32)
echo "使用 nonce: $nonce"

curl -X POST "$API_URL" \
-H "x-apigw-nonce: $nonce" \
-H "Host: test.example.com" \
-d '{"test": "data"}'
echo -e "\n"
}

# 测试用例2: 重放攻击
test_replay_attack() {
echo "测试用例2: 重放攻击"
nonce=$(openssl rand -base64 32)
echo "使用 nonce: $nonce"

# 第一次请求
echo "第一次请求:"
curl -X POST "$API_URL" \
-H "x-apigw-nonce: $nonce" \
-H "Host: test.example.com" \
-d '{"test": "data"}'
echo -e "\n"

# 重放请求
echo "重放请求:"
curl -X POST "$API_URL" \
-H "x-apigw-nonce: $nonce" \
-H "Host: test.example.com" \
-d '{"test": "data"}'
echo -e "\n"
}

# 测试用例3: 无 nonce
test_without_nonce() {
echo "测试用例3: 无 nonce"
curl -X POST "$API_URL" \
-H "Host: test.example.com" \
-d '{"test": "data"}'
echo -e "\n"
}

# 测试用例4: nonce 太短
test_short_nonce() {
echo "测试用例4: nonce 太短"
curl -X POST "$API_URL" \
-H "x-apigw-nonce: abc" \
-H "Host: test.example.com" \
-d '{"test": "data"}'
echo -e "\n"
}

# 运行所有测试
run_all_tests() {
test_normal_request
sleep 2
test_replay_attack
sleep 2
test_without_nonce
sleep 2
test_short_nonce
}

# 执行测试
run_all_tests
```


### 3. 预期结果

1. **正常请求**:
```json
{
"success": true,
"data": "..."
}
```

2. **重放攻击**:
```json
{
"code": 429,
"message": "Request replay detected"
}
```

3. **无 nonce**:
```json
{
"code": 400,
"message": "Missing nonce header"
}
```

4. **nonce 太短**:
```json
{
"code": 400,
"message": "Invalid nonce length"
}
```



88 changes: 88 additions & 0 deletions plugins/wasm-go/extensions/replay-protection/README_EN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
---
title: Nonce Replay Protection
keywords: [higress, replay-protection]
description: replay-protection config example
---


## Introduction

The Nonce (Number used ONCE) replay protection plugin prevents request replay attacks by validating a one-time random number in requests. Each request must carry a unique nonce value, which the server records and validates to prevent malicious request replay.

## Features

- Mandatory or optional nonce validation
- Redis-based nonce uniqueness verification
- Configurable nonce TTL
- Custom error responses
- Nonce format and length validation

## Configuration

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| force_nonce | bool | No | true | Whether to enforce nonce requirement |
| nonce_ttl | int | No | 900 | Nonce validity period (seconds) |
| nonce_min_length | int | No | 8 | Minimum nonce length |
| nonce_max_length | int | No | 128 | Maximum nonce length |

### Redis Configuration

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| serviceName | string | Yes | - | Redis service name |
| servicePort | int | No | 6379 | Redis service port |
| timeout | int | No | 1000 | Redis operation timeout (ms) |
| keyPrefix | string | No | "replay-protection" | Redis key prefix |

## Configuration Example

```yaml
apiVersion: extensions.higress.io/v1alpha1
kind: WasmPlugin
metadata:
name: replay-protection
namespace: higress-system
spec:
defaultConfig:
force_nonce: true
nonce_ttl: 900
nonce_min_length: 8
nonce_max_length: 128
redis:
serviceName: "redis.higress"
servicePort: 6379
timeout: 1000
keyPrefix: "replay-protection"
url: oci://higress-registry.cn-hangzhou.cr.aliyuncs.com/replay-protection:v1.0.0
```

## Usage

### Required Headers

| Header | Required | Description |
|--------|----------|-------------|
| x-apigw-nonce | Depends on force_nonce | Random generated nonce value in base64 format |

### Usage Example

```bash
# Generate nonce
nonce=$(openssl rand -base64 32)

# Send request
curl -X POST 'https://api.example.com/path' \
-H "x-apigw-nonce: $nonce" \
-d '{"key": "value"}'
```

## Error Response

```json
{
"code": 429,
"message": "Duplicate nonce detected"
}
```

1 change: 1 addition & 0 deletions plugins/wasm-go/extensions/replay-protection/VERSION
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1.0.0
20 changes: 20 additions & 0 deletions plugins/wasm-go/extensions/replay-protection/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module github.com/alibaba/higress/plugins/wasm-go/extensions/replay-protection

go 1.21.11

replace github.com/alibaba/higress/plugins/wasm-go => ../..

require (
github.com/alibaba/higress/plugins/wasm-go v1.4.2
github.com/higress-group/proxy-wasm-go-sdk v1.0.0
github.com/tidwall/gjson v1.18.0
)

require (
github.com/google/uuid v1.3.0 // indirect
github.com/higress-group/nottinygc v0.0.0-20231101025119-e93c4c2f8520 // indirect
github.com/magefile/mage v1.14.0 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
github.com/tidwall/resp v0.1.1 // indirect
)
24 changes: 24 additions & 0 deletions plugins/wasm-go/extensions/replay-protection/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/higress-group/nottinygc v0.0.0-20231101025119-e93c4c2f8520 h1:IHDghbGQ2DTIXHBHxWfqCYQW1fKjyJ/I7W1pMyUDeEA=
github.com/higress-group/nottinygc v0.0.0-20231101025119-e93c4c2f8520/go.mod h1:Nz8ORLaFiLWotg6GeKlJMhv8cci8mM43uEnLA5t8iew=
github.com/higress-group/proxy-wasm-go-sdk v1.0.0 h1:BZRNf4R7jr9hwRivg/E29nkVaKEak5MWjBDhWjuHijU=
github.com/higress-group/proxy-wasm-go-sdk v1.0.0/go.mod h1:iiSyFbo+rAtbtGt/bsefv8GU57h9CCLYGJA74/tF5/0=
github.com/magefile/mage v1.14.0 h1:6QDX3g6z1YvJ4olPhT1wksUcSa/V0a1B+pJb73fBjyo=
github.com/magefile/mage v1.14.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/resp v0.1.1 h1:Ly20wkhqKTmDUPlyM1S7pWo5kk0tDu8OoC/vFArXmwE=
github.com/tidwall/resp v0.1.1/go.mod h1:3/FrruOBAxPTPtundW0VXgmsQ4ZBA0Aw714lVYgwFa0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
Loading
Loading