|
|
|
'use strict';
|
|
|
|
|
|
|
|
const { ipHeader, ipHashPermLevel } = require(__dirname+'/../configs/main.js')
|
normalize IP addresses
Currently jschan takes the IP address as a string from the `X-Real-Ip` header,
which based on the frontend proxy configuration, OS settings, etc. can take
various forms:
IPv4 addresses can be given in normal IPv4 dotted notation (e.g. `1.2.3.4`) or
as an IPv4-mapped IPv6 address (e.g. `::ffff:1.2.3.4`). The problem is, that in
the latter case, node's `isIP` will report 6, so the code will try to split it
along colons, breaking hrange and qrange.
With IPv6 addresses, it's possible to elide runs of zeroes, so `::1` and
`0:0:0:0:0:0:0:1` (and also `0000:0000:0000:0000:0000:0000:0000:0001`)
represents the same address. Since it's pretty easy to get a /64 IPv6 block, a
spammer can abuse it, by spamming from `a:b:c:d::1` (`qrange=a:b:c:d`,
`hrange=a:b:c`), then from `a:b:c:d::1:1` (`qrange=a:b:c:d:`, `hrange=a:b:c`),
`a:b:c:d::1:1:1` (`qrange=a:b:c:d::1`, `hrange=a:b:c:d`) and
`a:b:c:d:1:1:1:1` (`qrange=a:b:c:d:1:1`, `hrange=a:b:c:d`). He practically got
two hranges and qrange is pretty much pointless for IPv6 addresses.
This change uses the `ip6addr` package to parse IP addresses and convert it to
some canonical form. This means:
* IPv4 and IPv4-mapped IPv6 addresses are converted to normal IPv4 notation.
* Zero are not elided in IPv6 (so you'll never see `::`).
* IPv6 addresses are not zero padded (so `..:1` instead of `..:0001`).
* Even though it's not documented, it seems like `ip6addr` always generates
lower-case letters.
This will unfortunately mean that some IP hashes may change after the update.
Normal IPv4 hashes will most probably remain the same though.
4 years ago
|
|
|
, { parse } = require('ip6addr')
|
|
|
|
, hashIp = require(__dirname+'/haship.js');
|
|
|
|
|
|
|
|
module.exports = (req, res, next) => {
|
|
|
|
const ip = req.headers[ipHeader] || req.connection.remoteAddress;
|
normalize IP addresses
Currently jschan takes the IP address as a string from the `X-Real-Ip` header,
which based on the frontend proxy configuration, OS settings, etc. can take
various forms:
IPv4 addresses can be given in normal IPv4 dotted notation (e.g. `1.2.3.4`) or
as an IPv4-mapped IPv6 address (e.g. `::ffff:1.2.3.4`). The problem is, that in
the latter case, node's `isIP` will report 6, so the code will try to split it
along colons, breaking hrange and qrange.
With IPv6 addresses, it's possible to elide runs of zeroes, so `::1` and
`0:0:0:0:0:0:0:1` (and also `0000:0000:0000:0000:0000:0000:0000:0001`)
represents the same address. Since it's pretty easy to get a /64 IPv6 block, a
spammer can abuse it, by spamming from `a:b:c:d::1` (`qrange=a:b:c:d`,
`hrange=a:b:c`), then from `a:b:c:d::1:1` (`qrange=a:b:c:d:`, `hrange=a:b:c`),
`a:b:c:d::1:1:1` (`qrange=a:b:c:d::1`, `hrange=a:b:c:d`) and
`a:b:c:d:1:1:1:1` (`qrange=a:b:c:d:1:1`, `hrange=a:b:c:d`). He practically got
two hranges and qrange is pretty much pointless for IPv6 addresses.
This change uses the `ip6addr` package to parse IP addresses and convert it to
some canonical form. This means:
* IPv4 and IPv4-mapped IPv6 addresses are converted to normal IPv4 notation.
* Zero are not elided in IPv6 (so you'll never see `::`).
* IPv6 addresses are not zero padded (so `..:1` instead of `..:0001`).
* Even though it's not documented, it seems like `ip6addr` always generates
lower-case letters.
This will unfortunately mean that some IP hashes may change after the update.
Normal IPv4 hashes will most probably remain the same though.
4 years ago
|
|
|
try {
|
|
|
|
const ipParsed = parse(ip);
|
|
|
|
const ipStr = ipParsed.toString({
|
|
|
|
format: ipParsed.kind() === 'ipv4' ? 'v4' : 'v6',
|
|
|
|
zeroElide: false,
|
|
|
|
zeroPad: false,
|
|
|
|
});
|
|
|
|
const delimiter = ipParsed.kind() === 'ipv4' ? '.' : ':';
|
|
|
|
let split = ipStr.split(delimiter);
|
|
|
|
const qrange = split.slice(0,Math.floor(split.length*0.75)).join(delimiter);
|
|
|
|
const hrange = split.slice(0,Math.floor(split.length*0.5)).join(delimiter);
|
|
|
|
res.locals.ip = {
|
normalize IP addresses
Currently jschan takes the IP address as a string from the `X-Real-Ip` header,
which based on the frontend proxy configuration, OS settings, etc. can take
various forms:
IPv4 addresses can be given in normal IPv4 dotted notation (e.g. `1.2.3.4`) or
as an IPv4-mapped IPv6 address (e.g. `::ffff:1.2.3.4`). The problem is, that in
the latter case, node's `isIP` will report 6, so the code will try to split it
along colons, breaking hrange and qrange.
With IPv6 addresses, it's possible to elide runs of zeroes, so `::1` and
`0:0:0:0:0:0:0:1` (and also `0000:0000:0000:0000:0000:0000:0000:0001`)
represents the same address. Since it's pretty easy to get a /64 IPv6 block, a
spammer can abuse it, by spamming from `a:b:c:d::1` (`qrange=a:b:c:d`,
`hrange=a:b:c`), then from `a:b:c:d::1:1` (`qrange=a:b:c:d:`, `hrange=a:b:c`),
`a:b:c:d::1:1:1` (`qrange=a:b:c:d::1`, `hrange=a:b:c:d`) and
`a:b:c:d:1:1:1:1` (`qrange=a:b:c:d:1:1`, `hrange=a:b:c:d`). He practically got
two hranges and qrange is pretty much pointless for IPv6 addresses.
This change uses the `ip6addr` package to parse IP addresses and convert it to
some canonical form. This means:
* IPv4 and IPv4-mapped IPv6 addresses are converted to normal IPv4 notation.
* Zero are not elided in IPv6 (so you'll never see `::`).
* IPv6 addresses are not zero padded (so `..:1` instead of `..:0001`).
* Even though it's not documented, it seems like `ip6addr` always generates
lower-case letters.
This will unfortunately mean that some IP hashes may change after the update.
Normal IPv4 hashes will most probably remain the same though.
4 years ago
|
|
|
raw: ipHashPermLevel === -1 ? hashIp(ipStr) : ipStr,
|
|
|
|
single: hashIp(ipStr),
|
|
|
|
qrange: hashIp(qrange),
|
|
|
|
hrange: hashIp(hrange),
|
|
|
|
}
|
|
|
|
next();
|
normalize IP addresses
Currently jschan takes the IP address as a string from the `X-Real-Ip` header,
which based on the frontend proxy configuration, OS settings, etc. can take
various forms:
IPv4 addresses can be given in normal IPv4 dotted notation (e.g. `1.2.3.4`) or
as an IPv4-mapped IPv6 address (e.g. `::ffff:1.2.3.4`). The problem is, that in
the latter case, node's `isIP` will report 6, so the code will try to split it
along colons, breaking hrange and qrange.
With IPv6 addresses, it's possible to elide runs of zeroes, so `::1` and
`0:0:0:0:0:0:0:1` (and also `0000:0000:0000:0000:0000:0000:0000:0001`)
represents the same address. Since it's pretty easy to get a /64 IPv6 block, a
spammer can abuse it, by spamming from `a:b:c:d::1` (`qrange=a:b:c:d`,
`hrange=a:b:c`), then from `a:b:c:d::1:1` (`qrange=a:b:c:d:`, `hrange=a:b:c`),
`a:b:c:d::1:1:1` (`qrange=a:b:c:d::1`, `hrange=a:b:c:d`) and
`a:b:c:d:1:1:1:1` (`qrange=a:b:c:d:1:1`, `hrange=a:b:c:d`). He practically got
two hranges and qrange is pretty much pointless for IPv6 addresses.
This change uses the `ip6addr` package to parse IP addresses and convert it to
some canonical form. This means:
* IPv4 and IPv4-mapped IPv6 addresses are converted to normal IPv4 notation.
* Zero are not elided in IPv6 (so you'll never see `::`).
* IPv6 addresses are not zero padded (so `..:1` instead of `..:0001`).
* Even though it's not documented, it seems like `ip6addr` always generates
lower-case letters.
This will unfortunately mean that some IP hashes may change after the update.
Normal IPv4 hashes will most probably remain the same though.
4 years ago
|
|
|
} catch(e) {
|
|
|
|
console.error('Ip parse failed', e);
|
|
|
|
return res.status(400).render('message', {
|
|
|
|
'title': 'Bad request',
|
|
|
|
'message': 'Malformed IP' //should never get here
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|