pgcrypto Postgresql PGP pgp_pub_decrypt with password - postgresql

I have been experiencing few problems with data encryption over pgcrypto with Postgresql 8.4.
First case : works fine :
select pgp_pub_decrypt(pgp_pub_encrypt('fsck',
dearmor(pubkey),'compress-algo=1,
cipher-algo=aes256'),dearmor(seckey)) from keytbl where keytbl.id=1
-> returns "fsck"
key 1 is pgp with no password
Second case : doesn't work
select pgp_pub_decrypt(pgp_pub_encrypt('fsck',
dearmor(pubkey),'compress-algo=1,
cipher-algo=aes256'),dearmor(seckey),'password') from keytbl where
keytbl.id=2
-> returns ERREUR: Corrupt data
When i generate keys with password pgcrypto doesn't want to decrypt the message crypted with the public key ....
Anyone got a guess ? This is driving me mad...

This appears to be a known bug in at least 8.4 and 9.0. I have avoided this in the past by avoiding using the passphrase functionality and using pgp_sym_encrypt and pgp_sym_decrypt to manage the keys' passphrases.
In general if this is giving you a problem your best option is to encrypt the keys with a passphrase separately using well-tested functions.
To give you an idea of how we do it:
create or replace function user__replace_keys
(in_public_key bytea, in_private_key bytea, in_passphrase text)
RETURNS user_key
LANGUAGE SQL AS
$$
UPDATE user_key
SET last_resort_key = pgp_pub_encrypt(
pgp_pub_decrypt(
last_resort_key,
pgp_sym_decrypt_bytea(priv_key, $3)
), $2
),
pub_key = $2,
priv_key = pgp_sym_encrypt_bytea($2, $3)
WHERE login = SESSION_USER
RETURNING *;
$$;
Note that the private key sent to the server must not be password encryted. We might actually generate it on the server or in middleware to avoid problems. This avoids bugs of the sort you are experiencing.

Related

missing "=" after "conn_db_link" in connection info string

I've been trying to figure out the issue here. This is running in a bash script. And I am getting the error below. However, if I run the INSERT statement manually from within psql, it works fine.
psql -U zrec -d zrec -t -c "INSERT INTO approved_recommendations
SELECT * FROM dblink('conn_db_link','SELECT * FROM approved_recommendations') AS x(
ticker varchar,
status varchar,
approved_recommendation integer,
yesterday_recommendation integer,
prior_recommendation integer,
date_approved date,
approved_by integer,
date_suggested date,
suggested_by integer,
recommendation_date date,
flag_24hours integer,
display_processed character varying,
display_processed_date date);"
ERROR: could not establish connection
DETAIL: missing "=" after "conn_db_link" in connection info string
Any thoughts on what I am missing? Did I set up the DB Link wrong? It works manually.....
If you use SELECT dblink_connect('conn_db_link',...), you have to repeat it in each session. The setting does not survive. However, if you create a foreign server (like 'server_vegeta_remote' in the tutorial) then that does survive between sessions. You can use the name of the foreign server directly calls in dblink.
I don't see the point of SELECT dblink_connect('conn_db_link','server_vegeta_remote') in the tutorial. Why not just use 'server_vegeta_remote' directly, rather than creating temporary named connection based on it?

Can I protect against SQL injection attacks by wrapping SQL in PostgreSQL functions?

Can I use a function like this
CREATE FUNCTION create_user(_firstName text)
RETURNS void AS $$
INSERT INTO user_account (first_name) VALUES (_firstName);
$$ LANGUAGE sql;
On the server to protect against SQL injection attacks? Then I can just run this on the client,
client.query(`SELECT create_user(${someUserInput})...`
Or will I still need to use parameterized queries with placeholders,
client.query(`SELECT create_user($1)`, [someUserInput])
Problem (exploit)
client.query(`select create_user(${someUserInput})`
The problem there is what happens if
let someUserInput = `'foo'); DROP DATABASE bar;`;
That will get sent to your call as,
client.query("select create_user('foo'); DROP DATABASE bar;")`
And, that would be bad. Yes, the argument to create_user is protected against injection, but the call to that isn't.
Solutions
Use placeholders (obvious choice: most failsafe and secure solution.)
Ensure someUserInput is properly quoted
Use the client-library to quote it with something like PQescapeLiteral
Use a second run to the server to quote it with quote_literal (requires placeholders anyway). SELECT quote_literal($1);
I would not try to create the quoting-mechanism myself.

Setting a User's Valid Until date in the future

I was trying to see if there was a way to automatically set a user's VALID UNTIL value three months in the future without having to type out the literal date. Tried the following:
alter user rchung set valuntil = dateadd(day,90,GETDATE());
alter user rchung set valuntil = select dateadd(day,90,GETDATE());
both failed with a syntax error.
alter user rchung valid until dateadd(day,90,GETDATE());
also failed with a syntax error.
Anyone have any success with this?
TIA,
Rich
It appears that this is a limitation on the PostgreSQL side.
CREATE USER, like pretty much all utility statements in Postgres,
won't do any expression evaluation --- the parameters have to be
simple literal constants.
VALID UNTIL programmatically in SQL
Since Amazon Redshift doesn't support plpgsql like PostgreSQL, client side scripting is really the only option. If you're using a semi-modern version (9.3+) of psql the following works:
select dateadd(day,90,GETDATE()) as expiry; \gset
alter user myuser valid until :'expiry';

Perl crypt seemingly not working

I am trying to use the crypt function to verify a user's details in database:
my $sql = $dbh->prepare("SELECT COUNT(id) FROM tblUsers WHERE UPPER(Username)=UPPER(?) AND Password=?");
my $count;
$sql->execute(
$args{login_username},
crypt($args{login_password},$args{login_username})
);
but it returns 0 rows (with the password definitely correct)
the line:
$sql->execute($args{login_username},$args{login_password});
works perfectly.
Any ideas as to what might be going wrong?
It looks like the password stored in the database unencrypted. To compare the values in encrypted form you need to encrypt them at the database side as well.
MySQL has an ENCRYPT function, so you can write
my $sql= $dbh->prepare(<<SQL);
SELECT COUNT(id)
FROM tblUsers
WHERE UPPER(Username) = UPPER(?)
AND ENCRYPT(Password, Username) = ?
SQL
$sql->execute($args{login_username}, crypt($args{login_password},$args{login_username}));
but the exact syntax depends on the platform you are using.

Recognizing invalid dates in postgresql

I am trying to parse dirty input into postgres tables. I have a problem with a 'date' field occasionally containing non-dates such as '00000000' or '20100100'. pg refuses to accept these, and rightly so.
Is there a way to have postgres recognize invalid dates (or only valid dates, if that works better), so I can substitute a sensible default?
(I've considered building a table listing the dates I'm willing to accept, and use that in a sub-select, but that seems awfully inelegant.)
Cheers,
Jurgen
http://www.tek-tips.com/viewthread.cfm?qid=1280050&page=9
A more generic approach than the above:
create function safe_cast(text,anyelement)
returns anyelement
language plpgsql as $$
begin
$0 := $1;
return $0;
exception when others then
return $2;
end; $$;
Used like this:
select safe_cast('Jan 10, 2009', '2011-01-01'::timestamp)
select safe_cast('Jan 10, 2009', null::timestamp)
Credited to the friendly dudes at the #postgresql irc channel. :)
You could write a pgsql function with an exception handling block.