PostgreSQL

Všechny procedurální jazyky PostgreSQL, které umožňují psát funkce a procedury uvnitř databáze, umožňují provádění libovolných SQL příkazů.

PL/pgSQL

Nejbezpečnějším způsobem, jak provádět SQL uvnitř příkazu PL/pgSQL, je jednoduše takhle:

CREATE OR REPLACE FUNCTION user_access (p_uname TEXT)
  RETURNS timestamp LANGUAGE plpgsql AS
$func$
BEGIN
    RETURN accessed_at FROM users WHERE username = p_uname;
END
$func$;

V takovém jednoduchém příkladu na tom vlastně budete líp s čistě SQL funkcí:

CREATE OR REPLACE FUNCTION user_access (p_uname TEXT)
  RETURNS timestamp LANGUAGE sql AS
$func$
    SELECT accessed_at FROM users WHERE username = $1
$func$;

Někdy ale potřebujete dělat složitější věci. Třeba dynamicky přidáváte WHERE klauzule podle vstupu. V takovém případě skončíte u PL/pgSQL příkazu EXECUTE. Tady je příklad, který je zranitelný SQL injection:

CREATE OR REPLACE FUNCTION get_users(p_column TEXT, p_value TEXT)
  RETURNS SETOF users LANGUAGE plpgsql AS
$func$
DECLARE
    query TEXT := 'SELECT * FROM users';
BEGIN
    IF p_column IS NOT NULL THEN
        query := query || ' WHERE ' || p_column
              || $_$ = '$_$ || p_value || $_$'$_$;
    END IF;
    RETURN QUERY EXECUTE query;
END
$func$;

Oba parametry p_column i p_value jsou zranitelné. Jedním způsobem, jak se tomuto problému vyhnout, je použít funkci quote_ident() k uvození SQL identifikátorů (v tomto případě p_column) a quote_literal() k uvození literálů:

CREATE OR REPLACE FUNCTION get_users(p_column TEXT, p_value TEXT)
  RETURNS SETOF users LANGUAGE plpgsql AS
$func$
DECLARE
    query TEXT := 'SELECT * FROM users';
BEGIN
    IF p_column IS NOT NULL THEN
        query := query || ' WHERE ' || quote_ident(p_column)
              || ' = ' || quote_literal(p_value);
    END IF;
    RETURN QUERY EXECUTE query;
END;
$func$;

Také se to o něco snadněji čte! Ještě lepší je u hodnot využívat klauzuli USING v příkazu EXECUTE (dostupná od v8.4):

CREATE OR REPLACE FUNCTION get_users(p_column TEXT, p_value TEXT)
  RETURNS SETOF users LANGUAGE plpgsql AS
$func$
DECLARE
    query TEXT := 'SELECT * FROM users';
BEGIN
    IF p_column IS NOT NULL THEN
        query := query || ' WHERE ' || quote_ident(p_column) || ' = $1';
    END IF;
    RETURN QUERY EXECUTE query
    USING p_value;
END;
$func$;

V této podobě kromě ochrany proti SQLi získáte i lepší výkon bez režie převádění hodnot na text a zpět.

PL/Perl

Chybí

PL/Python

Chybí

PL/Tcl

Chybí

Forkuj mě na GitHubu