csrf-shield 1.0.0 → 1.0.2

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.
Files changed (3) hide show
  1. package/app.js +64 -47
  2. package/old.js +74 -0
  3. package/package.json +1 -1
package/app.js CHANGED
@@ -1,70 +1,87 @@
1
- const CryptoJS = require('crypto-js');
1
+ const crypto = require('crypto');
2
2
 
3
3
  module.exports = function(options = {}) {
4
- const {
5
- secret = CryptoJS.lib.WordArray.random(32).toString(),
6
- timeout = 1000 * 60 * 10,
7
- } = options;
4
+ // Buffer işlemleri string işlemlerinden hızlıdır, secret'ı hazırda tutuyoruz.
5
+ const secretKey = options.secret ? Buffer.from(options.secret) : crypto.randomBytes(32);
6
+ const timeout = options.timeout || 1000 * 60 * 10;
7
+ const ALGORITHM = 'sha1'; // Hız için SHA1 (CSRF için yeterince güvenli). Daha yüksek güvenlik için 'sha256' yapın.
8
8
 
9
- function generateToken(ip, userAgent) {
10
- const timestamp = Date.now();
11
- const data = JSON.stringify({ ip, userAgent, timestamp });
12
- return CryptoJS.AES.encrypt(data, secret).toString();
13
- }
14
-
15
- function parseToken(token) {
16
- try {
17
- const bytes = CryptoJS.AES.decrypt(token, secret);
18
- const decryptedData = bytes.toString(CryptoJS.enc.Utf8);
19
- return JSON.parse(decryptedData);
20
- } catch (error) {
21
- throw new Error('Invalid token');
22
- }
23
- }
24
-
25
- function verifyToken(token, ip, userAgent) {
26
- try {
27
- const { ip: tokenIp, userAgent: tokenUserAgent, timestamp } = parseToken(token);
28
-
29
- if (tokenIp !== ip || tokenUserAgent !== userAgent) {
30
- return { valid: false, reason: 'Token mismatch' };
31
- }
32
-
33
- if (Date.now() - timestamp > timeout) {
34
- return { valid: false, reason: 'Token expired' };
35
- }
36
-
37
- return { valid: true };
38
- } catch (error) {
39
- return { valid: false, reason: error.message };
40
- }
9
+ // IP'yi en hızlı şekilde alma fonksiyonu
10
+ // Proxy güvenliği için 'trust proxy' ayarının express'te yapılı olduğunu varsayıyoruz.
11
+ function getIP(req) {
12
+ return req.headers['x-forwarded-for'] ||
13
+ req.socket.remoteAddress ||
14
+ '';
41
15
  }
42
16
 
43
17
  return {
44
18
  middleware: function(req, res, next) {
19
+ // MEMOIZATION: Token sadece ilk çağrıldığında hesaplanır, sonra cache'den gelir.
20
+ let _cachedToken = null;
21
+
45
22
  req.csrfToken = function() {
46
- const ip = req.headers['cf-connecting-ip'] || req.headers['x-forwarded-for'] || req.ip;
47
- const userAgent = req.headers['user-agent'];
48
- return generateToken(ip, userAgent);
23
+ if (_cachedToken) return _cachedToken;
24
+
25
+ const timestamp = Date.now();
26
+ // User-Agent ve IP'yi al
27
+ const ua = req.headers['user-agent'] || '';
28
+ const ip = getIP(req);
29
+
30
+ // Tek seferlik string birleştirme (V8 motoru bunu çok iyi optimize eder)
31
+ // Format: timestamp|ip|ua
32
+ const payload = timestamp + '|' + ip + '|' + ua;
33
+
34
+ // C++ Binding'e tek çağrı (Overhead'i azaltır)
35
+ const signature = crypto.createHmac(ALGORITHM, secretKey)
36
+ .update(payload)
37
+ .digest('base64');
38
+
39
+ // Token: timestamp:signature
40
+ _cachedToken = timestamp + ':' + signature;
41
+ return _cachedToken;
49
42
  };
50
43
  next();
51
44
  },
52
45
 
53
46
  verifyToken: function() {
54
47
  return function(req, res, next) {
55
- const token = req.body._csrf || req.query._csrf || req.headers['x-csrf-token'];
48
+ // Token'ı en olası lokasyonlardan sırayla dene
49
+ const token = req.body?._csrf || req.headers['x-csrf-token'] || req.query?._csrf;
56
50
 
57
51
  if (!token) {
58
- return res.status(403).json({ status: false, message: 'CSRF token is missing' });
52
+ return res.status(403).json({ status: false, message: 'MISSING_TOKEN' });
53
+ }
54
+
55
+ // OPTIMIZASYON: Split yerine hızlı indeks bulma
56
+ const separatorIndex = token.indexOf(':');
57
+ if (separatorIndex === -1) {
58
+ return res.status(403).json({ status: false, message: 'INVALID_FORMAT' });
59
59
  }
60
60
 
61
- const ip = req.headers['cf-connecting-ip'] || req.headers['x-forwarded-for'] || req.ip;
62
- const userAgent = req.headers['user-agent'];
61
+ // Timestamp'i parse et
62
+ const timestampStr = token.substring(0, separatorIndex);
63
+ const receivedSignature = token.substring(separatorIndex + 1);
64
+ const timestamp = parseInt(timestampStr, 10);
65
+
66
+ // 1. ADIM: Kriptografik işlemden ÖNCE zaman kontrolü (En ucuz kontrol)
67
+ if (!timestamp || (Date.now() - timestamp > timeout)) {
68
+ return res.status(403).json({ status: false, message: 'TOKEN_EXPIRED' });
69
+ }
70
+
71
+ // 2. ADIM: İmzayı yeniden oluştur
72
+ const ua = req.headers['user-agent'] || '';
73
+ const ip = getIP(req);
74
+
75
+ const expectedSignature = crypto.createHmac(ALGORITHM, secretKey)
76
+ .update(timestampStr + '|' + ip + '|' + ua)
77
+ .digest('base64');
63
78
 
64
- const { valid, reason } = verifyToken(token, ip, userAgent);
79
+ // 3. ADIM: Timing-Safe karşılaştırma (Buffer seviyesinde)
80
+ const signatureBuf = Buffer.from(receivedSignature);
81
+ const expectedBuf = Buffer.from(expectedSignature);
65
82
 
66
- if (!valid) {
67
- return res.status(403).json({ status: false, message: `Invalid CSRF token: ${reason}` });
83
+ if (signatureBuf.length !== expectedBuf.length || !crypto.timingSafeEqual(signatureBuf, expectedBuf)) {
84
+ return res.status(403).json({ status: false, message: 'CSRF_TOKEN_INVALID' });
68
85
  }
69
86
 
70
87
  next();
package/old.js ADDED
@@ -0,0 +1,74 @@
1
+ const CryptoJS = require('crypto-js');
2
+
3
+ module.exports = function(options = {}) {
4
+ const {
5
+ secret = CryptoJS.lib.WordArray.random(32).toString(),
6
+ timeout = 1000 * 60 * 10,
7
+ } = options;
8
+
9
+ function generateToken(ip, userAgent) {
10
+ const timestamp = Date.now();
11
+ const data = JSON.stringify({ ip, userAgent, timestamp });
12
+ return CryptoJS.AES.encrypt(data, secret).toString();
13
+ }
14
+
15
+ function parseToken(token) {
16
+ try {
17
+ const bytes = CryptoJS.AES.decrypt(token, secret);
18
+ const decryptedData = bytes.toString(CryptoJS.enc.Utf8);
19
+ return JSON.parse(decryptedData);
20
+ } catch (error) {
21
+ throw new Error('Invalid token');
22
+ }
23
+ }
24
+
25
+ function verifyToken(token, ip, userAgent) {
26
+ try {
27
+ const { ip: tokenIp, userAgent: tokenUserAgent, timestamp } = parseToken(token);
28
+
29
+ if (tokenIp !== ip || tokenUserAgent !== userAgent) {
30
+ return { valid: false, reason: 'Token mismatch' };
31
+ }
32
+
33
+ if (Date.now() - timestamp > timeout) {
34
+ return { valid: false, reason: 'Token expired' };
35
+ }
36
+
37
+ return { valid: true };
38
+ } catch (error) {
39
+ return { valid: false, reason: error.message };
40
+ }
41
+ }
42
+
43
+ return {
44
+ middleware: function(req, res, next) {
45
+ req.csrfToken = function() {
46
+ const ip = req.headers['cf-connecting-ip'] || req.headers['x-forwarded-for'] || req.ip;
47
+ const userAgent = req.headers['user-agent'];
48
+ return generateToken(ip, userAgent);
49
+ };
50
+ next();
51
+ },
52
+
53
+ verifyToken: function() {
54
+ return function(req, res, next) {
55
+ const token = req.body._csrf || req.query._csrf || req.headers['x-csrf-token'];
56
+
57
+ if (!token) {
58
+ return res.status(403).json({ status: false, message: 'CSRF token is missing' });
59
+ }
60
+
61
+ const ip = req.headers['cf-connecting-ip'] || req.headers['x-forwarded-for'] || req.ip;
62
+ const userAgent = req.headers['user-agent'];
63
+
64
+ const { valid, reason } = verifyToken(token, ip, userAgent);
65
+
66
+ if (!valid) {
67
+ return res.status(403).json({ status: false, message: `CSRF_TOKEN_INVALID`, details: reason });
68
+ }
69
+
70
+ next();
71
+ };
72
+ }
73
+ };
74
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "csrf-shield",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "CSRF protection middleware for Express.js applications.",
5
5
  "main": "app.js",
6
6
  "scripts": {