Skip to content
Permalink
Browse files

dont allow login with invalid ip, escape ip display on user/info page

  • Loading branch information
barisusakli committed Nov 30, 2017
1 parent da5997a commit a7a3f3619b070ce000c91321f13eb38e561fdc4b
Showing with 103 additions and 55 deletions.
  1. +22 −17 src/controllers/authentication.js
  2. +8 −11 src/meta/blacklist.js
  3. +27 −7 src/user/admin.js
  4. +10 −6 src/user/auth.js
  5. +30 −0 test/authentication.js
  6. +6 −14 test/blacklist.js
@@ -6,6 +6,7 @@ var passport = require('passport');
var nconf = require('nconf');
var validator = require('validator');
var _ = require('lodash');
var ipaddr = require('ipaddr.js');

var db = require('../database');
var meta = require('../meta');
@@ -289,26 +290,30 @@ authenticationController.doLogin = function (req, uid, callback) {

authenticationController.onSuccessfulLogin = function (req, uid, callback) {
var uuid = utils.generateUUID();
req.session.meta = {};

delete req.session.forceLogin;

// Associate IP used during login with user account
user.logIP(uid, req.ip);
req.session.meta.ip = req.ip;

// Associate metadata retrieved via user-agent
req.session.meta = _.extend(req.session.meta, {
uuid: uuid,
datetime: Date.now(),
platform: req.useragent.platform,
browser: req.useragent.browser,
version: req.useragent.version,
});

async.waterfall([
async.apply(meta.blacklist.test, req.ip),
function (next) {
meta.blacklist.test(req.ip, next);
},
function (next) {
user.logIP(uid, req.ip, next);
},
function (next) {
req.session.meta = {};

delete req.session.forceLogin;
// Associate IP used during login with user account
req.session.meta.ip = req.ip;

// Associate metadata retrieved via user-agent
req.session.meta = _.extend(req.session.meta, {
uuid: uuid,
datetime: Date.now(),
platform: req.useragent.platform,
browser: req.useragent.browser,
version: req.useragent.version,
});

async.parallel([
function (next) {
user.auth.addSession(uid, req.sessionID, next);
@@ -68,7 +68,12 @@ Blacklist.test = function (clientIp, callback) {
// clientIp = '127.0.15.1:3443'; // IPv4 with port strip port to not fail
clientIp = clientIp.split(':').length === 2 ? clientIp.split(':')[0] : clientIp;

var addr = ipaddr.parse(clientIp);
var addr;
try {
addr = ipaddr.parse(clientIp);
} catch (err) {
return callback(err);
}

if (
Blacklist._rules.ipv4.indexOf(clientIp) === -1 && // not explicitly specified in ipv4 list
@@ -88,23 +93,15 @@ Blacklist.test = function (clientIp, callback) {
analytics.increment('blacklist');
}

if (typeof callback === 'function') {
callback(err);
} else {
return !!err;
}
callback(err);
});
} else {
var err = new Error('[[error:blacklisted-ip]]');
err.code = 'blacklisted-ip';

analytics.increment('blacklist');

if (typeof callback === 'function') {
setImmediate(callback, err);
} else {
return true;
}
setImmediate(callback, err);
}
};

@@ -2,21 +2,41 @@
'use strict';

var async = require('async');
var winston = require('winston');
var validator = require('validator');

var db = require('../database');
var plugins = require('../plugins');
var winston = require('winston');

module.exports = function (User) {
User.logIP = function (uid, ip) {
User.logIP = function (uid, ip, callback) {
var now = Date.now();
db.sortedSetAdd('uid:' + uid + ':ip', now, ip || 'Unknown');
if (ip) {
db.sortedSetAdd('ip:' + ip + ':uid', now, uid);
}
async.waterfall([
function (next) {
db.sortedSetAdd('uid:' + uid + ':ip', now, ip || 'Unknown', next);
},
function (next) {
if (ip) {
db.sortedSetAdd('ip:' + ip + ':uid', now, uid, next);
} else {
next();
}
},
], callback);
};

User.getIPs = function (uid, stop, callback) {
db.getSortedSetRevRange('uid:' + uid + ':ip', 0, stop, callback);
async.waterfall([
function (next) {
db.getSortedSetRevRange('uid:' + uid + ':ip', 0, stop, next);
},
function (ips, next) {
ips = ips.map(function (ip) {
return validator.escape(String(ip));
});
next(null, ips);
},
], callback);
};

User.getUsersCSV = function (callback) {
@@ -2,6 +2,7 @@

var async = require('async');
var winston = require('winston');
var validator = require('validator');
var db = require('../database');
var meta = require('../meta');
var events = require('../events');
@@ -126,12 +127,15 @@ module.exports = function (User) {
next(err, sessions);
});
},
], function (err, sessions) {
callback(err, sessions ? sessions.map(function (sessObj) {
sessObj.meta.datetimeISO = new Date(sessObj.meta.datetime).toISOString();
return sessObj.meta;
}) : undefined);
});
function (sessions, next) {
sessions = sessions.map(function (sessObj) {
sessObj.meta.datetimeISO = new Date(sessObj.meta.datetime).toISOString();
sessObj.meta.ip = validator.escape(String(sessObj.meta.ip));
return sessObj.meta;
});
next(null, sessions);
},
], callback);
};

User.auth.addSession = function (uid, sessionId, callback) {
@@ -235,6 +235,36 @@ describe('authentication', function () {
});
});

it('should fail to login if ip address if invalid', function (done) {
var jar = request.jar();
request({
url: nconf.get('url') + '/api/config',
json: true,
jar: jar,
}, function (err, response, body) {
if (err) {
return done(err);
}

request.post(nconf.get('url') + '/login', {
form: {
username: 'regular',
password: 'regularpwd',
},
json: true,
jar: jar,
headers: {
'x-csrf-token': body.csrf_token,
'x-forwarded-for': '<script>alert("xss")</script>',
},
}, function (err, response, body) {
assert.ifError(err);
assert.equal(response.statusCode, 500);
done();
});
});
});

it('should fail to login if user does not exist', function (done) {
loginUser('doesnotexist', 'nopassword', function (err, response, body) {
assert.ifError(err);
@@ -49,32 +49,24 @@ describe('blacklist', function () {
});
});

it('should pass ip test against blacklist async', function (done) {
it('should pass ip test against blacklist', function (done) {
blacklist.test('3.3.3.3', function (err) {
assert.ifError(err);
done();
});
});

it('should pass ip test against blacklist sync', function (done) {
assert(!blacklist.test('3.3.3.3'));
done();
});

it('should fail ip test against blacklist async', function (done) {
it('should fail ip test against blacklist', function (done) {
blacklist.test('1.1.1.1', function (err) {
assert.equal(err.message, '[[error:blacklisted-ip]]');
done();
});
});

it('should fail ip test against blacklist sync', function (done) {
assert(blacklist.test('1.1.1.1'));
done();
});

it('should pass ip test and not crash with ipv6 address', function (done) {
assert(!blacklist.test('2001:db8:85a3:0:0:8a2e:370:7334'));
done();
blacklist.test('2001:db8:85a3:0:0:8a2e:370:7334', function (err) {
assert.ifError(err);
done();
});
});
});

0 comments on commit a7a3f36

Please sign in to comment.