November 29th, 2010 by depesz | Tags: , , , , , | 5 comments »
Did it help? If yes - maybe you can help me?

On 27th of November, Robert Haas committed patch which adds new, interesting contrib module:

New contrib module, auth_delay.
 
KaiGai Kohei, with a few changes by me.

Well, the commit log is far from informative, so let's see what and how it does.

First a word of introduction – in case you don't know KaiGai Kohei is a guy that is behind SE-PostgreSQL “thing", so even without checking what the module does – we can assume it's something related to security.

So. The basic idea is – slow down brute-force password cracking attempts.

Let's imagine that some bad cracker has access to host that he can use to try to connect to PostgreSQL. He can then try to run something like this (of course smarter, but that's just example):

perl -le 'my $x = "a"; while (1) {print $x; $x++; last if $x eq "zz"}' | \
    while read PGPASSWORD
    do
        export PGPASSWORD
        if psql -U test -d postgres -c 'select 1' &>/dev/null
        then
            echo "$PGPASSWORD"
            break
        fi
    done

Which is pretty simple brute force password cracker. How fast does it work? On my laptop it tests 140 passwords per second. This is of course too slow for any serious cracking, but with some trivial changes to the approach I can get *much* better speeds.

Now. What does auth_delay do, and how?

First, we need to change postgresql.conf, and make sure we have something like these lines:

shared_preload_libraries = 'auth_delay'
custom_variable_classes = 'auth_delay'
auth_delay.milliseconds = '500'

and then restart PostgreSQL. For comparison. Before the change time of trying to authenticate with bad password:

=$ time PGPASSWORD=bad psql -U test -d postgres -c 'select 1'
psql: FATAL:  password authentication failed for user "test"
 
real    0m0.009s
user    0m0.004s
sys     0m0.004s

With auth_delay loaded:

=$ time PGPASSWORD=bad psql -U test -d postgres -c 'select 1'
psql: FATAL:  password authentication failed for user "test"
 
real    0m0.509s
user    0m0.000s
sys     0m0.004s

Which is pretty cool. As it changes my cracker to effectively 2 passwords per second (in single thread) – thus making it impractical to use this approach.

Of course – this makes sense only in case you can have attackers get access to PostgreSQL, which they shouldn't. But still – it's really nice approach to alleviate some threats.

  1. 5 comments

  2. # John Doe
    Nov 29, 2010

    Judging by the number of brute force attacks I see in server logs, including postgresql logs, this modest hardening is a good step. Even better is to drop an IP after 10 or so bad attempts for 5 minutes or so.

  3. ++ should have been done a long time ago, is 500 milliseconds going to be the default? how does this affect if you script connects, query, disconnect, rinse repeat?

  4. Nov 29, 2010

    @Caleb:
    there is no default – it’s not enabled by default – you have to load it and configure on your own.

    as for such scripts – doesn’t matter – as long as you don’t make mistakes in password.

  5. # comboy
    Nov 30, 2010

    I’m not sure I get it. Unless there is some limit of connections per IP, why would I wait for answer before checking next combination?

  6. Nov 30, 2010

    @Comboy – well, you might want to crack in, but not bringing the server down by going over it’s max_connections.

Sorry, comments for this post are disabled.