Procedury składowane w PostgreSQL/Bezpieczeństwo

Przy tworzeniu procedury można podać dwie opcje związane z uprawnieniami:

SECURITY INVOKER [domyślnie]
procedura wywoływana jest z takimi uprawnieniami, jak aktualnie zalogowany użytkownik;
SECURITY DEFINER
procedura wywoływana jest z takimi uprawnieniami, jak użytkownik, który ją zdefiniował.

Dzięki drugiej opcji można częściowo lub całkowicie odizolować zwykłych użytkowników od tabel, i umożliwić modyfikację lub dostęp wyłącznie przez dedykowane funkcje. W szczególności mogą to być np. jakieś krytyczne ustawienia aplikacji, dane finansowe itp.

Ponadto można w chwili tworzenia funkcji określić ścieżkę wyszukiwania (search_path), ograniczając tym samym dostęp do innych schematów.

Funkcje CURRENT_USER i SESSION_USER

edytuj

Czasem zachodzi konieczność np. logowania, kto używał procedury. Do tego celu należy używać wyłącznie funkcji SESSION_USER, która zawsze zwraca nazwę użytkownika.

Funkcja CURRENT_USER, trochę wbrew nazwie, wywołana w funkcji zdefiniowanej jako SECURITY DEFINER zwróci użytkownika, który jest właścicielem funkcji.

Przykład

edytuj

W tym prostym przykładzie zostanie założona tabela pracowników, której tylko wybrane wiersze będą pokazywane użytkownikowi za pośrednictwem procedury.

CREATE TABLE pracownicy (
    imie        text,
    nazwisko    text,
    tajny       boolean
);

INSERT INTO pracownicy (imie, nazwisko, tajny) VALUES
    ('Jan',    'Kowalski', false),
    ('Tomasz', 'Nowak',    false),
    ('Anna',   'Kot',      false),
    ('James',  'Bond',     true)
;

CREATE FUNCTION lista_pracownikow() RETURNS TABLE(imie text, nazwisko text)
    LANGUAGE SQL
AS $$
    SELECT imie, nazwisko
      FROM pracownicy
     WHERE tajny = false;
$$
    SECURITY DEFINER
;

CREATE ROLE uzytkownik WITH LOGIN UNENCRYPTED PASSWORD '123';

-- użytkownik nie może nawet wykonać instrukcji SELECT
REVOKE ALL PRIVILEGES ON TABLE pracownicy FROM uzytkownik;

I przykładowa sesja

$ SELECT current_user;
 current_user 
--------------
 uzytkownik
(1 row)

$ SELECT * FROM pracownicy;
ERROR:  permission denied for relation pracownicy

$ SELECT * FROM lista_pracownikow();
  imie  | nazwisko 
--------+----------
 Jan    | Kowalski
 Tomasz | Nowak
 Anna   | Kot