@yognky/premium-security 1.0.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/CHANGELOG.md +39 -0
- package/LICENSE +21 -0
- package/README.md +8 -0
- package/dist/defenses/advanced.d.ts +2 -0
- package/dist/defenses/advanced.js +7 -0
- package/dist/defenses/advenced.d.ts +2 -0
- package/dist/defenses/advenced.js +48 -0
- package/dist/defenses/bruteforce.d.ts +2 -0
- package/dist/defenses/bruteforce.js +43 -0
- package/dist/defenses/curlBot.d.ts +2 -0
- package/dist/defenses/curlBot.js +31 -0
- package/dist/defenses/ddos.d.ts +2 -0
- package/dist/defenses/ddos.js +42 -0
- package/dist/defenses/headers.d.ts +2 -0
- package/dist/defenses/headers.js +25 -0
- package/dist/defenses/malware.d.ts +2 -0
- package/dist/defenses/malware.js +34 -0
- package/dist/defenses/rateLimit.d.ts +2 -0
- package/dist/defenses/rateLimit.js +31 -0
- package/dist/defenses/spoofing.d.ts +2 -0
- package/dist/defenses/spoofing.js +7 -0
- package/dist/defenses/sqlInjection.d.ts +2 -0
- package/dist/defenses/sqlInjection.js +40 -0
- package/dist/defenses/timingAttack.d.ts +2 -0
- package/dist/defenses/timingAttack.js +7 -0
- package/dist/defenses/whitelist.d.ts +2 -0
- package/dist/defenses/whitelist.js +22 -0
- package/dist/defenses/xss.d.ts +2 -0
- package/dist/defenses/xss.js +45 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +98 -0
- package/dist/type/index.d.ts +41 -0
- package/dist/type/index.js +2 -0
- package/dist/types/index.d.ts +27 -0
- package/dist/types/index.js +2 -0
- package/dist/utils/banner.d.ts +3 -0
- package/dist/utils/banner.js +71 -0
- package/dist/utils/helpers.d.ts +11 -0
- package/dist/utils/helpers.js +74 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.js +18 -0
- package/dist/utils/logger.d.ts +44 -0
- package/dist/utils/logger.js +148 -0
- package/docs/API.md +35 -0
- package/docs/EXAMPLE.md +45 -0
- package/docs/INSTALLATTION.md +6 -0
- package/examples/advenced-server.js +59 -0
- package/examples/basic-server.js +33 -0
- package/examples/simple-express.js +21 -0
- package/examples/with-express.ts +33 -0
- package/gitignore +48 -0
- package/nodemon.json +11 -0
- package/npmignore +36 -0
- package/package.json +27 -0
- package/src/defenses/advanced.ts +6 -0
- package/src/defenses/advenced.ts +54 -0
- package/src/defenses/bruteforce.ts +47 -0
- package/src/defenses/curlBot.ts +33 -0
- package/src/defenses/ddos.ts +46 -0
- package/src/defenses/headers.ts +27 -0
- package/src/defenses/malware.ts +35 -0
- package/src/defenses/rateLimit.ts +34 -0
- package/src/defenses/spoofing.ts +5 -0
- package/src/defenses/sqlInjection.ts +41 -0
- package/src/defenses/timingAttack.ts +5 -0
- package/src/defenses/whitelist.ts +23 -0
- package/src/defenses/xss.ts +46 -0
- package/src/index.ts +125 -0
- package/src/type/index.ts +48 -0
- package/src/types/index.ts +32 -0
- package/src/utils/banner.ts +73 -0
- package/src/utils/helpers +237 -0
- package/src/utils/helpers.ts +77 -0
- package/src/utils/index.ts +2 -0
- package/src/utils/logger.ts +174 -0
- package/test/bruteforce.test.ts +34 -0
- package/test/ddos.test.ts +30 -0
- package/test/integration.test.ts +44 -0
- package/test/sql.test.ts +39 -0
- package/test/xss.test.ts +39 -0
- package/test-module.js +23 -0
- package/tsconfig.build.json +17 -0
- package/tsconfig.json +26 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import ModulesEz from '../src/index';
|
|
3
|
+
|
|
4
|
+
const app = express();
|
|
5
|
+
|
|
6
|
+
// TypeScript version with full config
|
|
7
|
+
ModulesEz({
|
|
8
|
+
anti_ddos: { maxPerMinute: 60, blockDuration: 60 },
|
|
9
|
+
anti_sql: true,
|
|
10
|
+
anti_xss: true,
|
|
11
|
+
anti_curl: true,
|
|
12
|
+
anti_header: true,
|
|
13
|
+
anti_bruteforce: true,
|
|
14
|
+
anti_rateLimit: { windowMs: 60000, maxRequests: 100 },
|
|
15
|
+
anti_malware: true,
|
|
16
|
+
anti_pengunjung: "127.0.0.1"
|
|
17
|
+
})(app);
|
|
18
|
+
|
|
19
|
+
interface User {
|
|
20
|
+
id: number;
|
|
21
|
+
name: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
app.get<{}, { message: string; user?: User }>('/api/user', (req, res) => {
|
|
25
|
+
res.json({
|
|
26
|
+
message: 'User data protected by YONGKY',
|
|
27
|
+
user: { id: 1, name: 'YONGKY' }
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
app.listen(3000, () => {
|
|
32
|
+
console.log('TypeScript server with YONGKY Security running! 🗿');
|
|
33
|
+
});
|
package/gitignore
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Dependencies
|
|
2
|
+
node_modules/
|
|
3
|
+
package-lock.json
|
|
4
|
+
yarn.lock
|
|
5
|
+
|
|
6
|
+
# Build output
|
|
7
|
+
dist/
|
|
8
|
+
build/
|
|
9
|
+
*.tsbuildinfo
|
|
10
|
+
|
|
11
|
+
# Logs
|
|
12
|
+
logs/
|
|
13
|
+
*.log
|
|
14
|
+
npm-debug.log*
|
|
15
|
+
yarn-debug.log*
|
|
16
|
+
yarn-error.log*
|
|
17
|
+
|
|
18
|
+
# Runtime data
|
|
19
|
+
pids
|
|
20
|
+
*.pid
|
|
21
|
+
*.seed
|
|
22
|
+
*.pid.lock
|
|
23
|
+
|
|
24
|
+
# Coverage
|
|
25
|
+
coverage/
|
|
26
|
+
.nyc_output/
|
|
27
|
+
|
|
28
|
+
# IDE
|
|
29
|
+
.vscode/
|
|
30
|
+
.idea/
|
|
31
|
+
*.swp
|
|
32
|
+
*.swo
|
|
33
|
+
*~
|
|
34
|
+
.DS_Store
|
|
35
|
+
|
|
36
|
+
# Environment
|
|
37
|
+
.env
|
|
38
|
+
.env.local
|
|
39
|
+
.env.*.local
|
|
40
|
+
|
|
41
|
+
# Testing
|
|
42
|
+
jest.config.js
|
|
43
|
+
test-report.xml
|
|
44
|
+
|
|
45
|
+
# Temporary files
|
|
46
|
+
tmp/
|
|
47
|
+
temp/
|
|
48
|
+
*.tmp
|
package/nodemon.json
ADDED
package/npmignore
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
|
|
2
|
+
# Source files
|
|
3
|
+
src/
|
|
4
|
+
tests/
|
|
5
|
+
examples/
|
|
6
|
+
docs/
|
|
7
|
+
|
|
8
|
+
# Config files
|
|
9
|
+
tsconfig.json
|
|
10
|
+
tsconfig.build.json
|
|
11
|
+
.eslintrc
|
|
12
|
+
.prettierrc
|
|
13
|
+
.gitignore
|
|
14
|
+
.npmignore
|
|
15
|
+
|
|
16
|
+
# Development
|
|
17
|
+
node_modules/
|
|
18
|
+
coverage/
|
|
19
|
+
.nyc_output/
|
|
20
|
+
|
|
21
|
+
# Logs
|
|
22
|
+
*.log
|
|
23
|
+
yongky-security.log
|
|
24
|
+
|
|
25
|
+
# IDE
|
|
26
|
+
.vscode/
|
|
27
|
+
.idea/
|
|
28
|
+
|
|
29
|
+
# Test files
|
|
30
|
+
*.test.ts
|
|
31
|
+
*.spec.ts
|
|
32
|
+
jest.config.js
|
|
33
|
+
|
|
34
|
+
# Only keep dist and essential files
|
|
35
|
+
# dist/ will be included
|
|
36
|
+
# README.md, LICENSE, CHANGELOG.md will be included
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@yognky/premium-security",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "💎 PREMIUM SECURITY - Official Premium Package by YOGNKY",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc -p tsconfig.build.json",
|
|
9
|
+
"build:dev": "tsc",
|
|
10
|
+
"clean": "rm -rf dist",
|
|
11
|
+
"prepublishOnly": "npm run clean && npm run build"
|
|
12
|
+
},
|
|
13
|
+
"keywords": ["premium", "security", "yognky"],
|
|
14
|
+
"author": "YOGNKY",
|
|
15
|
+
"license": "UNLICENSED",
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"express": "^4.18.2",
|
|
18
|
+
"chalk": "^4.1.2",
|
|
19
|
+
"figlet": "^1.6.0",
|
|
20
|
+
"gradient-string": "^2.0.0"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@types/express": "^4.17.21",
|
|
24
|
+
"@types/node": "^20.10.0",
|
|
25
|
+
"typescript": "^5.3.0"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Request, Response, NextFunction } from 'express';
|
|
2
|
+
import crypto from 'crypto';
|
|
3
|
+
|
|
4
|
+
const fingerprintMap = new Map<string, number>();
|
|
5
|
+
const requestHistory = new Map<string, string[]>();
|
|
6
|
+
|
|
7
|
+
// Generate unique fingerprint
|
|
8
|
+
const generateFingerprint = (req: Request): string => {
|
|
9
|
+
const ip = req.ip || '';
|
|
10
|
+
const ua = req.get('user-agent') || '';
|
|
11
|
+
const acceptLang = req.get('accept-language') || '';
|
|
12
|
+
const acceptEncoding = req.get('accept-encoding') || '';
|
|
13
|
+
|
|
14
|
+
return crypto
|
|
15
|
+
.createHash('sha256')
|
|
16
|
+
.update(`${ip}|${ua}|${acceptLang}|${acceptEncoding}`)
|
|
17
|
+
.digest('hex');
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const advancedProtection = (req: Request, res: Response, next: NextFunction) => {
|
|
21
|
+
const fingerprint = generateFingerprint(req);
|
|
22
|
+
const requests = fingerprintMap.get(fingerprint) || 0;
|
|
23
|
+
|
|
24
|
+
// Deteksi fingerprint anomali
|
|
25
|
+
if (requests > 100) {
|
|
26
|
+
res.status(403).json({
|
|
27
|
+
error: '🔐 Advanced Detection!',
|
|
28
|
+
message: 'Fingerprint anomaly detected - YOGNKY KNOWS! 🗿',
|
|
29
|
+
by: 'YOGNKY ADVANCED SECURITY'
|
|
30
|
+
});
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Deteksi request pattern mencurigakan
|
|
35
|
+
const urlHistory = requestHistory.get(fingerprint) || [];
|
|
36
|
+
const currentPath = req.path;
|
|
37
|
+
|
|
38
|
+
if (urlHistory.length > 10 && !urlHistory.includes(currentPath)) {
|
|
39
|
+
// Attacker scanning banyak endpoint
|
|
40
|
+
res.status(403).json({
|
|
41
|
+
error: '🔍 Scanning Detected!',
|
|
42
|
+
message: 'Lu lagi scanning ya? YOGNKY tau! 🗿',
|
|
43
|
+
by: 'YOGNKY SECURITY'
|
|
44
|
+
});
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
urlHistory.push(currentPath);
|
|
49
|
+
if (urlHistory.length > 50) urlHistory.shift();
|
|
50
|
+
requestHistory.set(fingerprint, urlHistory);
|
|
51
|
+
|
|
52
|
+
fingerprintMap.set(fingerprint, requests + 1);
|
|
53
|
+
next();
|
|
54
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Request, Response, NextFunction } from 'express';
|
|
2
|
+
|
|
3
|
+
const loginAttempts = new Map<string, { count: number; firstAttempt: number }>();
|
|
4
|
+
const blockedLogins = new Map<string, number>();
|
|
5
|
+
|
|
6
|
+
export const bruteforceProtection = (req: Request, res: Response, next: NextFunction) => {
|
|
7
|
+
if (req.method !== 'POST') return next();
|
|
8
|
+
|
|
9
|
+
const ip = req.ip || req.socket.remoteAddress || 'unknown';
|
|
10
|
+
const now = Date.now();
|
|
11
|
+
|
|
12
|
+
if (blockedLogins.has(ip)) {
|
|
13
|
+
const unblockTime = blockedLogins.get(ip)!;
|
|
14
|
+
if (now < unblockTime) {
|
|
15
|
+
res.status(429).json({
|
|
16
|
+
error: '🔒 Brute Force Protection!',
|
|
17
|
+
message: `Coba lagi ${Math.ceil((unblockTime - now) / 1000)} detik lagi`,
|
|
18
|
+
by: 'YOGNKY SECURITY'
|
|
19
|
+
});
|
|
20
|
+
return;
|
|
21
|
+
} else {
|
|
22
|
+
blockedLogins.delete(ip);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const attempts = loginAttempts.get(ip) || { count: 0, firstAttempt: now };
|
|
27
|
+
|
|
28
|
+
if (now - attempts.firstAttempt > 15 * 60 * 1000) {
|
|
29
|
+
attempts.count = 0;
|
|
30
|
+
attempts.firstAttempt = now;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
attempts.count++;
|
|
34
|
+
loginAttempts.set(ip, attempts);
|
|
35
|
+
|
|
36
|
+
if (attempts.count >= 5) {
|
|
37
|
+
blockedLogins.set(ip, now + 30 * 60 * 1000);
|
|
38
|
+
res.status(429).json({
|
|
39
|
+
error: '⛔ Account Temporary Locked!',
|
|
40
|
+
message: 'Terlalu banyak percobaan login. Coba lagi 30 menit kemudian.',
|
|
41
|
+
by: 'YOGNKY SECURITY'
|
|
42
|
+
});
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
next();
|
|
47
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Request, Response, NextFunction } from 'express';
|
|
2
|
+
|
|
3
|
+
const botSignatures = [
|
|
4
|
+
/curl/i, /wget/i, /python/i, /perl/i, /ruby/i,
|
|
5
|
+
/java/i, /php/i, /libwww/i, /http client/i,
|
|
6
|
+
/scrapy/i, /spider/i, /crawler/i, /bot/i,
|
|
7
|
+
/scraper/i, /fetch/i, /headless/i, /puppeteer/i,
|
|
8
|
+
/selenium/i, /phantomjs/i, /axios/i, /requests/i,
|
|
9
|
+
/go-http-client/i, /okhttp/i, /python-requests/i,
|
|
10
|
+
/node-fetch/i, /got/i, /axios/i, /superagent/i
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
const suspiciousHeaders = [
|
|
14
|
+
'x-scrapy', 'x-requested-with', 'x-http-method-override',
|
|
15
|
+
'x-crawler', 'x-bot', 'x-scraper'
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
export const curlBotProtection = (req: Request, res: Response, next: NextFunction) => {
|
|
19
|
+
const ua = req.get('user-agent') || '';
|
|
20
|
+
const isBot = botSignatures.some(pattern => pattern.test(ua));
|
|
21
|
+
const hasSuspiciousHeader = suspiciousHeaders.some(header => req.headers[header]);
|
|
22
|
+
|
|
23
|
+
if (isBot || hasSuspiciousHeader) {
|
|
24
|
+
res.status(403).json({
|
|
25
|
+
error: '🤖 Bot Detected!',
|
|
26
|
+
message: 'BOT ga boleh lewat kata YOGNKY 🤣',
|
|
27
|
+
by: 'YOGNKY SECURITY'
|
|
28
|
+
});
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
next();
|
|
33
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Request, Response, NextFunction } from 'express';
|
|
2
|
+
|
|
3
|
+
const requestMap = new Map<string, number[]>();
|
|
4
|
+
const blockedIPs = new Map<string, number>();
|
|
5
|
+
|
|
6
|
+
export const ddosProtection = (config: any = {}) => {
|
|
7
|
+
const maxPerMinute = config.maxPerMinute || 10;
|
|
8
|
+
const blockDuration = (config.blockDuration || 60) * 1000;
|
|
9
|
+
|
|
10
|
+
return (req: Request, res: Response, next: NextFunction) => {
|
|
11
|
+
const ip = req.ip || req.socket.remoteAddress || 'unknown';
|
|
12
|
+
const now = Date.now();
|
|
13
|
+
|
|
14
|
+
// Cek apakah IP diblokir
|
|
15
|
+
if (blockedIPs.has(ip)) {
|
|
16
|
+
const unblockTime = blockedIPs.get(ip)!;
|
|
17
|
+
if (now < unblockTime) {
|
|
18
|
+
res.status(429).json({
|
|
19
|
+
error: '⛔ DDoS Protection',
|
|
20
|
+
message: `IP diblokir ${Math.ceil((unblockTime - now) / 1000)} detik lagi`,
|
|
21
|
+
by: 'YOGNKY SECURITY'
|
|
22
|
+
});
|
|
23
|
+
return;
|
|
24
|
+
} else {
|
|
25
|
+
blockedIPs.delete(ip);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const requests = requestMap.get(ip) || [];
|
|
30
|
+
const recent = requests.filter(time => time > now - 60000);
|
|
31
|
+
|
|
32
|
+
if (recent.length >= maxPerMinute) {
|
|
33
|
+
blockedIPs.set(ip, now + blockDuration);
|
|
34
|
+
res.status(429).json({
|
|
35
|
+
error: '🔥 DDoS Attack Detected!',
|
|
36
|
+
message: `IP ${ip} diblokir sementara`,
|
|
37
|
+
by: 'YOGNKY SECURITY'
|
|
38
|
+
});
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
recent.push(now);
|
|
43
|
+
requestMap.set(ip, recent);
|
|
44
|
+
next();
|
|
45
|
+
};
|
|
46
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Request, Response, NextFunction } from 'express';
|
|
2
|
+
|
|
3
|
+
const maliciousHeaders = [
|
|
4
|
+
'x-forwarded-for', 'x-real-ip', 'x-original-url',
|
|
5
|
+
'x-http-method-override', 'x-http-method',
|
|
6
|
+
'x-method-override', 'x-rewrite-url'
|
|
7
|
+
];
|
|
8
|
+
|
|
9
|
+
export const headerProtection = (req: Request, res: Response, next: NextFunction) => {
|
|
10
|
+
const hasMalicious = maliciousHeaders.some(header => req.headers[header] !== undefined);
|
|
11
|
+
|
|
12
|
+
if (hasMalicious) {
|
|
13
|
+
res.status(400).json({
|
|
14
|
+
error: '🎭 Malicious Headers Detected!',
|
|
15
|
+
message: 'Jangan nakal pake header aneh2 ya 🙏',
|
|
16
|
+
by: 'YOGNKY SECURITY'
|
|
17
|
+
});
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
res.setHeader('X-Powered-By', 'YOGNKY SECURITY 🗿');
|
|
22
|
+
res.setHeader('X-Content-Type-Options', 'nosniff');
|
|
23
|
+
res.setHeader('X-Frame-Options', 'DENY');
|
|
24
|
+
res.setHeader('X-XSS-Protection', '1; mode=block');
|
|
25
|
+
|
|
26
|
+
next();
|
|
27
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Request, Response, NextFunction } from 'express';
|
|
2
|
+
|
|
3
|
+
const malwarePatterns = [
|
|
4
|
+
/base64_decode/i, /eval\(/i, /system\(/i, /shell_exec/i,
|
|
5
|
+
/passthru/i, /proc_open/i, /popen/i, /curl_exec/i,
|
|
6
|
+
/file_get_contents/i, /fopen/i, /readfile/i,
|
|
7
|
+
/\.\.\/|\.\.\\/, /etc\/passwd/i, /wget\s+http/i,
|
|
8
|
+
/cmd\.exe/i, /powershell/i
|
|
9
|
+
];
|
|
10
|
+
|
|
11
|
+
const checkMalware = (data: any): boolean => {
|
|
12
|
+
if (!data) return false;
|
|
13
|
+
if (typeof data === 'string') {
|
|
14
|
+
return malwarePatterns.some(pattern => pattern.test(data));
|
|
15
|
+
}
|
|
16
|
+
if (typeof data === 'object') {
|
|
17
|
+
return Object.values(data).some(val => checkMalware(val));
|
|
18
|
+
}
|
|
19
|
+
return false;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const malwareProtection = (req: Request, res: Response, next: NextFunction) => {
|
|
23
|
+
const hasMalware = checkMalware(req.query) || checkMalware(req.body);
|
|
24
|
+
|
|
25
|
+
if (hasMalware) {
|
|
26
|
+
res.status(403).json({
|
|
27
|
+
error: '🦠 Malware Detected!',
|
|
28
|
+
message: 'Woi jangan coba2 pake malware 🗿',
|
|
29
|
+
by: 'YOGNKY SECURITY'
|
|
30
|
+
});
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
next();
|
|
35
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Request, Response, NextFunction } from 'express';
|
|
2
|
+
|
|
3
|
+
const rateMap = new Map<string, { count: number; resetTime: number }>();
|
|
4
|
+
|
|
5
|
+
export const rateLimitProtection = (config: any = {}) => {
|
|
6
|
+
const windowMs = config.windowMs || 60000;
|
|
7
|
+
const maxRequests = config.maxRequests || 10;
|
|
8
|
+
|
|
9
|
+
return (req: Request, res: Response, next: NextFunction) => {
|
|
10
|
+
const ip = req.ip || req.socket.remoteAddress || 'unknown';
|
|
11
|
+
const now = Date.now();
|
|
12
|
+
const rate = rateMap.get(ip);
|
|
13
|
+
|
|
14
|
+
if (!rate || now > rate.resetTime) {
|
|
15
|
+
rateMap.set(ip, { count: 1, resetTime: now + windowMs });
|
|
16
|
+
next();
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (rate.count >= maxRequests) {
|
|
21
|
+
res.status(429).json({
|
|
22
|
+
error: '🐌 Rate Limit Exceeded!',
|
|
23
|
+
message: `Maksimal ${maxRequests} request per ${windowMs / 1000} detik`,
|
|
24
|
+
retryAfter: Math.ceil((rate.resetTime - now) / 1000),
|
|
25
|
+
by: 'YOGNKY SECURITY'
|
|
26
|
+
});
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
rate.count++;
|
|
31
|
+
rateMap.set(ip, rate);
|
|
32
|
+
next();
|
|
33
|
+
};
|
|
34
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Request, Response, NextFunction } from 'express';
|
|
2
|
+
|
|
3
|
+
const sqlPatterns = [
|
|
4
|
+
/(\%27)|(\')|(\-\-)|(\%23)|(#)/i,
|
|
5
|
+
/select.+from/i,
|
|
6
|
+
/insert.+into/i,
|
|
7
|
+
/delete.+from/i,
|
|
8
|
+
/update.+set/i,
|
|
9
|
+
/drop.+table/i,
|
|
10
|
+
/union.+select/i,
|
|
11
|
+
/exec(\s|\+)+(s|x)p\w+/i,
|
|
12
|
+
/sleep\(/i,
|
|
13
|
+
/benchmark\(/i,
|
|
14
|
+
/information_schema/i
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
const checkNested = (obj: any): boolean => {
|
|
18
|
+
if (!obj) return false;
|
|
19
|
+
if (typeof obj === 'string') {
|
|
20
|
+
return sqlPatterns.some(pattern => pattern.test(obj));
|
|
21
|
+
}
|
|
22
|
+
if (typeof obj === 'object') {
|
|
23
|
+
return Object.values(obj).some(val => checkNested(val));
|
|
24
|
+
}
|
|
25
|
+
return false;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const sqlInjectionProtection = (req: Request, res: Response, next: NextFunction) => {
|
|
29
|
+
const isInjected = checkNested(req.query) || checkNested(req.body) || checkNested(req.params);
|
|
30
|
+
|
|
31
|
+
if (isInjected) {
|
|
32
|
+
res.status(403).json({
|
|
33
|
+
error: '💉 SQL Injection Detected!',
|
|
34
|
+
message: 'Nice try, tapi YOGNKY gaakan kena 😎',
|
|
35
|
+
by: 'YOGNKY SECURITY'
|
|
36
|
+
});
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
next();
|
|
41
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Request, Response, NextFunction } from 'express';
|
|
2
|
+
|
|
3
|
+
export const whitelistProtection = (allowedIPsStr: string | false) => {
|
|
4
|
+
if (!allowedIPsStr) return (_req: Request, _res: Response, next: NextFunction) => next();
|
|
5
|
+
|
|
6
|
+
const allowedIPs = allowedIPsStr.split(',').map(ip => ip.trim());
|
|
7
|
+
|
|
8
|
+
return (req: Request, res: Response, next: NextFunction) => {
|
|
9
|
+
const clientIP = req.ip || req.socket.remoteAddress || 'unknown';
|
|
10
|
+
const cleanIP = clientIP.replace('::ffff:', '');
|
|
11
|
+
|
|
12
|
+
if (!allowedIPs.includes(cleanIP) && !allowedIPs.includes(clientIP)) {
|
|
13
|
+
res.status(403).json({
|
|
14
|
+
error: '🚫 Access Denied!',
|
|
15
|
+
message: `IP ${clientIP} tidak ada di whitelist`,
|
|
16
|
+
by: 'YOGNKY SECURITY'
|
|
17
|
+
});
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
next();
|
|
22
|
+
};
|
|
23
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Request, Response, NextFunction } from 'express';
|
|
2
|
+
|
|
3
|
+
const xssPatterns = [
|
|
4
|
+
/<script.*?>.*?<\/script>/i,
|
|
5
|
+
/javascript:/i,
|
|
6
|
+
/onerror=/i,
|
|
7
|
+
/onload=/i,
|
|
8
|
+
/onclick=/i,
|
|
9
|
+
/onmouseover=/i,
|
|
10
|
+
/eval\(/i,
|
|
11
|
+
/alert\(/i,
|
|
12
|
+
/prompt\(/i,
|
|
13
|
+
/confirm\(/i,
|
|
14
|
+
/document\./i,
|
|
15
|
+
/window\./i,
|
|
16
|
+
/location\./i,
|
|
17
|
+
/<iframe/i,
|
|
18
|
+
/<object/i,
|
|
19
|
+
/<embed/i
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
const checkXSS = (data: any): boolean => {
|
|
23
|
+
if (!data) return false;
|
|
24
|
+
if (typeof data === 'string') {
|
|
25
|
+
return xssPatterns.some(pattern => pattern.test(data));
|
|
26
|
+
}
|
|
27
|
+
if (typeof data === 'object') {
|
|
28
|
+
return Object.values(data).some(val => checkXSS(val));
|
|
29
|
+
}
|
|
30
|
+
return false;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const xssProtection = (req: Request, res: Response, next: NextFunction) => {
|
|
34
|
+
const hasXSS = checkXSS(req.query) || checkXSS(req.body) || checkXSS(req.params);
|
|
35
|
+
|
|
36
|
+
if (hasXSS) {
|
|
37
|
+
res.status(403).json({
|
|
38
|
+
error: '⚠️ XSS Attack Detected!',
|
|
39
|
+
message: 'Ga bisa XSS disini, YOGNKY pinter 😤',
|
|
40
|
+
by: 'YOGNKY SECURITY'
|
|
41
|
+
});
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
next();
|
|
46
|
+
};
|