I got requests from china,
The problem is that the req.url does not belong to my domain
Usually it is / or /login, etc
Was I hacked?
I'd like to know a rationale for this
const logger = function (req, res, next) {
let { url, ip, method, statusCode } = req
console.log(`${moment().format("L - hh:mm:ss")} `.red + `${method} `.green + `From: ` + `${ip?.replace("::ffff:", "")?.replace("::1", "localhost")} (${geoip.lookup(ip)?.country || "No IP"})`.cyan + ` : ` + `${req.user?.id || null} `.yellow + `At: ` + `${url} `.cyan)
next()
}
app.use(logger)
No, somebody merely sent proxy requests to you. They are directed to your server but request a full URL instead of a path. Protocol-wise they'd look like GET http://google.com/ HTTP/1.1 instead of just GET / HTTP/1.1 as you are used to. If your server were (mis)configured to honor such requests as proxy, it'd send another request itself to http://google.com/ and forward the response, but that doesn't happen in your case anyway so you can just ignore it.
See also this answer.
Related
I'd like to restrict the connection to the socket server only to a list of IP, is there any simple way to do that? I couldn't find any so far.
Thanks
This can be done a few ways..
Put some middleware on the connect to read the incoming request IP, check it against an IP list (an array), if it does not work then respond with an error code.
Use an ACL via NGINX, if their IP is not listed, NGINX will block the request
Not really based on IP but, control access via JWT. This would require you to generate the token and send it with each request but it does provide a little more security and you won't have to manage IP ranges. In this architecture your user would request the token, once the token is sent back you make the request to the socket server with the token in the head or as a req param. The socket server then decodes it, if it decodes correctly (meaning it valid) then let them connect, otherwise don't
Following #proxim0 post, I used a middleware :
io.use((socket, next) => {
fs.readFile(__dirname + '/config.json', function(err, file) {
if(err) return next(new Error("Internal error : " + err.message));
const config = JSON.parse(file.toString('utf8'));
if(!config.restrictAccess) return next();
var ip = socket.request.headers['x-forwarded-for'] || socket.request.socket.remoteAddress || null;
if(ip == null || config.allowedAddresses.indexOf(ip) == -1) {
console.log("Access denied from remote IP " + ip);
return next(new Error("Your IP address is not allowed to access this resource."));
}
next();
});
});
With a config.json file with the following format :
{
"restrictAccess": true,
"allowedAddresses": [
"127.0.0.1",
"123.456.789"
]
}
Any comment on improvment is welcome.
there I want to extract the domain name of incoming request using request module. I tried by looking at
request.headers,
request.headers.hostname
with no luck. Any help please?
I figured it out. Client domain is available at origin.
request.headers.origin
for ex:
if(request.headers.origin.indexOf('gowtham') > -1) {
// do something
}
Thanks!
You should use request.headers.host .
So you want the domain name of the client that is making the request?
Since Express only provides you with their IP-address, you have to reverse-lookup that IP-address to get the hostname. From there, you can extract the domain name.
In a middleware
const dns = require('dns');
...
app.use(function(req, res, next) {
dns.reverse(req.ip, function(err, hostnames) {
if (! err) {
console.log('domain name(s):', hostnames.map(function(hostname) {
return hostname.split('.').slice(-2).join('.');
}));
}
next();
});
});
However, very big disclaimer: making a DNS lookup for every request can have a severe performance impact.
I am developing a MEAN stack based web app, using Cloud9 and Heroku. I want all users to be forced to use HTTPS, and I found a nice way using Express middleware:
app.use(function(req,res,next) {
if(req.headers["x-forwarded-proto"] == "http") {
console.log("HTTP call detected, not allowed);
return res.redirect('https://' + req.hostname + req.path);
} else {
console.log("HTTPs call detected, allowed");
return next();
}
I hope this can work for all GET, POST, PUT, DELETE requests I receive, thinking that any call to HTTP should just be redirected to the identical, corresponding HTTPS request.
Now while this in itself seems to work, I am getting the CORS policy error
XMLHttpRequest cannot load
https://myappurlhere.herokuapp.com/app. No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://myappurlhere.herokuapp.com' is
therefore not allowed access. The response had HTTP status code 401.
Now I do understand the concept that this is the standard CORS policy that prevents cross-domain requests, but I am not exactly sure how I should approach this. I thought this would go as a same-domain-request, but apparently not.
I have seen that some people argue that a second server should run and catch HTTP traffic, redirecting, but I am only using one server. I don't understand why that couldn't work.
Any help greatly appreciated.
Regards
function requireHTTPS(req, res, next) {
if (!req.secure) {
return res.redirect('https://' + req.get('host') + req.url);
}
next();
}
app.use(requireHTTPS);
This worked for me.
I made HTTPS traffic forced now, but I don't think I solved the actual problem. What I am now using is this:
app.use(function (req, res, next) {
res.setHeader('Strict-Transport-Security', 'max-age=8640000; includeSubDomains');
if (req.headers['x-forwarded-proto'] && req.headers['x-forwarded-proto'] === "http") {
return res.redirect(301, 'https://' + req.host + req.url);
} else {
console.log("HTTPS call detected");
return next();
}
});
It seems that this effectively forces strict TLS, probably because of the header change. However, this also mean that the redirect never seems to happen - the test for "http" is always false. Hence, the problem with COTS redirect policy remains.
This is where I would really like some input - should I add some policies allowing cross domain requests? That sounds insecure, and why? I am on the same domain, I just want HTTPS.
Any input appreciated.
I'm trying to prevent open redirect attack. Please look at the code below and check for security:
var = require('url');
// http://example.com/login?redirect=http://example.com/dashboard
app.route('/login', function (req, res, next) {
var redirect = req.query.redirect,
paths = url.parse(redirect);
if (paths.host !== req.headers.host) {
return next(new Error('Open redirect attack detected'));
}
return res.redirect(redirect);
});
Is it enough for preventing open redirect attack or should I add anything else?
CWE-601: URL Redirection to Untrusted Site ('Open Redirect')
Description of Open Redirect:
An http parameter may contain a URL value and could cause the web application to redirect the request to the specified URL. By modifying the URL value to a malicious site, an attacker may successfully launch a phishing scam and steal user credentials. Because the server name in the modified link is identical to the original site, phishing attempts have a more trustworthy appearance.
The suggestion of input validation strategy to prevent open redirect attack:
Assume all input is malicious. Use an "accept known good" input validation strategy, i.e., use a whitelist of acceptable inputs that strictly conform to specifications. Reject any input that does not strictly conform to specifications, or transform it into something that does.
Do not rely exclusively on looking for malicious or malformed inputs (i.e., do not rely on a blacklist). A blacklist is likely to miss at least one undesirable input, especially if the code's environment changes. This can give attackers enough room to bypass the intended validation. However, blacklists can be useful for detecting potential attacks or determining which inputs are so malformed that they should be rejected outright.
Use a whitelist of approved URLs or domains to be used for redirection.
Use req.headers.host, req.host or req.hostname is insecure, because req.headers can be forged (eg. a HTTP request have a custom Host header to access a express app written in code below)
var url = require('url');
app.get('/login', function (req, res, next) {
var redirect = req.query.redirect,
targetUrl = url.parse(redirect);
console.log('req.headers.host: [%s]', req.headers.host);
console.log('req.host: [%s]', req.host);
console.log('req.hostname: [%s]', req.hostname);
if (targetUrl.host != req.headers.host) {
return next(new Error('Open redirect attack detected'));
}
return res.redirect(redirect);
});
Use curl to make a request:
$ curl -H 'Host: malicious.example.com' 'http://localhost:3012/login?redirect=http://malicious.example.com' -i
HTTP/1.1 302 Found
X-Powered-By: Express
Location: http://malicious.example.com
Vary: Accept
Content-Type: text/plain; charset=utf-8
Content-Length: 54
Date: Mon, 13 Jun 2016 06:30:55 GMT
Connection: keep-alive
$ #server output
req.headers.host: [malicious.example.com]
req.host: [malicious.example.com]
req.hostname: [malicious.example.com]
I suggest you use whitelist to validate input, a example code below:
const WHITELIST_TO_REDIRECT = new Set(["localhost:3012", "www.realdomain.com"]);
app.get('/login', function (req, res, next) {
var redirect = req.query.redirect,
targetUrl = url.parse(redirect);
console.log("req.hostname: [%s]", req.hostname);
console.log("url.host: [%s]", targetUrl.host);
if (!WHITELIST_TO_REDIRECT.has(targetUrl.host)) {
return next(new Error('Open redirect attack detected'));
}
return res.redirect(redirect);
});
In this situation I'd use a HMAC. This will allow the login controller to verify that the redirect parameter was generated by someone that knows the secret key.
When you generate the "login" url you add a HMAC digest of the redirect parameter to the url along with the redirect parameter itself.
The login handler can use the HMAC to ensure that the redirect parameter was generated by a trusted server that knows the HMAC secret key thereby preventing open redirect attacks.
e.g.
var crypto = require('crypto');
var secretKey = 'change-me';
var loginUrl = 'http://example.com/login'
// called to work out where to redirect to for login
function getLoginUrl(redirectBackUrl) {
var sig = crypto.createHmac('sha1', secretKey)
.update(redirectBackUrl)
.digest('hex');
return loginUrl
+ '?redirect='
+ encodeURIComponent(redirectBackUrl)
+'&sig=' + sig;
}
// called by the login function to ensure that the
// redirect parameter is valid
function isRedirectUrlValid(url, sig) {
var expectedSig
= crypto.createHmac('sha1', secretKey)
.update(url)
.digest('hex');
return expectedSig === sig;
}
How do I prevent people from hotlinking images on an app using nodejs and express? I'm hosting on my own server. Is this something I would need to build into the app.js file? Thanks.
You need to write middleware to only accept certain valid referers from your website. Be sure to accept modern search engines, social media sites, and no referers too.
If you are using nginx with node, here is a solution.
Basically, it checks for referrer and returns 403 if not authorized.
You could also write a middleware for express using the same logic.
Create a middleware to check Referer in HTTP request header, with domains of popular search engines, media, and websites.
Add this in your app.js file. Assuming you are using express 4.x.
app.use('/', (req , res , next) => {
const referrer = req.get('Referrer');
if (referrer == null) {
next(); // referrer is an optional http header, it may not exist
}
if (["https://www.facebook.com", "https://www.google.com"].includes(referrer)) { // compare referrer with your whitelist
res.status(403).send("You are not allowed to view this photo!"); // block the request
}
next();
});
app.get('/',(req,res)=>{
res.sendFile(`PATH_OF_YOUR_IMAGE`);
});
Find out more about Referer HTTP header
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referer
The issue using referrer is that most hotlinks do not use this header and browsers do not send it either with the image in an tag or src="" property... if your website is using cookies and cookies are accepted by the users of your website you could verify the presence of the cookie in the request by adding the following server.get() funcion before the static folder generic case (assuming you use an express server):
server.get(['/assets/img/**','/assets/images/**'], (req, res, next) => {
if(req.url == '/assets/images/logo*.*') {
next();
}
console.log('requested image' + req.url);
let maxage;
if (!isBot(req)) {
let cookieLang = req.cookies['my_cookie'];
if (!cookieLang) {
console.log('no cookie ');
res.status(403);
maxage = 5; // 5 seconds, in order not to damage your website visibility if viewer ends up visiting your site
res.setHeader('Cache-Control', 'public, max-age='+maxage);
res.setHeader('Expires', new Date(Date.now() + maxage * 1000).toUTCString());
res.sendFile(join(distFolder + '/assets/images/logo.png'));
} else {
console.log('withcookie ' + cookieLang);
res.set({'Cache-Control': 'max-age=31536000'});
res.sendFile(join(distFolder + req.url));
}
} else {
res.set({'Cache-Control': 'max-age=31536000'});
res.sendFile(join(distFolder + req.url));
}
});
server.get('*.*', express.static(distFolder, {
maxAge: '1y'
}));
in my case in order to help me benefit from hotlinks I return the logo of our company instead of the requested image :o) but you could return just nothing.
Also in this example there is a isBot() funcion where I check the user agent in order to let WhatsApp, Twitter and other bots get the required image for company visibility when adding a link in a post in those social networks.
Any idea of improvement welcomed !