neutralizing sql injection in postgresql

Post on 14-Apr-2017

911 Views

Category:

Software

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Juliano Atanazio

Neutralizando SQL Injection no PostgreSQLNeutralizando SQL Injection no PostgreSQL

Neutralizing SQL Injection in PostgreSQLNeutralizing SQL Injection in PostgreSQL

2/59

About me

Juliano Atanazio

● Graduated in Computer Science for Business Management (Informática para Gestão de Negócios), FATEC Zona Sul, São Paulo – SP;

● PostgreSQL DBA;

● Linux admin;

● Instructor (PostgreSQL);

● LPIC-1, LPIC-2 Certified;

● Linux user since 2000;

● Free Software enthusiast;

● Favorite technologies: PostgreSQL, Linux, Python, Shell Script, FreeBSD, etc...;

● Headbanger :) \m/

3/59

SQL Injection

Definition

SQL Injection is a method to introducing malicious SQL code to get unauthorized access or even damage a system.

Definição

SQL Injection é um método para introduzir código SQL maligno para obter acesso indevido ou mesmo danificar um sistema.

4/59

SQL Injection: Practice

$DBHOST enviroment variable to database server address:

Variável de ambiente $DBHOST para o endereço do servidor de banco de dados:

$ read -p 'Type the database host address: ' DBHOST

Type the database host address:

Type the server address.

Digite o endereço do servidor.

5/59

SQL Injection: Practice

Database user with encrypted stored password, login permission, no superuser:

Usuário de banco de dados com senha armazenada criptografada, permissão de login, não superuser:

$ psql -U postgres -h ${DBHOST} -c \"CREATE ROLE u_sql_injection \ENCRYPTED PASSWORD 'secret' LOGIN NOSUPERUSER;"

6/59

SQL Injection: Practice

Database creation "db_sql_injection" with user "u_sql_injection" as owner:

Criação de banco de dados "db_sql_injection" com o usuário "u_sql_injection" como proprietário:

$ psql -U postgres -h ${DBHOST} -c \"CREATE DATABASE db_sql_injection OWNER u_sql_injection;"

7/59

SQL Injection: Practice

Accessing the database via psql:

Acessando a base de dados via psql:

$ psql -U u_sql_injection db_sql_injection -h ${DBHOST}

8/59

SQL Injection: Practice

User table creation for the application (without hashing):

Criação de tabela de usuários para a aplicação (sem hashing):

> CREATE TABLE tb_user( username varchar(50) PRIMARY KEY, -- natural primary key password VARCHAR(72) NOT NULL);

Inserting a application user in the table:

Inserindo um usuário do aplicativo na tabela:

> INSERT INTO tb_user (username, password) VALUES ('foo', 'mypassword');

9/59

SQL Injection: Practice

Script (1):__________ sql_injection_1.py ___________________________

#_*_ encoding: utf-8 _*_

import getpass

user = input('User: ')password = getpass.getpass('Password: ')

sql = """SELECT TRUE FROM tb_userWHERE username = '{}'AND password = '{}';""".format(user, password)

print('\n{}'.format(sql))

____________________________________________________

10/59

SQL Injection: Practice

A simple test:

Um teste simples:

$ python3 sql_injection_1.py

User: fooPassword:

SELECT TRUE FROM tb_userWHERE username = 'foo'AND password = 'mypassword';

11/59

SQL Injection: About the Script

The script is pretty simple, does not yet have any interaction with the database, but it serves to illustrate.

O script é bem simples, ainda não possui qualquer interação com o banco de dados, mas serve para ilustrar.

12/59

SQL Injection: Practice

Script (2):__________ sql_injection_2.py ___________________________

# _*_ encoding: utf-8 _*_

import getpassimport psycopg2import sys

# DB server as first argumentdbhost = sys.argv[1]

# Connection stringconn_string = """ host='{}' dbname='db_sql_injection' user='u_sql_injection' password='secret' port='5432' """.format(dbhost)→

13/59

SQL Injection: Practice

Script (2):__________ sql_injection_2.py ___________________________

try: # Connection conn = psycopg2.connect(conn_string)

# Cursor creation to execute SQL commands cursor = conn.cursor()

# User input user = input('User: ')

# Password input password = getpass.getpass('Password: ')

14/59

SQL Injection: Practice

Script (2):__________ sql_injection_2.py ___________________________

# SQL string sql = """ SELECT TRUE FROM tb_user \ WHERE username = '{}' \ AND password = '{}'; """.format(user, password)

# Print the sql string after user and password input print('{}\n'.format(sql))

# Execute the SQL string in database cursor.execute(sql)

# The result of the string SQL execution res = cursor.fetchone()

15/59

SQL Injection: Practice

Script (2):__________ sql_injection_2.py ___________________________

# User login validation if res: print('\nAcessed!') else: print('\nError: Invalid user and password combination!') sys.exit(1)

except psycopg2.Error as e: print('\nAn error has occurred!\n{}'.format(e))

# Close the database connectionconn.close()

____________________________________________________

16/59

SQL Injection: Practice

A simple test access with correct password:

Um teste simples de acesso com senha correta:

$ python3 sql_injection_2.py ${DBHOST}

User: fooPassword:

SELECT TRUE FROM tb_userWHERE username = 'foo' AND password = 'mypassword';

Acessed!

17/59

SQL Injection: Practice

A simple test access with wrong password:

Um teste simples de acesso com senha errada:

$ python3 sql_injection_2.py ${DBHOST}

User: fooPassword:

SELECT TRUE FROM tb_userWHERE username = 'foo'AND password = '123';

Error: Invalid user and password combination!

18/59

SQL Injection: Practice

Malicious code at user login input:

Código malicioso na entrada de login de usuário:

$ python3 sql_injection_2.py ${DBHOST}

User: ' OR 1 = 1; DROP TABLE tb_user; --Password:

SELECT TRUE FROM tb_userWHERE username = '' OR 1 = 1; DROP TABLE tb_user; –-'AND password = '';

An error has occurred!no results to fetch

Does the table has been deleted?

Será que a tabela foi apagada?

19/59

SQL Injection: Practice

Checking the table in the database:

Verificando a tabela na base de dados:

> SELECT TRUE FROM tb_user;

bool ------ t

Everithing is OK... for a while...No commit...

Está tudo OK... por enquanto...Sem efetivação...

20/59

SQL Injection: Practice

Malicious code at user login input (with COMMIT):

Código malicioso na entrada de login de usuário (com COMMIT):

$ python3 sql_injection.py

User: ' OR 1 = 1; DROP TABLE tb_user; COMMIT; --Password:

SELECT TRUE FROM tb_user WHERE username = '' OR 1 = 1; DROP TABLE tb_user; COMMIT; –-'AND password = '';

An error has occurred!no results to fetch

21/59

SQL Injection: Practice

Checking the table in the database:

Verificando a tabela na base de dados:

> SELECT TRUE FROM tb_user;

ERROR: relation "tb_user" does not existLINE 1: SELECT id FROM tb_user; ^

The table was dropped and must be created with the data again.

A tabela foi apagada e terá que ser criada com os dados novamente.

