java.jdbc

Connection string query parameters aren't decoded correctly

Details

  • Type: Defect Defect
  • Status: Open Open
  • Priority: Major Major
  • Resolution: Unresolved
  • Affects Version/s: None
  • Fix Version/s: None
  • Component/s: None
  • Labels:
  • Environment:
    clojure 1.9.0, clojure.java.jdbc 0.7.7

Description

The parse-properties-uri function uses uri.getQuery(), which decodes the entire query string. This means that any query string parameters that include the characters = or & (even when properly encoded) will be lost in the subsequent steps to split apart the k/v pairs.

Instead, it should split the raw query string on "&", then the raw key-value pairs on "=". Then, decode each name or value independently using e.g. URLDecoder.

My use case is that I am passing a CA certificate in PEM format to Postgres in the connection string. Since the PEM data is base64, it sometimes ends with = padding, for example:

-----BEGIN CERTIFICATE-----
...
pvjw==
-----END CERTIFICATE-----

The parameter in the connection string is properly encoded:

sslfactoryarg=-----BEGIN+CERTIFICATE...pvjw%3D%3D%0A-----END+CERTIFICATE-----

When I use this connection string with clojure.java.jdbc, I receive an exception (java.security.cert.CertificateException: java.io.IOException: Incomplete data) because the equals padding and everything after it was truncated by this split.

Perhaps a more compelling use case for most folks is passwords. Postgres accepts passwords as connection string query parameters. If the password contains an equals sign or ampersand, you will receive baffling authentication failures:

First, set up a few users. (This also requires altering pg_hba.conf from the default.)

postgres=# CREATE USER user_control PASSWORD 'no-special-characters';
CREATE ROLE

postgres=# CREATE USER user_eq PASSWORD 'password contains =';
CREATE ROLE

postgres=# CREATE USER user_amp PASSWORD 'password contains &';
CREATE ROLE

Now try connecting with clojure.java.jdbc:

user=> (require '[clojure.java.jdbc :as j]
  #_=>          '[clojure.test :refer :all])
nil

user=> (import '[java.net URLEncoder])
java.net.URLEncoder

user=> (defn test-conn
  #_=>   [user pass]
  #_=>   (let [db (str "postgresql://127.0.0.1:5432/postgres"
  #_=>                 "?user=" (URLEncoder/encode user)
  #_=>                 "&password=" (URLEncoder/encode pass))]
  #_=>     (j/query db "select current_user")))
#'user/test-conn

user=> (test-conn "user_control" "no-special-characters")
({:current_user "user_control"})

user=> (test-conn "user_eq" "password contains =")
...
PSQLException FATAL: password authentication failed for user "user_eq"  org.postgresql.core.v3.ConnectionFactoryImpl.doAuthentication (ConnectionFactoryImpl.java:438)

user=> (test-conn "user_amp" "password contains &")
...
PSQLException FATAL: password authentication failed for user "user_amp"  org.postgresql.core.v3.ConnectionFactoryImpl.doAuthentication (ConnectionFactoryImpl.java:438)

Activity

Hide
Sean Corfield added a comment -

For now, can you work around this with:

(let [db {:dbtype "postgres" :dbname "postgres"
          :user user :password pass
          :sslfactoryarg "-----BEGIN+CERTIFICATE...pvjw%3D%3D%0A-----END+CERTIFICATE-----"}]
  ...)

The hash map form for db-spec is preferred and should avoid any URI encoding/parsing issues.

Show
Sean Corfield added a comment - For now, can you work around this with:
(let [db {:dbtype "postgres" :dbname "postgres"
          :user user :password pass
          :sslfactoryarg "-----BEGIN+CERTIFICATE...pvjw%3D%3D%0A-----END+CERTIFICATE-----"}]
  ...)
The hash map form for db-spec is preferred and should avoid any URI encoding/parsing issues.

People

Vote (0)
Watch (0)

Dates

  • Created:
    Updated: