nextjs-secure 0.6.0 → 0.7.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 +185 -1
- package/dist/bot.cjs +1521 -0
- package/dist/bot.cjs.map +1 -0
- package/dist/bot.d.cts +567 -0
- package/dist/bot.d.ts +567 -0
- package/dist/bot.js +1484 -0
- package/dist/bot.js.map +1 -0
- package/dist/index.cjs +1511 -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 +1479 -2
- package/dist/index.js.map +1 -1
- package/package.json +14 -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
|
|
|
@@ -52,6 +52,7 @@ pnpm add nextjs-secure
|
|
|
52
52
|
- [Authentication](#authentication)
|
|
53
53
|
- [Input Validation](#input-validation)
|
|
54
54
|
- [Audit Logging](#audit-logging)
|
|
55
|
+
- [Bot Detection](#bot-detection)
|
|
55
56
|
- [Utilities](#utilities)
|
|
56
57
|
- [API Reference](#api-reference)
|
|
57
58
|
- [Examples](#examples)
|
|
@@ -800,6 +801,170 @@ const structuredFormatter = new StructuredFormatter({
|
|
|
800
801
|
|
|
801
802
|
---
|
|
802
803
|
|
|
804
|
+
## Bot Detection
|
|
805
|
+
|
|
806
|
+
Protect your endpoints from automated bots, scrapers, and spam.
|
|
807
|
+
|
|
808
|
+
### Basic Usage
|
|
809
|
+
|
|
810
|
+
```typescript
|
|
811
|
+
import { withBotProtection } from 'nextjs-secure/bot'
|
|
812
|
+
|
|
813
|
+
export const POST = withBotProtection(handler, {
|
|
814
|
+
userAgent: {
|
|
815
|
+
blockAllBots: false,
|
|
816
|
+
allowList: ['Googlebot', 'Bingbot'],
|
|
817
|
+
},
|
|
818
|
+
honeypot: true,
|
|
819
|
+
behavior: {
|
|
820
|
+
maxRequestsPerSecond: 10,
|
|
821
|
+
},
|
|
822
|
+
})
|
|
823
|
+
```
|
|
824
|
+
|
|
825
|
+
### Presets
|
|
826
|
+
|
|
827
|
+
```typescript
|
|
828
|
+
import { withBotProtectionPreset } from 'nextjs-secure/bot'
|
|
829
|
+
|
|
830
|
+
// Relaxed: Only blocks obvious bots
|
|
831
|
+
export const GET = withBotProtectionPreset(handler, 'relaxed')
|
|
832
|
+
|
|
833
|
+
// Standard: Good balance (default)
|
|
834
|
+
export const GET = withBotProtectionPreset(handler, 'standard')
|
|
835
|
+
|
|
836
|
+
// Strict: Maximum protection
|
|
837
|
+
export const GET = withBotProtectionPreset(handler, 'strict')
|
|
838
|
+
|
|
839
|
+
// API: Optimized for API endpoints
|
|
840
|
+
export const GET = withBotProtectionPreset(handler, 'api')
|
|
841
|
+
```
|
|
842
|
+
|
|
843
|
+
### User-Agent Detection
|
|
844
|
+
|
|
845
|
+
```typescript
|
|
846
|
+
import { withUserAgentProtection, analyzeUserAgent, KNOWN_BOT_PATTERNS } from 'nextjs-secure/bot'
|
|
847
|
+
|
|
848
|
+
// Middleware
|
|
849
|
+
export const GET = withUserAgentProtection(handler, {
|
|
850
|
+
blockAllBots: true,
|
|
851
|
+
allowCategories: ['search_engine', 'social_media'],
|
|
852
|
+
allowList: ['Googlebot', 'Twitterbot'],
|
|
853
|
+
blockList: ['BadBot'],
|
|
854
|
+
})
|
|
855
|
+
|
|
856
|
+
// Manual detection
|
|
857
|
+
const result = analyzeUserAgent('Googlebot/2.1')
|
|
858
|
+
// { isBot: true, category: 'search_engine', name: 'Googlebot', confidence: 0.95 }
|
|
859
|
+
```
|
|
860
|
+
|
|
861
|
+
### Honeypot Protection
|
|
862
|
+
|
|
863
|
+
```typescript
|
|
864
|
+
import { withHoneypotProtection, generateHoneypotHTML, generateHoneypotCSS } from 'nextjs-secure/bot'
|
|
865
|
+
|
|
866
|
+
// Middleware
|
|
867
|
+
export const POST = withHoneypotProtection(handler, {
|
|
868
|
+
fieldName: '_hp_email',
|
|
869
|
+
additionalFields: ['_hp_name', '_hp_phone'],
|
|
870
|
+
})
|
|
871
|
+
|
|
872
|
+
// Generate HTML for forms
|
|
873
|
+
const honeypotHTML = generateHoneypotHTML({ fieldName: '_hp_email' })
|
|
874
|
+
// Returns hidden input fields
|
|
875
|
+
|
|
876
|
+
// Generate CSS
|
|
877
|
+
const honeypotCSS = generateHoneypotCSS({ fieldName: '_hp_email' })
|
|
878
|
+
// Returns CSS to hide fields
|
|
879
|
+
```
|
|
880
|
+
|
|
881
|
+
### Behavior Analysis
|
|
882
|
+
|
|
883
|
+
```typescript
|
|
884
|
+
import { withBehaviorProtection, MemoryBehaviorStore } from 'nextjs-secure/bot'
|
|
885
|
+
|
|
886
|
+
const store = new MemoryBehaviorStore()
|
|
887
|
+
|
|
888
|
+
export const GET = withBehaviorProtection(handler, {
|
|
889
|
+
store,
|
|
890
|
+
minRequestInterval: 100, // Min ms between requests
|
|
891
|
+
maxRequestsPerSecond: 10, // Max requests per second
|
|
892
|
+
patterns: {
|
|
893
|
+
sequentialAccess: true, // Detect sequential URL patterns
|
|
894
|
+
regularTiming: true, // Detect bot-like timing
|
|
895
|
+
missingHeaders: true, // Detect missing browser headers
|
|
896
|
+
},
|
|
897
|
+
})
|
|
898
|
+
```
|
|
899
|
+
|
|
900
|
+
### CAPTCHA Integration
|
|
901
|
+
|
|
902
|
+
```typescript
|
|
903
|
+
import { withCaptchaProtection, verifyCaptcha } from 'nextjs-secure/bot'
|
|
904
|
+
|
|
905
|
+
// reCAPTCHA v3
|
|
906
|
+
export const POST = withCaptchaProtection(handler, {
|
|
907
|
+
provider: 'recaptcha-v3',
|
|
908
|
+
siteKey: process.env.RECAPTCHA_SITE_KEY,
|
|
909
|
+
secretKey: process.env.RECAPTCHA_SECRET_KEY,
|
|
910
|
+
threshold: 0.5,
|
|
911
|
+
})
|
|
912
|
+
|
|
913
|
+
// hCaptcha
|
|
914
|
+
export const POST = withCaptchaProtection(handler, {
|
|
915
|
+
provider: 'hcaptcha',
|
|
916
|
+
siteKey: process.env.HCAPTCHA_SITE_KEY,
|
|
917
|
+
secretKey: process.env.HCAPTCHA_SECRET_KEY,
|
|
918
|
+
})
|
|
919
|
+
|
|
920
|
+
// Cloudflare Turnstile
|
|
921
|
+
export const POST = withCaptchaProtection(handler, {
|
|
922
|
+
provider: 'turnstile',
|
|
923
|
+
siteKey: process.env.TURNSTILE_SITE_KEY,
|
|
924
|
+
secretKey: process.env.TURNSTILE_SECRET_KEY,
|
|
925
|
+
})
|
|
926
|
+
|
|
927
|
+
// Manual verification
|
|
928
|
+
const result = await verifyCaptcha(token, {
|
|
929
|
+
provider: 'recaptcha-v3',
|
|
930
|
+
secretKey: process.env.RECAPTCHA_SECRET_KEY,
|
|
931
|
+
})
|
|
932
|
+
```
|
|
933
|
+
|
|
934
|
+
### Manual Bot Detection
|
|
935
|
+
|
|
936
|
+
```typescript
|
|
937
|
+
import { detectBot } from 'nextjs-secure/bot'
|
|
938
|
+
|
|
939
|
+
const result = await detectBot(request, {
|
|
940
|
+
userAgent: { blockAllBots: true },
|
|
941
|
+
honeypot: true,
|
|
942
|
+
behavior: { maxRequestsPerSecond: 10 },
|
|
943
|
+
})
|
|
944
|
+
|
|
945
|
+
if (result.isBot) {
|
|
946
|
+
console.log(`Bot detected: ${result.reason}`)
|
|
947
|
+
console.log(`Category: ${result.category}`)
|
|
948
|
+
console.log(`Confidence: ${result.confidence}`)
|
|
949
|
+
}
|
|
950
|
+
```
|
|
951
|
+
|
|
952
|
+
### Bot Categories
|
|
953
|
+
|
|
954
|
+
| Category | Examples |
|
|
955
|
+
|----------|----------|
|
|
956
|
+
| `search_engine` | Googlebot, Bingbot, Yandex |
|
|
957
|
+
| `social_media` | Twitterbot, FacebookBot, LinkedInBot |
|
|
958
|
+
| `ai_crawler` | GPTBot, Claude-Web, Anthropic |
|
|
959
|
+
| `monitoring` | UptimeRobot, Pingdom |
|
|
960
|
+
| `feed_reader` | Feedly, Feedbin |
|
|
961
|
+
| `preview` | Slackbot, Discord |
|
|
962
|
+
| `scraper` | Scrapy, DataMiner |
|
|
963
|
+
| `spam` | Spam bots, malicious crawlers |
|
|
964
|
+
| `unknown` | Unidentified automated traffic |
|
|
965
|
+
|
|
966
|
+
---
|
|
967
|
+
|
|
803
968
|
## Utilities
|
|
804
969
|
|
|
805
970
|
### Duration Parsing
|
|
@@ -904,6 +1069,24 @@ isLocalhost('127.0.0.1') // true
|
|
|
904
1069
|
| `trackSecurityEvent(store, event)` | Track single event |
|
|
905
1070
|
| `redactObject(obj, config)` | Redact PII from object |
|
|
906
1071
|
|
|
1072
|
+
### Bot Detection
|
|
1073
|
+
|
|
1074
|
+
| Function | Description |
|
|
1075
|
+
|----------|-------------|
|
|
1076
|
+
| `withBotProtection(handler, config)` | Combined bot protection |
|
|
1077
|
+
| `withUserAgentProtection(handler, config)` | User-agent only protection |
|
|
1078
|
+
| `withHoneypotProtection(handler, config)` | Honeypot only protection |
|
|
1079
|
+
| `withBehaviorProtection(handler, config)` | Behavior analysis only |
|
|
1080
|
+
| `withCaptchaProtection(handler, config)` | CAPTCHA verification |
|
|
1081
|
+
| `withBotProtectionPreset(handler, preset)` | Use preset configuration |
|
|
1082
|
+
| `detectBot(request, config)` | Manual bot detection |
|
|
1083
|
+
| `analyzeUserAgent(userAgent, config)` | Analyze user-agent string |
|
|
1084
|
+
| `checkHoneypot(request, config)` | Check honeypot fields |
|
|
1085
|
+
| `checkBehavior(request, config)` | Check request behavior |
|
|
1086
|
+
| `verifyCaptcha(token, config)` | Verify CAPTCHA token |
|
|
1087
|
+
| `generateHoneypotHTML(config)` | Generate honeypot HTML |
|
|
1088
|
+
| `generateHoneypotCSS(config)` | Generate honeypot CSS |
|
|
1089
|
+
|
|
907
1090
|
---
|
|
908
1091
|
|
|
909
1092
|
## Examples
|
|
@@ -1008,6 +1191,7 @@ export async function GET(req) {
|
|
|
1008
1191
|
- [x] **v0.4.0** - Authentication
|
|
1009
1192
|
- [x] **v0.5.0** - Input Validation
|
|
1010
1193
|
- [x] **v0.6.0** - Audit Logging
|
|
1194
|
+
- [x] **v0.7.0** - Bot Detection
|
|
1011
1195
|
|
|
1012
1196
|
See [ROADMAP.md](ROADMAP.md) for detailed progress and future plans.
|
|
1013
1197
|
|