:(

22/59

Dollar Quoting

It consists of a dollar sign ($), an optional “tag” of zero or more characters, another dollar sign, an arbitrary sequence of characters that makes up the string content, a dollar sign, the same tag that began this dollar quote, and a dollar sign. For example, here are two different ways to specify the string “Dianne's horse” using dollar quoting:

Consiste de um caractere de dólar, uma “tag” opcional de zero ou mais caracteres, outro caractere de dólar, uma sequência arbitrária de caracteres que é o conteúdo da string, um caractere de dólar, a mesma tag que começou o dollar quoting e um caractere de dólar. Por exemplo, há duas maneiras diferentes de especificar a string “Dianne's horse” usando dollar quoting:

$$Dianne's horse$$$SomeTag$Dianne's horse$SomeTag$

23/59

Dollar Quoting

Dollar quoting is also a very nice feature to avoid SQL injection, particularly when the application generates a random tag.This tag must start with either a letter or with an underscore, the rest can have underscore, letters or numbers.

Dollar quoting também é um recurso muito interessante para se evitar SQL injection, principalmente quando a aplicação gera uma tag aleatória.Essa tag deve começar ou com uma letra ou com underscore, o resto pode ter underscore, letras ou números.

24/59

Dollar Quoting: Practice

Script (3):__________ sql_injection_3.py ___________________________

# _*_ encoding: utf-8 _*_

import getpassimport psycopg2import sys

# DB server as first argumentdbhost = sys.argv[1]

# Connection stringconn_string = """ host='{}' dbname='db_sql_injection' user='u_sql_injection' password='secret' port='5432' """.format(dbhost)→

25/59

Dollar Quoting: Practice

Script (3):__________ sql_injection_3.py ___________________________

try: # Connection conn = psycopg2.connect(conn_string)

# Cursor creation to execute SQL commands cursor = conn.cursor()

# User input user = input('User: ')

# Password input password = getpass.getpass('Password: ')

26/59

Dollar Quoting: Practice

Script (3):__________ sql_injection_3.py ___________________________

# SQL string sql = """ SELECT TRUE FROM tb_user WHERE username = $${}$$ AND password = $${}$$; """.format(user, password)

# Print the sql string after user and password input print('{}\n'.format(sql))

# Execute the SQL string in database cursor.execute(sql)

# The result of the string SQL execution res = cursor.fetchone()

27/59

Dollar Quoting: Practice

Script (3):__________ sql_injection_3.py ___________________________

# User login validation if res: print('\nAcessed!\n') else: print('\nError: Invalid user and password combination!\n') sys.exit(1)except psycopg2.Error as e: print('\nAn error has occurred!\n{}'.format(e))

# Close the database connectionconn.close()

____________________________________________________

28/59

Dollar Quoting: Practice

Normal access:

Acesso normal:

$ python3 sql_injection_3.py ${DBHOST}

User: fooPassword: SELECT TRUE FROM tb_userWHERE username = $$foo$$AND password = $$mypassword$$;

Acessed!

29/59

Dollar Quoting: Practice

Attempted malicious code (with apostrophe):

Tentativa de código malicioso (com apóstrofo):

$ python3 sql_injection_3.py ${DBHOST}

User: ' OR 1 = 1; DROP TABLE tb_user; COMMIT; --Password: SELECT TRUE FROM tb_userWHERE username = $$' OR 1 = 1; DROP TABLE tb_user; COMMIT; --$$AND password = $$$$;

Error: Invalid user and password combination!

Neutralized malicious code.

Código malicioso neutralizado.

30/59

Dollar Quoting: Practice

Attempted malicious code (with double dollar sign):

Tentativa de código malicioso (com dólar duplo):

$ python3 sql_injection_3.py ${DBHOST} User: $$ OR 1 = 1; DROP TABLE tb_user; COMMIT; --Password:

SELECT TRUE FROM tb_userWHERE username = $$$$ OR 1 = 1; DROP TABLE tb_user; COMMIT; --$$AND password = $$$$;

An error has occurred!no results to fetch

31/59

Dollar Quoting: Practice

Checking the table in the database:

Verificando a tabela na base de dados:

> SELECT TRUE FROM tb_user;

ERROR: relation "tb_user" does not existLINE 1: SELECT id FROM tb_user; ^

The table was dropped and must be created with the data again.

A tabela foi apagada e terá que ser criada com os dados novamente.

:(

32/59

Dollar Quoting: Practice

Script (4):__________ sql_injection_4.py ___________________________

# _*_ encoding: utf-8 _*_

import getpassimport psycopg2import sysimport stringimport random

# DB server as first argumentdbhost = sys.argv[1]

33/59

Dollar Quoting: Practice

Script (4):__________ sql_injection_4.py ___________________________

# Connection stringconn_string = """ host='{}' dbname='db_sql_injection' user='u_sql_injection' password='secret' port='5432' """.format(dbhost)

34/59

Dollar Quoting: Practice

Script (4):__________ sql_injection_4.py ___________________________

# Function: tag generatordef tag_gen(size): first_char = '{}_'.format(string.ascii_letters) last_chars = '{}{}'.format(string.digits, first_char) tag = random.choice(first_char)

for i in range(size - 1): tag = '{}{}'.format(tag, random.choice(last_chars))

return tag

# Tag for dollar quotingtag = tag_gen(7)

35/59

Dollar Quoting: Practice

Script (4):__________ sql_injection_4.py ___________________________

try: # Connection conn = psycopg2.connect(conn_string)

# Cursor creation to execute SQL commands cursor = conn.cursor()

# User input user = input('User: ')

# Password input password = getpass.getpass('Password: ')

36/59

Dollar Quoting: Practice

Script (4):__________ sql_injection_4.py ___________________________

# SQL string sql = """ SELECT TRUE FROM tb_user WHERE username = ${}${}${}$ AND password = ${}${}${}$; """.format(tag, user, tag, tag, password, tag)

# Print the sql string after user and password input print('{}\n'.format(sql))

# Execute the SQL string in database cursor.execute(sql)

# The result of the string SQL execution res = cursor.fetchone()

37/59

Dollar Quoting: Practice

Script (4):__________ sql_injection_4.py ___________________________

# User login validation if res: print('\nAcessed!\n') else: print('\nError: Invalid user and password combination!\n') sys.exit(1)except psycopg2.Error as e: print('\nAn error has occurred!\n{}'.format(e))

# Close the database connectionconn.close()

____________________________________________________

38/59

Dollar Quoting: Practice

A simple test access with correct password:

Um teste simples de acesso com senha correta:

$ python3 sql_injection_4.py ${DBHOST}

User: fooPassword: SELECT TRUE FROM tb_userWHERE username = $PJPWqvS$foo$PJPWqvS$AND password = $PJPWqvS$mypassword$PJPWqvS$;

Acessed!

39/59

Dollar Quoting: Practice

Attempted malicious code (with apostrophe):

Tentativa de código malicioso (com apóstrofo):

$ python3 sql_injection_4.py ${DBHOST}

User: ' OR 1 = 1; DROP TABLE tb_user; COMMIT; --Password: SELECT TRUE FROM tb_userWHERE username = $EbVRSoG$' OR 1 = 1; DROP TABLE tb_user; COMMIT; --$EbVRSoG$AND password = $EbVRSoG$$EbVRSoG$;

Error: Invalid user and password combination!

Neutralized malicious code.

Código malicioso neutralizado.

40/59

Dollar Quoting: Practice

Attempted malicious code (with double dollar sign):

Tentativa de código malicioso (com dólar duplo):

$ python3 sql_injection_4.py ${DBHOST}

User: $$ OR 1 = 1; DROP TABLE tb_user; COMMIT; --Password:

SELECT TRUE FROM tb_userWHERE username = $Re7Gqwb$$$ OR 1 = 1; DROP TABLE tb_user; COMMIT; --$Re7Gqwb$AND password = $Re7Gqwb$$Re7Gqwb$;

Error: Invalid user and password combination!

Neutralized malicious code.

Código malicioso neutralizado.

41/59

Prepared Statement

A prepared statement is a server-side object that can be used to optimize performance.

Um prepared statement (comando preparado) é um objeto do lado do servidor que pode ser usado para otimizar performance.

When the PREPARE statement is executed, the statement is analyzed, statistics collections are made (ANALYZE) and rewritten.

Quando PREPARE statement é executado, o comando (statement) é analisado, são feitas coletas de estatísticas (ANALYZE) e reescrito.

42/59

Prepared Statement

When given an EXECUTE statement, the statement is planned and prepared executed.

Quando é dado um comando EXECUTE, o prepared statement é planejado e executado.

This division of labor prevents repetitive tasks of collecting statistics, while allowing the execution plan depend on specific parameters that can be provided.

Essa divisão de trabalho evita repetitivos trabalhos de coleta de estatística, enquanto permite ao plano de execução de depender de parâmetros específicos que podem ser fornecidos.

43/59

Prepared Statement

Steps / Etapas

Normal query:

Consulta normal:

1) Parser → 2) Rewrite System → 3) Planner / Optimizer → 4) Executor

