Re: Prevent query cancel packets from being replayed by an attacker (From TODO) - Mailing list pgsql-hackers

From Sebastian Cabot
Subject Re: Prevent query cancel packets from being replayed by an attacker (From TODO)
Date
Msg-id CAEmynK8m7GYwdob_R0Uc_KAT2L_9xeutM9FM74p7S3w0UHzZKw@mail.gmail.com
Whole thread Raw
In response to Re: Prevent query cancel packets from being replayed by an attacker (From TODO)  (Laurenz Albe <laurenz.albe@cybertec.at>)
List pgsql-hackers
On Wed, Mar 31, 2021 at 5:44 PM Laurenz Albe <laurenz.albe@cybertec.at> wrote:
>
> On Wed, 2021-03-31 at 16:54 +0300, Sebastian Cabot wrote:
> > My name is Sebastian and I am new to this list and community.
> > I have been following PostgreSQL for several years and I love the work done on
> >  it, but I never had the chance (time) to join.
> >
> > I was going through the TODO list and studied the code and the thread discussing the
> >  optional fixes  and I think I have a solution to this one which has the following advantages:
> > 1. No change to the protocol is needed
> > 2. Can be implemented in a both forward and backward compatible way
> > 3. Does not require any shared memory trickery
> > 4. Is immune to brute force attacks  (probably)
> >
> > If this is still something we wish to fix I will be happy to share the details (and
> >  implement it) - I don't wish to burden you with the details if there is no real interest in solving this.
>
> Thank you for your willingness to help!
>
> Sure, this is the place to discuss your idea, go ahead.
>
> Right now is the end of the final commitfest for v14, so people
> are busy getting patches committed and you may get less echo than normally.
>
> Yours,
> Laurenz Albe
>
Thank you Laurenz.
I will post the details. Hopefully some developers will find the time
to review the idea.

Summary of the problem:
To cancel a query a query cancel message is sent. This message is
always sent unencrypted even if SSL is enabled because there is a need
for the cancel message to be sent from signals. This opens up the
possibility of a replay attack since the cancel message contains a
cancel auth key which does not change after the backend is started.

Summary of the solution proposed in the discussion on the thread:
https://www.postgresql.org/message-id/489C969D.8020800@enterprisedb.com
Not all the details were agreed upon but the trend was as follows:
1. The current way of canceling a request shall continue to be
supported (at least until the next protocol version update)
2. A new cancel message format and processing method shall be
supported alongside the original and the server shall advertise in the
startup runtime parameters whether this new method is supported
3. A new libpq client that supports the new method shall know to use
it if the server supports it
4. The new method involves the client sending a hashed representation
of the cancel authkey so that the key is never sent in clear text
5. There will be a mechanism (an integer which is incremented before
every cancel message) to generate a new hash for every request
6. When using the new method the postmaster shall check that the
integer of a new request is larger than the one used for the last
request
8. There will be a postmaster configuration parameter to decide
whether the new method is enforced or is optional

The above suggestion is not bad and quite appealing but in reality it
forms a protocol change, It requires changes to both the server and
the client.

Details for new suggestion:

1. We notice that the incentive for the elaborate solution proposed is
that the cancel auth key cannot be regenerated easily (Without
touching shared memory in a way that is not trivial with the current
implementation especially for non EXEC_BACKEND).
2. If a new cancelation authkey can be regenerated once a cancel
message was sent then we could just send the new key and no changes to
libpq or the protocol are required.
2.1. One problem with such an approach is that other clients (JDBC?)
only read the key at startup and this shall be addressed below
3. There will be a postmaster configuration parameter to decide
whether the new method is enforced or is optional

Here is how the new design will allow generating a new "random"
authkey without any shared memory trickery:
We will add two backend variables:
int32 original_cancel_key; (To allow cancels from client that do not
update the key in case the new method is not enforced by the server)
char  random_bits[32];

POSTMASTER:
When creating a new backend the original_cancel_key shall have a copy
of the random key generated. ramdom_bits shall be initialized using
pg_strong_random

When a new cancel message request arrives:
POSTMASTER:
(If the new method is not enforced then also a match against the
original_cancel_key shall be accepted)
If the message's cancelAuthCode matches the current cancel_key then
1. Generate a new key by a deterministic algorithm from random_bits
2. regenerate random_bits using a SHA256 of the current key and the
current random bits
3. As in today send SIGINT to backend
BACKEND:
Upon receiving SIGINT mark that a new key should be generated.
When appropriate in the loop:
1. Generate a new key by a deterministic algorithm from random_bits
(same way POSTMASTER did so it will get the exact same key)
2. regenerate random_bits using a SHA256 of the current key and the
current random bits (The same way POSTMANGER did so it will get the
same randomness)
3. Send new key to client

So if the server does not enforce the new method old clients will work
just the same. For clients using the new libpq or derived
implementations the new secure cancel shall be the default whether the
server enforces it or not.

I welcome any comments or questions.

Thanks
Sebastian



pgsql-hackers by date:

Previous
From: Robert Haas
Date:
Subject: Re: New IndexAM API controlling index vacuum strategies
Next
From: Pavel Stehule
Date:
Subject: Re: Idea: Avoid JOINs by using path expressions to follow FKs