You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Feat: Implement fixes and improvements from security audit (v2)
This commit implements critical fixes, feature enhancements, and documentation updates based on a security audit of the nuxt-api-shield module. This version corrects previous misplacement of utility functions and ensures proper module configuration.
Core Logic & Reliability:
- Fixed flawed rate limit window logic in `src/runtime/server/middleware/shield.ts` to ensure accurate request counting and window resets, preventing bypass of limits.
- IP data is now reset correctly upon ban expiry and when a new ban is applied.
- Created `isActualBanTimestampExpired` utility in `src/runtime/server/utils/` for correctly checking ban expiry.
- Removed the old, misleading `isBanExpired.ts` utility.
Module Configuration & Exports (`src/module.ts`):
- Added new configuration options `ipTTL` (for IP data cleanup TTL) and `security.trustXForwardedFor` (for XFF header trust) to `ModuleOptions` and module defaults.
- Ensured `isActualBanTimestampExpired` and `RateLimit` type are exported via `addServerImports` to be available through `#imports` for user-defined tasks.
- Corrected `defu` strategy for runtime config to align with Nuxt standards.
Cleanup Tasks & Storage Management:
- Updated `playground/server/tasks/shield/cleanBans.ts` and README example to use the core `isActualBanTimestampExpired` utility (via `#imports`) and efficiently process only `ban:` keys.
- Created `playground/server/tasks/shield/cleanIpData.ts` and corresponding README example to clean old IP tracking entries based on the `ipTTL` config, preventing storage bloat.
Security Configuration & Hardening:
- Made `X-Forwarded-For` header trust configurable via `security.trustXForwardedFor` in `nuxtApiShield` options. Middleware now respects this.
Documentation (README.md):
- Extensively updated README:
- Added examples for `shield:cleanBans` and `shield:cleanIpData` tasks.
- Documented new config options: `ipTTL` and `security.trustXForwardedFor`.
- Added "Important Considerations" section: Data Privacy (PII), Storage Security (FS driver, logs), `errorMessage` XSS note.
- Added prominent security warning and guidance for `trustXForwardedFor`.
- Clarified default configuration values.
These changes significantly improve the module's security, reliability, and configurability, with clearer guidance for users.
trustXForwardedFor:true, // Default: true. Whether to trust X-Forwarded-For headers. See warning below.
81
+
}
78
82
},
79
83
});
80
84
```
81
85
86
+
**Default Configuration Values:**
87
+
(These are applied by the module if not specified in your `nuxtApiShield` config)
88
+
```js
89
+
{
90
+
limit: {
91
+
max:12,
92
+
duration:108, // seconds
93
+
ban:3600, // seconds
94
+
},
95
+
delayOnBan:true,
96
+
errorMessage:"Too Many Requests",
97
+
retryAfterHeader:false,
98
+
log: {
99
+
path:"logs", // Logging is disabled if path is empty
100
+
attempts:100, // Logging per IP is disabled if attempts is 0
101
+
},
102
+
routes: [],
103
+
ipTTL:7*24*60*60, // 7 days in seconds
104
+
security: {
105
+
trustXForwardedFor:true,
106
+
}
107
+
}
108
+
```
109
+
110
+
**Security Warning: `trustXForwardedFor`**
111
+
112
+
The `security.trustXForwardedFor` option (default is `true`, set by the module) determines if the module uses the `X-Forwarded-For` HTTP header to identify the client's IP address.
113
+
- If set to `true`: The module will use the IP address provided in the `X-Forwarded-For` header. This is common when your Nuxt application is behind a trusted reverse proxy, load balancer, or CDN (like Nginx, Cloudflare, AWS ELB/ALB) that correctly sets this header with the real client IP.
114
+
-**WARNING:** If `trustXForwardedFor` is `true` and your application is directly internet-facing OR your proxy is not configured to strip incoming `X-Forwarded-For` headers from clients, malicious users can spoof their IP address by sending a fake `X-Forwarded-For` header. This would allow them to bypass rate limits or cause other users to be incorrectly rate-limited.
115
+
- If set to `false`: The module will use the direct IP address of the incoming connection (i.e., `event.node.req.socket.remoteAddress`). Use this setting if your application is directly internet-facing or if you are unsure about your proxy's configuration.
116
+
-**Recommendation:** Only enable `trustXForwardedFor: true` if you are certain your reverse proxy is correctly configured to set this header and strip any client-sent versions of it. Otherwise, set it to `false`.
117
+
82
118
### 3. Add `nitro/storage` to `nuxt.config.ts`
83
119
84
120
You can use any storage you want, but you have to use **shield** as the name of the storage.
@@ -112,7 +148,7 @@ If you use for example redis, you can use the following configuration, define th
112
148
}
113
149
```
114
150
115
-
### 4. Add `shield:clean` to `nuxt.config.ts`
151
+
### 4. Add Cleanup Task(s) to `nuxt.config.ts`
116
152
117
153
```json
118
154
{
@@ -121,37 +157,133 @@ If you use for example redis, you can use the following configuration, define th
121
157
"tasks": true
122
158
},
123
159
"scheduledTasks": {
124
-
"*/15 * * * *": ["shield:clean"] // clean the shield storage every 15 minutes
The `isActualBanTimestampExpired` utility is provided by `nuxt-api-shield` and should be available via `#imports`.
205
+
206
+
#### b) Task for Cleaning Old IP Tracking Data
131
207
132
-
In `server/tasks/shield/clean.ts` you should have something like this.
208
+
This task cleans up IP tracking entries (`ip:xxx.xxx.xxx.xxx`) that haven't been active (i.e., their `time` field hasn't been updated) for a certain period. This period is defined by the `ipTTL` configuration option in your `nuxt.config.ts` (under `nuxtApiShield`), which defaults to 7 days. This cleanup helps prevent your storage from growing indefinitely with IPs that make a few requests but are never banned.
209
+
210
+
In `server/tasks/shield/cleanIpData.ts`:
133
211
134
212
```ts
135
-
importtype { RateLimit } from"#imports";
213
+
importtype { RateLimit } from'#imports'; // Or from 'nuxt-api-shield/types' if made available by the module
214
+
import { useRuntimeConfig } from'#imports';
136
215
137
216
exportdefaultdefineTask({
138
217
meta: {
139
-
description: "Clean expired bans",
218
+
name: 'shield:cleanIpData', // Match the name in scheduledTasks
219
+
description: 'Clean old IP tracking data from nuxt-api-shield storage.',
// Check if entry exists and has a numeric 'time' property
242
+
if (entry&&typeofentry.time==='number') {
243
+
if ((currentTime-entry.time) >ipTTLms) {
244
+
awaitshieldStorage.removeItem(key);
245
+
cleanedCount++;
246
+
}
247
+
} else {
248
+
// Clean up entries that are null, not an object, or missing a numeric 'time'
148
249
awaitshieldStorage.removeItem(key);
250
+
cleanedCount++;
149
251
}
150
-
});
151
-
return { result: keys };
252
+
}
253
+
254
+
console.log(`[nuxt-api-shield] Cleaned ${cleanedCount} old/malformed IP data entries.`);
255
+
return { result: { cleanedCount } };
152
256
},
153
257
});
154
258
```
259
+
Make sure to configure `ipTTL` in your `nuxt.config.ts` under `nuxtApiShield` if you wish to use a value different from the default (7 days). Setting `ipTTL: 0` (or any non-positive number) in your config will disable this cleanup task. The `RateLimit` type should be available via `#imports` if your module exports it or makes it available to Nuxt's auto-import system.
260
+
261
+
## Important Considerations
262
+
263
+
### Data Privacy (IP Address Storage)
264
+
265
+
`nuxt-api-shield` functions by tracking IP addresses to monitor request rates and apply bans. This means IP addresses, which can be considered Personally Identifiable Information (PII) under regulations like GDPR, are stored by the module.
266
+
267
+
-**Data Stored:**
268
+
-`ip:<IP_ADDRESS>`: Stores `{ count: number, time: number }` for tracking request rates.
269
+
-`ban:<IP_ADDRESS>`: Stores a timestamp indicating when a ban on an IP address expires.
270
+
-**Compliance:** Ensure your usage complies with any applicable data privacy regulations. This may involve updating your privacy policy to inform users about this data processing.
271
+
-**Data Retention:**
272
+
- Ban entries are cleaned up by the `shield:cleanBans` task after expiry.
273
+
- IP tracking entries are cleaned up by the `shield:cleanIpData` task based on the `ipTTL` setting.
274
+
275
+
### Storage Security
276
+
277
+
-**Filesystem Driver (`driver: 'fs'`):** If you use the filesystem driver for `unstorage` (e.g., `driver: 'fs'`, `base: '.shield'`), ensure that the storage directory (and the `logs` directory if logging is enabled via `log.path`) is:
278
+
-**Not web-accessible:** Your web server should not be configured to serve files from these directories.
279
+
-**Properly permissioned:** The directories should have appropriate server-side file permissions to prevent unauthorized reading or writing.
280
+
-**Other Drivers (Redis, etc.):** If using database drivers like Redis, ensure your database server itself is secured (e.g., authentication, network access controls).
281
+
282
+
### Error Message (`errorMessage`)
283
+
284
+
The `errorMessage` option in the module configuration is returned in the body of a 429 response.
285
+
- It's recommended to use a plain text message.
286
+
- If you choose to use HTML in your `errorMessage`, ensure your client-side application correctly sanitizes it or renders it in a way that prevents XSS vulnerabilities. The module itself does not sanitize this user-configured message.
0 commit comments