Ctrl K
ComunidadGuías…

lib/account-auth-throttle.js

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
var fs = require('fs');
var path = require('path');
var crypto = require('crypto');
var WINDOW = 15 * 60000;
function attachThrottle(ctx) {
  function key(value) { return crypto.createHash('sha256').update(value).digest('hex'); }
  function load() {
    try {
      var data = JSON.parse(fs.readFileSync(ctx.file, 'utf8'));
      if (!data || typeof data !== 'object' || Array.isArray(data) || Object.values(data).some(function (entry) { return !entry || !Number.isFinite(entry.count) || !Number.isFinite(entry.until); })) throw new Error('Invalid limiter data.');
      return data;
    }
    catch (error) { if (error.code === 'ENOENT') return {}; throw new Error('Sign-in protection is unavailable. Please retry later.'); }
  }
  function save(data) {
    try {
      fs.mkdirSync(path.dirname(ctx.file), { recursive: true });
      fs.writeFileSync(ctx.file + '.tmp', JSON.stringify(data), { mode: 0o600 }); fs.renameSync(ctx.file + '.tmp', ctx.file);
    } catch (error) { throw new Error('Could not save sign-in protection. Please retry later.'); }
  }
  function prune(data) { Object.keys(data).forEach(function (id) { if (data[id].until <= Date.now()) delete data[id]; }); }
  function consumeKeys(keys) {
    var data = load(); prune(data);
    if (Object.keys(data).length >= 10000) throw new Error('Sign-in is busy. Please retry later.');
    keys.forEach(function (value) {
      var id = key(value.name); var entry = data[id] || { count: 0, until: Date.now() + WINDOW };
      if (entry.count >= value.max) { var error = new Error('Too many attempts. Retry in 15 minutes.'); error.status = 429; throw error; }
      entry.count++; data[id] = entry;
    });
    save(data);
  }
  function consume(req, username) {
    var keys = [{ name: 'ip:' + ctx.clientAddress(req), max: 100 }];
    if (username) keys.push({ name: 'account:' + String(username).toLowerCase().slice(0, 100), max: 10 });
    consumeKeys(keys);
  }
  function success(req, username) {
    // Successful logins must not erase the IP budget for attacks against other accounts.
    if (!username) return;
    var data = load(); delete data[key('account:' + String(username).toLowerCase().slice(0, 100))]; prune(data); save(data);
  }
  return { consume: consume, success: success };
}
module.exports = { attachThrottle: attachThrottle };