3 steps to WTF
-
Step one:
OK, now that the project is in testing, we need some simple way to secure our stuff. Some basic kind of authentication, just so that we aren't fully exposed.
var basicAuthLib = require("basic-auth"); module.exports = function basicAuth(options) { return function basicAuthMiddleware(req, res, next) { if (req.user) { // Already authorized return next(); } var auth = basicAuthLib(req); if (!options.users[auth.name] || options.users[auth.name] !== auth.pass) { return unauthorized(res, options.realm); } req.user = auth.name; return next(); }; function unauthorized(res, realm) { res.statusCode = 401; res.setHeader("WWW-Authenticate", 'Basic realm="' + realm + '"'); res.end("Unauthorized"); } };
Step two:
How do I script curls and wgets from local console when I'm using basic auth? And the fucking postman stuff? Ugh! So bothersome to setup for every fucking little thing.
Let me just add a quick exclusion to localhost (127.0.0.1) addresses. After all, if an intruder is in position to reach us through localhost, we have bigger problems than this API-s security.
//... if (options.excludeUser && req.connection.remoteAddress === "127.0.0.1") { req.user = options.excludeUser; return next(); } var auth = basicAuthLib(req); //...
Step three:
The project is growing and we need to put some architecture around it. Instead of listening directly on a stupid dev port, let's put it behind haproxy. That's what all the big boys are doing.
Oh, and haproxy can be on the same server as the API. We have plenty of resources.
frontend cartapi *:80 default_backend cartapi backend cartapi balance roundrobin server cartapi1 127.0.0.1:8080 check
There...
127.0.0.1:8080
After all...
127.0.0.1:8080
... what could possibly go wrong?
127.0.0.1:8080
-
options.users[auth.name] !== auth.pass
Don't recognise the language, but is that doing what I think it's doing?
-
Don't recognise the language, but is that doing what I think it's doing?
Checks if user/pass provided in the header matches any of the supplied user/pass pairs provided in options. That part is OK.
-
That part is OK.
Only as an interim measure; it's currently keeping passwords in the clear and that's very much not best practice. OTOH, it's not the first thing you need to fix.
-
There's a different system for web users (salting and all that jazz). This is just for internal API, a few hardcoded users etc.
-
This is just for internal API, a few hardcoded users etc.
I know. I've been there. Even so, it's better to be cautious.
A word of caution. If this is an API that you're running, you might need to be very careful with adding bcrypt()ing; that can have a significant performance impact. It's not a big issue with user-facing stuff as they tend to use clients that support cookies, but other types of API users are more often fully session-less, which requires reestablishing the auth on each request…
-
Hmmm. You would probably want to run haproxy on some other IP-number, say 127.0.0.2 or something.
-
Hmmm. You would probably want to run haproxy on some other IP-number, say 127.0.0.2 or something.
I'd stay away from 127.127.0.0/16 though...
-
Hmmm. You would probably want to run haproxy on some other IP-number, say 127.0.0.2 or something.
I wouldn't even know how to do that.
-
Looks fine to me, just read the Forwarded-For header instead. Your proxy does that right?
-
Looks fine to me, just read the Forwarded-For header instead. Your proxy does that right?
Pre-empting in case you're serious: Proxies typically append to an existing Forwarded-For header.
-
Sure, whatever. I know how to fix it. The problem was fucking it up in the first place.
-
Pre-empting in case you're serious: Proxies typically append to an existing Forwarded-For header.
I hope node.js has a built-in function to handle this correctly...
-
Yeah, but that means an attacker can influence it, so you can't use its contents for anything access-related.
-
Yeah, but that means an attacker can influence it, so you can't use its contents for anything access-related.
The first value you find in it will be the one added by your reverse proxy, so you can use it. Of course, your application needs to do this only if running behind a reverse proxy.
-
The first value you find in it will be the one added by your reverse proxy, so you can use it. Of course, your application needs to do this only if running behind a reverse proxy.
Exactly. Right now I'm doing this manually. Later, I found there's a built-in functionality inside express.js for parsing this, but I feel no pressing urgency to wobble the code jelly for this.
The only difference is they allow you to specify an array of proxy IP-s. Useful if you're behind some kind of complex proxy labyrinth. I'm not. I just take the last one in chain and am certain that's the IP request came in through.
And yeah, I don't see how an attacker can influence it in any way, if I'm just taking the value I know my own proxy had added. @PleegWat, do you know something we don't?
-
@PleegWat, do you know something we don't?
Don't think so. Just as long as you're not blindly taking the oldest address in the list.
-
Don't think so. Just as long as you're not blindly taking the oldest address in the list.
That's what I'm doing. The question is, what's wrong with this?
-
What happens if I send your proxy a request with the header
X-Forwarded-For: 127.0.0.1
-
What happens if I send your proxy a request with the header X-Forwarded-For: 127.0.0.1
My proxy adds:
X-Forwarded-For: 127.0.0.1, <your real IP>
I take just the end (
<your real IP>
) and use that.
-
So you're blindly taking the newest one not the oldest. Which is OK because you know your proxy is adding an IP.
-
So you're blindly taking the newest one not the oldest. Which is OK because you know your proxy is adding an IP.
Yeah. I see, that's what you meant by "oldest". I'm taking the latest.
The only way this can bite me in the butt is if I try taking the last IP while I'm not behind proxy.
Overall, the whole idea of trusting security with this feels brittle. I'll rework it.
-
If it makes you feel better @cartman82 TIL I can ditch Apache for HAProxy for my side project. Thanks!
-
How do I script curls and wgets from local console when I'm using basic auth?
This is TRWTF. Both have options for it (
curl -u user:password …
,wget --user=user --password=password …
), both accepthttp://user:password@localhost:port/…
and both will understandmachine localhost login user password password
in.netrc
. It takes about 1 minute to find this in corresponding man pages.
-
-
The estimate process is just such a huge PITA.
-
Did you misspell that, or did you find it like that?
-
Did you misspell that, or did you find it like that?
Guess. Both solutions indicate low level of effort, so both are viable according to the image...