Prepared Statement:

1) Planner / Optimizer → 2) Executor

44/59

Prepared Statement: Practice

Create a prepared statement:

Criar um prepared statement:

> PREPARE q_user(text, text) AS SELECT TRUE FROM tb_user WHERE username = $1 AND password = $2;

45/59

Prepared Statement: Practice

Execute a prepared statement:

Executar um prepared statement:

> EXECUTE q_user('foo', 'mypassword');

bool ------ t

46/59

Prepared Statement: Practice

Script (5):__________ sql_injection_5.py ___________________________

# _*_ encoding: utf-8 _*_

import getpassimport psycopg2import sys

# DB server as first argumentdbhost = sys.argv[1]

# Connection stringconn_string = """ host='{}' dbname='db_sql_injection' user='u_sql_injection' password='secret' port='5432' """.format(dbhost)→

47/59

Prepared Statement: Practice

Script (5):__________ sql_injection_5.py ___________________________

try: # Connection conn = psycopg2.connect(conn_string)

# Cursor creation to execute SQL commands cursor = conn.cursor()

# User input user = input('User: ')

# Password input password = getpass.getpass('Password: ')

48/59

Prepared Statement: Practice

Script (5):__________ sql_injection_5.py ___________________________

# SQL string sql = """ PREPARE q_user (text, text) AS SELECT TRUE FROM tb_user WHERE username = $1 AND password = $2; """

