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



  • @cartman82 said:

    options.users[auth.name] !== auth.pass

    Don't recognise the language, but is that doing what I think it's doing?



  • @PJH said:

    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.


  • Discourse touched me in a no-no place

    @cartman82 said:

    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.


  • Discourse touched me in a no-no place

    @cartman82 said:

    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…



  • @cartman82 said:

    There...
    > ### 127.0.0.1:8080

    After all...

    > ## 127.0.0.1:8080

    ... what could possibly go wrong?

    > # 127.0.0.1:8080

    Nothing to see here, move along. Make sure the developer (Hereafter referred to as "security consultant") invoices you for the downtime maintenance and emergency callouts



  • Hmmm. You would probably want to run haproxy on some other IP-number, say 127.0.0.2 or something. 😄



  • @Mikael_Svahnberg said:

    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... <!-- unless you don't run an ntp server with local clocks... -->



  • @Mikael_Svahnberg said:

    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?



  • @henke37 said:

    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.



  • @PleegWat said:

    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.



  • @PleegWat said:

    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.



  • @VinDuv said:

    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?



  • @cartman82 said:

    @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.



  • @PleegWat said:

    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



  • @PleegWat said:

    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.



  • @PleegWat said:

    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!



  • @cartman82 said:

    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 accept http://user:password@localhost:port/… and both will understand machine 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?



  • @PJH said:

    Did you misspell that, or did you find it like that?

    Don't annoy him, he's suffered enough. He doesn't like sand. It's coarse, and it gets everywhere.



  • @PJH said:

    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...


Log in to reply
 

Looks like your connection to What the Daily WTF? was lost, please wait while we try to reconnect.