nextjs-secure 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +228 -1
- package/dist/api.cjs +1707 -0
- package/dist/api.cjs.map +1 -0
- package/dist/api.d.cts +708 -0
- package/dist/api.d.ts +708 -0
- package/dist/api.js +1650 -0
- package/dist/api.js.map +1 -0
- package/dist/index.cjs +1674 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1631 -2
- package/dist/index.js.map +1 -1
- package/package.json +11 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
5
|
[](https://www.typescriptlang.org/)
|
|
6
6
|
[](https://nextjs.org/)
|
|
7
|
-
[]()
|
|
8
8
|
|
|
9
9
|
Production-ready security middleware for Next.js 13+ App Router. Zero config, maximum protection.
|
|
10
10
|
|
|
@@ -53,6 +53,7 @@ pnpm add nextjs-secure
|
|
|
53
53
|
- [Input Validation](#input-validation)
|
|
54
54
|
- [Audit Logging](#audit-logging)
|
|
55
55
|
- [Bot Detection](#bot-detection)
|
|
56
|
+
- [API Security](#api-security)
|
|
56
57
|
- [Utilities](#utilities)
|
|
57
58
|
- [API Reference](#api-reference)
|
|
58
59
|
- [Examples](#examples)
|
|
@@ -965,6 +966,210 @@ if (result.isBot) {
|
|
|
965
966
|
|
|
966
967
|
---
|
|
967
968
|
|
|
969
|
+
## API Security
|
|
970
|
+
|
|
971
|
+
Protect your APIs with request signing, replay prevention, and versioning.
|
|
972
|
+
|
|
973
|
+
### Request Signing (HMAC)
|
|
974
|
+
|
|
975
|
+
Sign and verify requests using HMAC to prevent tampering.
|
|
976
|
+
|
|
977
|
+
```typescript
|
|
978
|
+
import { withRequestSigning, generateSignature } from 'nextjs-secure/api'
|
|
979
|
+
|
|
980
|
+
// Server: Verify signed requests
|
|
981
|
+
export const POST = withRequestSigning(handler, {
|
|
982
|
+
secret: process.env.API_SECRET,
|
|
983
|
+
algorithm: 'sha256', // or 'sha512'
|
|
984
|
+
timestampTolerance: 300, // 5 minutes
|
|
985
|
+
})
|
|
986
|
+
|
|
987
|
+
// Client: Sign outgoing requests
|
|
988
|
+
const timestamp = Math.floor(Date.now() / 1000).toString()
|
|
989
|
+
const signature = await generateSignature(request, {
|
|
990
|
+
secret: API_SECRET,
|
|
991
|
+
algorithm: 'sha256',
|
|
992
|
+
})
|
|
993
|
+
|
|
994
|
+
fetch('/api/data', {
|
|
995
|
+
method: 'POST',
|
|
996
|
+
headers: {
|
|
997
|
+
'x-timestamp': timestamp,
|
|
998
|
+
'x-signature': signature,
|
|
999
|
+
},
|
|
1000
|
+
body: JSON.stringify(data),
|
|
1001
|
+
})
|
|
1002
|
+
```
|
|
1003
|
+
|
|
1004
|
+
### Custom Signing Components
|
|
1005
|
+
|
|
1006
|
+
```typescript
|
|
1007
|
+
export const POST = withRequestSigning(handler, {
|
|
1008
|
+
secret: process.env.API_SECRET,
|
|
1009
|
+
components: {
|
|
1010
|
+
method: true,
|
|
1011
|
+
path: true,
|
|
1012
|
+
query: true,
|
|
1013
|
+
body: true,
|
|
1014
|
+
timestamp: true,
|
|
1015
|
+
},
|
|
1016
|
+
signatureHeader: 'x-signature',
|
|
1017
|
+
timestampHeader: 'x-timestamp',
|
|
1018
|
+
})
|
|
1019
|
+
```
|
|
1020
|
+
|
|
1021
|
+
### Replay Prevention
|
|
1022
|
+
|
|
1023
|
+
Prevent replay attacks using nonces.
|
|
1024
|
+
|
|
1025
|
+
```typescript
|
|
1026
|
+
import { withReplayPrevention, MemoryNonceStore, generateNonce } from 'nextjs-secure/api'
|
|
1027
|
+
|
|
1028
|
+
const store = new MemoryNonceStore({ maxSize: 10000, ttl: 300000 })
|
|
1029
|
+
|
|
1030
|
+
// Server: Block replay attacks
|
|
1031
|
+
export const POST = withReplayPrevention(handler, {
|
|
1032
|
+
store,
|
|
1033
|
+
ttl: 300000, // 5 minutes
|
|
1034
|
+
required: true,
|
|
1035
|
+
nonceHeader: 'x-nonce',
|
|
1036
|
+
})
|
|
1037
|
+
|
|
1038
|
+
// Client: Generate unique nonce
|
|
1039
|
+
const nonce = generateNonce(32)
|
|
1040
|
+
fetch('/api/payment', {
|
|
1041
|
+
method: 'POST',
|
|
1042
|
+
headers: { 'x-nonce': nonce },
|
|
1043
|
+
body: JSON.stringify(data),
|
|
1044
|
+
})
|
|
1045
|
+
```
|
|
1046
|
+
|
|
1047
|
+
### Timestamp Validation
|
|
1048
|
+
|
|
1049
|
+
Reject old or future-dated requests.
|
|
1050
|
+
|
|
1051
|
+
```typescript
|
|
1052
|
+
import { withTimestamp, validateTimestamp } from 'nextjs-secure/api'
|
|
1053
|
+
|
|
1054
|
+
export const POST = withTimestamp(handler, {
|
|
1055
|
+
maxAge: 300, // 5 minutes
|
|
1056
|
+
format: 'unix', // 'unix' | 'unix-ms' | 'iso8601'
|
|
1057
|
+
required: true,
|
|
1058
|
+
allowFuture: false,
|
|
1059
|
+
timestampHeader: 'x-timestamp',
|
|
1060
|
+
})
|
|
1061
|
+
|
|
1062
|
+
// Manual validation
|
|
1063
|
+
const result = validateTimestamp(request, { maxAge: 300 })
|
|
1064
|
+
if (!result.valid) {
|
|
1065
|
+
console.log(result.reason) // 'Timestamp too old'
|
|
1066
|
+
}
|
|
1067
|
+
```
|
|
1068
|
+
|
|
1069
|
+
### API Versioning
|
|
1070
|
+
|
|
1071
|
+
Manage API versions with deprecation support.
|
|
1072
|
+
|
|
1073
|
+
```typescript
|
|
1074
|
+
import { withAPIVersion, createVersionRouter, extractVersion } from 'nextjs-secure/api'
|
|
1075
|
+
|
|
1076
|
+
// Single version validation
|
|
1077
|
+
export const GET = withAPIVersion(handler, {
|
|
1078
|
+
current: 'v2',
|
|
1079
|
+
supported: ['v1', 'v2', 'v3'],
|
|
1080
|
+
deprecated: ['v1'],
|
|
1081
|
+
sunset: ['v0'],
|
|
1082
|
+
source: 'header', // 'header' | 'query' | 'path' | 'accept'
|
|
1083
|
+
addDeprecationHeaders: true,
|
|
1084
|
+
})
|
|
1085
|
+
|
|
1086
|
+
// Version-based routing
|
|
1087
|
+
const router = createVersionRouter({
|
|
1088
|
+
v1: v1Handler,
|
|
1089
|
+
v2: v2Handler,
|
|
1090
|
+
v3: v3Handler,
|
|
1091
|
+
}, { default: 'v2' })
|
|
1092
|
+
|
|
1093
|
+
export const GET = router
|
|
1094
|
+
```
|
|
1095
|
+
|
|
1096
|
+
### Idempotency Keys
|
|
1097
|
+
|
|
1098
|
+
Ensure safe retries for payment and critical operations.
|
|
1099
|
+
|
|
1100
|
+
```typescript
|
|
1101
|
+
import { withIdempotency, MemoryIdempotencyStore, generateIdempotencyKey } from 'nextjs-secure/api'
|
|
1102
|
+
|
|
1103
|
+
const store = new MemoryIdempotencyStore({ maxSize: 10000 })
|
|
1104
|
+
|
|
1105
|
+
// Server: Handle idempotent requests
|
|
1106
|
+
export const POST = withIdempotency(handler, {
|
|
1107
|
+
store,
|
|
1108
|
+
ttl: 86400000, // 24 hours
|
|
1109
|
+
required: true,
|
|
1110
|
+
keyHeader: 'idempotency-key',
|
|
1111
|
+
hashRequestBody: true, // Detect body mismatches
|
|
1112
|
+
})
|
|
1113
|
+
|
|
1114
|
+
// Client: Use idempotency key
|
|
1115
|
+
const key = generateIdempotencyKey()
|
|
1116
|
+
fetch('/api/payment', {
|
|
1117
|
+
method: 'POST',
|
|
1118
|
+
headers: { 'idempotency-key': key },
|
|
1119
|
+
body: JSON.stringify(payment),
|
|
1120
|
+
})
|
|
1121
|
+
```
|
|
1122
|
+
|
|
1123
|
+
### Combined API Protection
|
|
1124
|
+
|
|
1125
|
+
Use presets for common security profiles.
|
|
1126
|
+
|
|
1127
|
+
```typescript
|
|
1128
|
+
import { withAPIProtection, withAPIProtectionPreset } from 'nextjs-secure/api'
|
|
1129
|
+
|
|
1130
|
+
// Full configuration
|
|
1131
|
+
export const POST = withAPIProtection(handler, {
|
|
1132
|
+
signing: {
|
|
1133
|
+
secret: process.env.API_SECRET,
|
|
1134
|
+
algorithm: 'sha256',
|
|
1135
|
+
},
|
|
1136
|
+
replay: {
|
|
1137
|
+
store: nonceStore,
|
|
1138
|
+
ttl: 300000,
|
|
1139
|
+
},
|
|
1140
|
+
timestamp: {
|
|
1141
|
+
maxAge: 300,
|
|
1142
|
+
required: true,
|
|
1143
|
+
},
|
|
1144
|
+
versioning: {
|
|
1145
|
+
current: 'v2',
|
|
1146
|
+
supported: ['v1', 'v2'],
|
|
1147
|
+
},
|
|
1148
|
+
idempotency: {
|
|
1149
|
+
store: idempotencyStore,
|
|
1150
|
+
required: true,
|
|
1151
|
+
},
|
|
1152
|
+
})
|
|
1153
|
+
|
|
1154
|
+
// Presets
|
|
1155
|
+
export const POST = withAPIProtectionPreset(handler, 'basic') // Minimal
|
|
1156
|
+
export const POST = withAPIProtectionPreset(handler, 'standard') // Balanced
|
|
1157
|
+
export const POST = withAPIProtectionPreset(handler, 'strict') // Maximum
|
|
1158
|
+
export const POST = withAPIProtectionPreset(handler, 'financial') // Banking/Payment
|
|
1159
|
+
```
|
|
1160
|
+
|
|
1161
|
+
### Preset Comparison
|
|
1162
|
+
|
|
1163
|
+
| Feature | Basic | Standard | Strict | Financial |
|
|
1164
|
+
|---------|-------|----------|--------|-----------|
|
|
1165
|
+
| Signing | ❌ | ❌ | SHA-256 | SHA-512 |
|
|
1166
|
+
| Replay Prevention | ❌ | ✅ (5min) | ✅ (5min) | ✅ (24h) |
|
|
1167
|
+
| Timestamp Validation | ✅ (10min) | ✅ (5min) | ✅ (5min) | ✅ (1min) |
|
|
1168
|
+
| Idempotency | ❌ | ❌ | ✅ (1h) | ✅ (24h) |
|
|
1169
|
+
| Versioning | ❌ | ❌ | ✅ | ✅ |
|
|
1170
|
+
|
|
1171
|
+
---
|
|
1172
|
+
|
|
968
1173
|
## Utilities
|
|
969
1174
|
|
|
970
1175
|
### Duration Parsing
|
|
@@ -1087,6 +1292,27 @@ isLocalhost('127.0.0.1') // true
|
|
|
1087
1292
|
| `generateHoneypotHTML(config)` | Generate honeypot HTML |
|
|
1088
1293
|
| `generateHoneypotCSS(config)` | Generate honeypot CSS |
|
|
1089
1294
|
|
|
1295
|
+
### API Security
|
|
1296
|
+
|
|
1297
|
+
| Function | Description |
|
|
1298
|
+
|----------|-------------|
|
|
1299
|
+
| `withRequestSigning(handler, config)` | HMAC request signing |
|
|
1300
|
+
| `withReplayPrevention(handler, config)` | Nonce-based replay prevention |
|
|
1301
|
+
| `withTimestamp(handler, config)` | Timestamp validation |
|
|
1302
|
+
| `withAPIVersion(handler, config)` | API version validation |
|
|
1303
|
+
| `withIdempotency(handler, config)` | Idempotency key support |
|
|
1304
|
+
| `withAPIProtection(handler, config)` | Combined API protection |
|
|
1305
|
+
| `withAPIProtectionPreset(handler, preset)` | Use preset configuration |
|
|
1306
|
+
| `generateSignature(request, config)` | Generate HMAC signature |
|
|
1307
|
+
| `verifySignature(request, config)` | Verify HMAC signature |
|
|
1308
|
+
| `generateNonce(length)` | Generate secure nonce |
|
|
1309
|
+
| `checkReplay(request, config)` | Check for replay attack |
|
|
1310
|
+
| `validateTimestamp(request, config)` | Validate request timestamp |
|
|
1311
|
+
| `extractVersion(request, config)` | Extract API version |
|
|
1312
|
+
| `createVersionRouter(handlers, config)` | Create version-based router |
|
|
1313
|
+
| `generateIdempotencyKey(length)` | Generate idempotency key |
|
|
1314
|
+
| `checkIdempotency(request, config)` | Check idempotency status |
|
|
1315
|
+
|
|
1090
1316
|
---
|
|
1091
1317
|
|
|
1092
1318
|
## Examples
|
|
@@ -1192,6 +1418,7 @@ export async function GET(req) {
|
|
|
1192
1418
|
- [x] **v0.5.0** - Input Validation
|
|
1193
1419
|
- [x] **v0.6.0** - Audit Logging
|
|
1194
1420
|
- [x] **v0.7.0** - Bot Detection
|
|
1421
|
+
- [x] **v0.8.0** - API Security
|
|
1195
1422
|
|
|
1196
1423
|
See [ROADMAP.md](ROADMAP.md) for detailed progress and future plans.
|
|
1197
1424
|
|