Skip to content

Commit

Permalink
SQL escape passwords
Browse files Browse the repository at this point in the history
To correctly SQL escape passwords, escaping \ first is
required. Then we need to escape ' in the password to
prevent it being treated as a end of SQL statement quote.

All escaping needs to use \, so we cannot be in
NO_BACKSLASH_ESCAPES sql_mode otherwise no escaping will
work.

As an added complication when using the config file within
the entrypoint, for waiting until MariaDB can initialize,
a password mechanism a different escaping applies
(https://mariadb.com/kb/en/configuring-mariadb-with-option-files/).

The printf %q is close, however it provides a much
richer escape of non-printable characters than what can be
read in the configuration file. As such the password
complexity of the root password is limited to escaping
\n, \r, \t, \b, \s, \", \', and \\ while for a user
MARIADB_PASSWORD anything, including positively crazy strings
such as \0 will work.

Closes MariaDB#183
  • Loading branch information
grooverdan committed Mar 12, 2021
1 parent 0f355be commit 7c0795b
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 40 deletions.
26 changes: 18 additions & 8 deletions 10.2/docker-entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -258,24 +258,31 @@ docker_setup_db() {
fi
# Sets root password and creates root users for non-localhost hosts
local rootCreate=
# SQL escape the root password, \ followed by '
local rootPasswordEscaped=${MARIADB_ROOT_PASSWORD//\\/\\\\}
rootPasswordEscaped=${rootPasswordEscaped//\'/\\\'}

# default root to listen for connections from anywhere
if [ -n "$MARIADB_ROOT_HOST" ] && [ "$MARIADB_ROOT_HOST" != 'localhost' ]; then
# no, we don't care if read finds a terminating character in this heredoc
# https://unix.stackexchange.com/questions/265149/why-is-set-o-errexit-breaking-this-read-heredoc-expression/265151#265151
read -r -d '' rootCreate <<-EOSQL || true
CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY '${MARIADB_ROOT_PASSWORD}' ;
CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY '${rootPasswordEscaped}' ;
GRANT ALL ON *.* TO 'root'@'${MARIADB_ROOT_HOST}' WITH GRANT OPTION ;
EOSQL
fi

# tell docker_process_sql to not use MARIADB_ROOT_PASSWORD since it is just now being set
docker_process_sql --dont-use-mysql-root-password --database=mysql <<-EOSQL
# we need the SQL_MODE NO_BACKSLASH_ESCAPES mode to be clear for the password to be set.
# --binary-mode to save us from the semi-mad users who put nulls into passwords
docker_process_sql --dont-use-mysql-root-password --database=mysql --binary-mode <<-EOSQL
-- What's done in this file shouldn't be replicated
-- or products like mysql-fabric won't work
SET @@SESSION.SQL_LOG_BIN=0;
SET @@SESSION.SQL_MODE=REPLACE(@@SESSION.SQL_MODE, 'NO_BACKSLASH_ESCAPES', '');
DELETE FROM mysql.user WHERE user NOT IN ('mysql.sys', 'mariadb.sys', 'mysqlxsys', 'root') OR host NOT IN ('localhost') ;
SET PASSWORD FOR 'root'@'localhost'=PASSWORD('${MARIADB_ROOT_PASSWORD}') ;
SET PASSWORD FOR 'root'@'localhost'=PASSWORD('${rootPasswordEscaped}') ;
-- 10.1: https://github.com/MariaDB/server/blob/d925aec1c10cebf6c34825a7de50afe4e630aff4/scripts/mysql_secure_installation.sh#L347-L365
-- 10.5: https://github.com/MariaDB/server/blob/00c3a28820c67c37ebbca72691f4897b57f2eed5/scripts/mysql_secure_installation.sh#L351-L369
DELETE FROM mysql.db WHERE Db='test' OR Db='test\_%' ;
Expand All @@ -294,7 +301,13 @@ docker_setup_db() {

if [ -n "$MARIADB_USER" ] && [ -n "$MARIADB_PASSWORD" ]; then
mysql_note "Creating user ${MARIADB_USER}"
docker_process_sql --database=mysql <<<"CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY '$MARIADB_PASSWORD' ;"
# SQL escape the user password, \ followed by '
local userPasswordEscaped=${MARIADB_PASSWORD//\\/\\\\}
userPasswordEscaped=${userPasswordEscaped//\'/\\\'}
docker_process_sql --database=mysql --binary-mode <<-EOSQL_USER
SET @@SESSION.SQL_MODE=REPLACE(@@SESSION.SQL_MODE, 'NO_BACKSLASH_ESCAPES', '');
CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY '$userPasswordEscaped';
EOSQL_USER

if [ -n "$MARIADB_DATABASE" ]; then
mysql_note "Giving user ${MARIADB_USER} access to schema ${MARIADB_DATABASE}"
Expand All @@ -308,10 +321,7 @@ _mysql_passfile() {
# the client command will use process substitution to create a file on the fly
# ie: --defaults-extra-file=<( _mysql_passfile )
if [ '--dont-use-mysql-root-password' != "$1" ] && [ -n "$MARIADB_ROOT_PASSWORD" ]; then
cat <<-EOF
[client]
password="${MARIADB_ROOT_PASSWORD}"
EOF
echo -e "[client]\npassword=$(printf \"%q\" "${MARIADB_ROOT_PASSWORD}")\n"
fi
}

Expand Down
26 changes: 18 additions & 8 deletions 10.3/docker-entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -258,24 +258,31 @@ docker_setup_db() {
fi
# Sets root password and creates root users for non-localhost hosts
local rootCreate=
# SQL escape the root password, \ followed by '
local rootPasswordEscaped=${MARIADB_ROOT_PASSWORD//\\/\\\\}
rootPasswordEscaped=${rootPasswordEscaped//\'/\\\'}

# default root to listen for connections from anywhere
if [ -n "$MARIADB_ROOT_HOST" ] && [ "$MARIADB_ROOT_HOST" != 'localhost' ]; then
# no, we don't care if read finds a terminating character in this heredoc
# https://unix.stackexchange.com/questions/265149/why-is-set-o-errexit-breaking-this-read-heredoc-expression/265151#265151
read -r -d '' rootCreate <<-EOSQL || true
CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY '${MARIADB_ROOT_PASSWORD}' ;
CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY '${rootPasswordEscaped}' ;
GRANT ALL ON *.* TO 'root'@'${MARIADB_ROOT_HOST}' WITH GRANT OPTION ;
EOSQL
fi

# tell docker_process_sql to not use MARIADB_ROOT_PASSWORD since it is just now being set
docker_process_sql --dont-use-mysql-root-password --database=mysql <<-EOSQL
# we need the SQL_MODE NO_BACKSLASH_ESCAPES mode to be clear for the password to be set.
# --binary-mode to save us from the semi-mad users who put nulls into passwords
docker_process_sql --dont-use-mysql-root-password --database=mysql --binary-mode <<-EOSQL
-- What's done in this file shouldn't be replicated
-- or products like mysql-fabric won't work
SET @@SESSION.SQL_LOG_BIN=0;
SET @@SESSION.SQL_MODE=REPLACE(@@SESSION.SQL_MODE, 'NO_BACKSLASH_ESCAPES', '');
DELETE FROM mysql.user WHERE user NOT IN ('mysql.sys', 'mariadb.sys', 'mysqlxsys', 'root') OR host NOT IN ('localhost') ;
SET PASSWORD FOR 'root'@'localhost'=PASSWORD('${MARIADB_ROOT_PASSWORD}') ;
SET PASSWORD FOR 'root'@'localhost'=PASSWORD('${rootPasswordEscaped}') ;
-- 10.1: https://github.com/MariaDB/server/blob/d925aec1c10cebf6c34825a7de50afe4e630aff4/scripts/mysql_secure_installation.sh#L347-L365
-- 10.5: https://github.com/MariaDB/server/blob/00c3a28820c67c37ebbca72691f4897b57f2eed5/scripts/mysql_secure_installation.sh#L351-L369
DELETE FROM mysql.db WHERE Db='test' OR Db='test\_%' ;
Expand All @@ -294,7 +301,13 @@ docker_setup_db() {

if [ -n "$MARIADB_USER" ] && [ -n "$MARIADB_PASSWORD" ]; then
mysql_note "Creating user ${MARIADB_USER}"
docker_process_sql --database=mysql <<<"CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY '$MARIADB_PASSWORD' ;"
# SQL escape the user password, \ followed by '
local userPasswordEscaped=${MARIADB_PASSWORD//\\/\\\\}
userPasswordEscaped=${userPasswordEscaped//\'/\\\'}
docker_process_sql --database=mysql --binary-mode <<-EOSQL_USER
SET @@SESSION.SQL_MODE=REPLACE(@@SESSION.SQL_MODE, 'NO_BACKSLASH_ESCAPES', '');
CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY '$userPasswordEscaped';
EOSQL_USER

if [ -n "$MARIADB_DATABASE" ]; then
mysql_note "Giving user ${MARIADB_USER} access to schema ${MARIADB_DATABASE}"
Expand All @@ -308,10 +321,7 @@ _mysql_passfile() {
# the client command will use process substitution to create a file on the fly
# ie: --defaults-extra-file=<( _mysql_passfile )
if [ '--dont-use-mysql-root-password' != "$1" ] && [ -n "$MARIADB_ROOT_PASSWORD" ]; then
cat <<-EOF
[client]
password="${MARIADB_ROOT_PASSWORD}"
EOF
echo -e "[client]\npassword=$(printf \"%q\" "${MARIADB_ROOT_PASSWORD}")\n"
fi
}

Expand Down
26 changes: 18 additions & 8 deletions 10.4/docker-entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -258,24 +258,31 @@ docker_setup_db() {
fi
# Sets root password and creates root users for non-localhost hosts
local rootCreate=
# SQL escape the root password, \ followed by '
local rootPasswordEscaped=${MARIADB_ROOT_PASSWORD//\\/\\\\}
rootPasswordEscaped=${rootPasswordEscaped//\'/\\\'}

# default root to listen for connections from anywhere
if [ -n "$MARIADB_ROOT_HOST" ] && [ "$MARIADB_ROOT_HOST" != 'localhost' ]; then
# no, we don't care if read finds a terminating character in this heredoc
# https://unix.stackexchange.com/questions/265149/why-is-set-o-errexit-breaking-this-read-heredoc-expression/265151#265151
read -r -d '' rootCreate <<-EOSQL || true
CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY '${MARIADB_ROOT_PASSWORD}' ;
CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY '${rootPasswordEscaped}' ;
GRANT ALL ON *.* TO 'root'@'${MARIADB_ROOT_HOST}' WITH GRANT OPTION ;
EOSQL
fi

# tell docker_process_sql to not use MARIADB_ROOT_PASSWORD since it is just now being set
docker_process_sql --dont-use-mysql-root-password --database=mysql <<-EOSQL
# we need the SQL_MODE NO_BACKSLASH_ESCAPES mode to be clear for the password to be set.
# --binary-mode to save us from the semi-mad users who put nulls into passwords
docker_process_sql --dont-use-mysql-root-password --database=mysql --binary-mode <<-EOSQL
-- What's done in this file shouldn't be replicated
-- or products like mysql-fabric won't work
SET @@SESSION.SQL_LOG_BIN=0;
SET @@SESSION.SQL_MODE=REPLACE(@@SESSION.SQL_MODE, 'NO_BACKSLASH_ESCAPES', '');
DELETE FROM mysql.user WHERE user NOT IN ('mysql.sys', 'mariadb.sys', 'mysqlxsys', 'root') OR host NOT IN ('localhost') ;
SET PASSWORD FOR 'root'@'localhost'=PASSWORD('${MARIADB_ROOT_PASSWORD}') ;
SET PASSWORD FOR 'root'@'localhost'=PASSWORD('${rootPasswordEscaped}') ;
-- 10.1: https://github.com/MariaDB/server/blob/d925aec1c10cebf6c34825a7de50afe4e630aff4/scripts/mysql_secure_installation.sh#L347-L365
-- 10.5: https://github.com/MariaDB/server/blob/00c3a28820c67c37ebbca72691f4897b57f2eed5/scripts/mysql_secure_installation.sh#L351-L369
DELETE FROM mysql.db WHERE Db='test' OR Db='test\_%' ;
Expand All @@ -294,7 +301,13 @@ docker_setup_db() {

if [ -n "$MARIADB_USER" ] && [ -n "$MARIADB_PASSWORD" ]; then
mysql_note "Creating user ${MARIADB_USER}"
docker_process_sql --database=mysql <<<"CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY '$MARIADB_PASSWORD' ;"
# SQL escape the user password, \ followed by '
local userPasswordEscaped=${MARIADB_PASSWORD//\\/\\\\}
userPasswordEscaped=${userPasswordEscaped//\'/\\\'}
docker_process_sql --database=mysql --binary-mode <<-EOSQL_USER
SET @@SESSION.SQL_MODE=REPLACE(@@SESSION.SQL_MODE, 'NO_BACKSLASH_ESCAPES', '');
CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY '$userPasswordEscaped';
EOSQL_USER

if [ -n "$MARIADB_DATABASE" ]; then
mysql_note "Giving user ${MARIADB_USER} access to schema ${MARIADB_DATABASE}"
Expand All @@ -308,10 +321,7 @@ _mysql_passfile() {
# the client command will use process substitution to create a file on the fly
# ie: --defaults-extra-file=<( _mysql_passfile )
if [ '--dont-use-mysql-root-password' != "$1" ] && [ -n "$MARIADB_ROOT_PASSWORD" ]; then
cat <<-EOF
[client]
password="${MARIADB_ROOT_PASSWORD}"
EOF
echo -e "[client]\npassword=$(printf \"%q\" "${MARIADB_ROOT_PASSWORD}")\n"
fi
}

Expand Down
26 changes: 18 additions & 8 deletions 10.5/docker-entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -258,24 +258,31 @@ docker_setup_db() {
fi
# Sets root password and creates root users for non-localhost hosts
local rootCreate=
# SQL escape the root password, \ followed by '
local rootPasswordEscaped=${MARIADB_ROOT_PASSWORD//\\/\\\\}
rootPasswordEscaped=${rootPasswordEscaped//\'/\\\'}

# default root to listen for connections from anywhere
if [ -n "$MARIADB_ROOT_HOST" ] && [ "$MARIADB_ROOT_HOST" != 'localhost' ]; then
# no, we don't care if read finds a terminating character in this heredoc
# https://unix.stackexchange.com/questions/265149/why-is-set-o-errexit-breaking-this-read-heredoc-expression/265151#265151
read -r -d '' rootCreate <<-EOSQL || true
CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY '${MARIADB_ROOT_PASSWORD}' ;
CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY '${rootPasswordEscaped}' ;
GRANT ALL ON *.* TO 'root'@'${MARIADB_ROOT_HOST}' WITH GRANT OPTION ;
EOSQL
fi

# tell docker_process_sql to not use MARIADB_ROOT_PASSWORD since it is just now being set
docker_process_sql --dont-use-mysql-root-password --database=mysql <<-EOSQL
# we need the SQL_MODE NO_BACKSLASH_ESCAPES mode to be clear for the password to be set.
# --binary-mode to save us from the semi-mad users who put nulls into passwords
docker_process_sql --dont-use-mysql-root-password --database=mysql --binary-mode <<-EOSQL
-- What's done in this file shouldn't be replicated
-- or products like mysql-fabric won't work
SET @@SESSION.SQL_LOG_BIN=0;
SET @@SESSION.SQL_MODE=REPLACE(@@SESSION.SQL_MODE, 'NO_BACKSLASH_ESCAPES', '');
DELETE FROM mysql.user WHERE user NOT IN ('mysql.sys', 'mariadb.sys', 'mysqlxsys', 'root') OR host NOT IN ('localhost') ;
SET PASSWORD FOR 'root'@'localhost'=PASSWORD('${MARIADB_ROOT_PASSWORD}') ;
SET PASSWORD FOR 'root'@'localhost'=PASSWORD('${rootPasswordEscaped}') ;
-- 10.1: https://github.com/MariaDB/server/blob/d925aec1c10cebf6c34825a7de50afe4e630aff4/scripts/mysql_secure_installation.sh#L347-L365
-- 10.5: https://github.com/MariaDB/server/blob/00c3a28820c67c37ebbca72691f4897b57f2eed5/scripts/mysql_secure_installation.sh#L351-L369
DELETE FROM mysql.db WHERE Db='test' OR Db='test\_%' ;
Expand All @@ -294,7 +301,13 @@ docker_setup_db() {

if [ -n "$MARIADB_USER" ] && [ -n "$MARIADB_PASSWORD" ]; then
mysql_note "Creating user ${MARIADB_USER}"
docker_process_sql --database=mysql <<<"CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY '$MARIADB_PASSWORD' ;"
# SQL escape the user password, \ followed by '
local userPasswordEscaped=${MARIADB_PASSWORD//\\/\\\\}
userPasswordEscaped=${userPasswordEscaped//\'/\\\'}
docker_process_sql --database=mysql --binary-mode <<-EOSQL_USER
SET @@SESSION.SQL_MODE=REPLACE(@@SESSION.SQL_MODE, 'NO_BACKSLASH_ESCAPES', '');
CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY '$userPasswordEscaped';
EOSQL_USER

if [ -n "$MARIADB_DATABASE" ]; then
mysql_note "Giving user ${MARIADB_USER} access to schema ${MARIADB_DATABASE}"
Expand All @@ -308,10 +321,7 @@ _mysql_passfile() {
# the client command will use process substitution to create a file on the fly
# ie: --defaults-extra-file=<( _mysql_passfile )
if [ '--dont-use-mysql-root-password' != "$1" ] && [ -n "$MARIADB_ROOT_PASSWORD" ]; then
cat <<-EOF
[client]
password="${MARIADB_ROOT_PASSWORD}"
EOF
echo -e "[client]\npassword=$(printf \"%q\" "${MARIADB_ROOT_PASSWORD}")\n"
fi
}

Expand Down
26 changes: 18 additions & 8 deletions docker-entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -258,24 +258,31 @@ docker_setup_db() {
fi
# Sets root password and creates root users for non-localhost hosts
local rootCreate=
# SQL escape the root password, \ followed by '
local rootPasswordEscaped=${MARIADB_ROOT_PASSWORD//\\/\\\\}
rootPasswordEscaped=${rootPasswordEscaped//\'/\\\'}

# default root to listen for connections from anywhere
if [ -n "$MARIADB_ROOT_HOST" ] && [ "$MARIADB_ROOT_HOST" != 'localhost' ]; then
# no, we don't care if read finds a terminating character in this heredoc
# https://unix.stackexchange.com/questions/265149/why-is-set-o-errexit-breaking-this-read-heredoc-expression/265151#265151
read -r -d '' rootCreate <<-EOSQL || true
CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY '${MARIADB_ROOT_PASSWORD}' ;
CREATE USER 'root'@'${MARIADB_ROOT_HOST}' IDENTIFIED BY '${rootPasswordEscaped}' ;
GRANT ALL ON *.* TO 'root'@'${MARIADB_ROOT_HOST}' WITH GRANT OPTION ;
EOSQL
fi

# tell docker_process_sql to not use MARIADB_ROOT_PASSWORD since it is just now being set
docker_process_sql --dont-use-mysql-root-password --database=mysql <<-EOSQL
# we need the SQL_MODE NO_BACKSLASH_ESCAPES mode to be clear for the password to be set.
# --binary-mode to save us from the semi-mad users who put nulls into passwords
docker_process_sql --dont-use-mysql-root-password --database=mysql --binary-mode <<-EOSQL
-- What's done in this file shouldn't be replicated
-- or products like mysql-fabric won't work
SET @@SESSION.SQL_LOG_BIN=0;
SET @@SESSION.SQL_MODE=REPLACE(@@SESSION.SQL_MODE, 'NO_BACKSLASH_ESCAPES', '');
DELETE FROM mysql.user WHERE user NOT IN ('mysql.sys', 'mariadb.sys', 'mysqlxsys', 'root') OR host NOT IN ('localhost') ;
SET PASSWORD FOR 'root'@'localhost'=PASSWORD('${MARIADB_ROOT_PASSWORD}') ;
SET PASSWORD FOR 'root'@'localhost'=PASSWORD('${rootPasswordEscaped}') ;
-- 10.1: https://github.com/MariaDB/server/blob/d925aec1c10cebf6c34825a7de50afe4e630aff4/scripts/mysql_secure_installation.sh#L347-L365
-- 10.5: https://github.com/MariaDB/server/blob/00c3a28820c67c37ebbca72691f4897b57f2eed5/scripts/mysql_secure_installation.sh#L351-L369
DELETE FROM mysql.db WHERE Db='test' OR Db='test\_%' ;
Expand All @@ -294,7 +301,13 @@ docker_setup_db() {

if [ -n "$MARIADB_USER" ] && [ -n "$MARIADB_PASSWORD" ]; then
mysql_note "Creating user ${MARIADB_USER}"
docker_process_sql --database=mysql <<<"CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY '$MARIADB_PASSWORD' ;"
# SQL escape the user password, \ followed by '
local userPasswordEscaped=${MARIADB_PASSWORD//\\/\\\\}
userPasswordEscaped=${userPasswordEscaped//\'/\\\'}
docker_process_sql --database=mysql --binary-mode <<-EOSQL_USER
SET @@SESSION.SQL_MODE=REPLACE(@@SESSION.SQL_MODE, 'NO_BACKSLASH_ESCAPES', '');
CREATE USER '$MARIADB_USER'@'%' IDENTIFIED BY '$userPasswordEscaped';
EOSQL_USER

if [ -n "$MARIADB_DATABASE" ]; then
mysql_note "Giving user ${MARIADB_USER} access to schema ${MARIADB_DATABASE}"
Expand All @@ -308,10 +321,7 @@ _mysql_passfile() {
# the client command will use process substitution to create a file on the fly
# ie: --defaults-extra-file=<( _mysql_passfile )
if [ '--dont-use-mysql-root-password' != "$1" ] && [ -n "$MARIADB_ROOT_PASSWORD" ]; then
cat <<-EOF
[client]
password="${MARIADB_ROOT_PASSWORD}"
EOF
echo -e "[client]\npassword=$(printf \"%q\" "${MARIADB_ROOT_PASSWORD}")\n"
fi
}

Expand Down

0 comments on commit 7c0795b

Please sign in to comment.