# Print the sql string after user and password input print('{}\n'.format(sql))

# Execute the SQL string in database cursor.execute(sql)

49/59

Prepared Statement: Practice

Script (5):__________ sql_injection_5.py ___________________________ # SQL string with EXECUTE sql = "EXECUTE q_user('{}', '{}');".format(user, password)

# Print the SQL string print('{}\n'.format(sql)) # Execute the SQL string in database cursor.execute(sql)

# The result of the string SQL execution res = cursor.fetchone()

50/59

Prepared Statement: Practice

Script (5):__________ sql_injection_5.py ___________________________ # User login validation if res: print('\nAcessed!') else: print('\nError: Invalid user and password combination!') sys.exit(1)except psycopg2.Error as e: print('\nAn error has occurred!\n{}'.format(e))

# Close the database connectionconn.close()

____________________________________________________

51/59

Prepared Statement: Practice

A simple test access with correct password:

Um teste simples de acesso com senha correta:

$ python3 sql_injection_5.py ${DBHOST}

User: fooPassword:

PREPARE q_user (text, text) ASSELECT TRUE FROM tb_userWHERE username = $1 AND password = $2;

EXECUTE q_user('foo', 'mypassword');

Acessed!

52/59

Prepared Statement: Practice

A simple test access with wrong password:

Um teste simples de acesso com senha errada:

$ python3 sql_injection_5.py ${DBHOST} User: fooPassword:

PREPARE q_user (text, text) ASSELECT TRUE FROM tb_user WHERE username = $1 AND password = $2;

EXECUTE q_user('foo', '123');

Error: Invalid user and password combination!

53/59

Prepared Statement: Practice

Attempted malicious code (with apostrophe):

Tentativa de código malicioso (com apóstrofo):

$ python3 sql_injection_5.py ${DBHOST}

User: ' OR 1 = 1; DROP TABLE tb_user; COMMIT; --Password:

PREPARE q_user (text, text) AS SELECT TRUE FROM tb_userWHERE username = $1 AND password = $2;

EXECUTE q_user('' OR 1 = 1; DROP TABLE tb_user; COMMIT; --', '');

An error has occurred!syntax error at or near ";"LINE 1: EXECUTE q_user('' OR 1 = 1; DROP TABLE tb_user; COMMIT; --',... ^

Neutralized malicious code. / Código malicioso neutralizado

54/59

Prepared Statement: Practice

Attempted malicious code (with double dollar sign):

Tentativa de código malicioso (com dólar duplo):

$ python3 sql_injection_5.py ${DBHOST} User: $$ OR 1 = 1; DROP TABLE tb_user; COMMIT; --Password: PREPARE q_user (text, text) AS SELECT TRUE FROM tb_user WHERE username = $1 AND password = $2;

EXECUTE q_user('$$ OR 1 = 1; DROP TABLE tb_user; COMMIT; --', '');

Error: Invalid user and password combination!

Neutralized malicious code. / Código malicioso neutralizado.

55/59

Conclusion / Conclusão

PostgreSQL has its own mechanisms against SQL injection which makes it very independent of the application.

O PostgreSQL possui mecanismos próprios contra SQL injection que o torna muito independente da aplicação.

56/59

Conclusion / Conclusão

This makes it easier for the application developer, may delegate such tasks to the database, avoiding technical adjustments in the application and finally provide a robust solution independent of language. Isso facilita para o desenvolvedor da aplicação, podendo confiar tais tarefas ao banco de dados, evitando adaptações técnicas na aplicação e por fim prover uma solução robusta independente da linguagem.

57/59

Donate!

The elephant needs you!O Elefante precisa de você!

Contribute! Contribua!

:)

http://www.postgresql.org/about/donate/

58/59

Save our planet!Save our planet!

59/59

See you soon!!! Até a próxima!!!

Juliano Atanazio

juliano777@gmail.com

http://slideshare.net/spjuliano

https://speakerdeck.com/julianometalsp

https://juliano777.wordpress.com

:)

top related