Безопасность в PostgreSQL

Стал вопрос о безопасности в PostgreSQL. Поведаю подробнее.

Есть функция,созданная от привилегированного пользователя:

CREATE OR REPLACE FUNCTION counts(text)
  RETURNS integer AS
$BODY$
DECLARE sql varchar;
DECLARE c integer;
BEGIN  
   sql:='select count(id) from table1 where ' || $1 || ' ';
       EXECUTE sql INTO c;
   RETURN c;
END;
$BODY$
  LANGUAGE 'plpgsql' VOLATILE SECURITY DEFINER
  COST 100;
GRANT EXECUTE ON FUNCTION counts(text) TO exec_user;

И есть таблица foo (не важно, что в ней).
Если выполнить запрос от непривилигированного пользователя exec_user таким образом:
SELECT counts('1=1; DROP TABLE foo;SELECT id FROM table1;');

, то таблица foo - дропается :( Это понятно, потому, что SECURITY DEFINER. А права на удаление у привилегированого пользователя - есть.

Вопрос. Как организовать безопасное выполнение функций и только функций на своем сервере?
Я много рылся, но не нашел подробное описание IMMUTABLE/STABLE /VOLATILE
Лишь, в официальном мануале про них написано 2 строки про то, что IMMUTABLE/STABLE выполняются в режиме чтения а VOLATILE в режиме чтения-записи.

и SECURITY DEFINER, есть еще какие-то варианты SECURITY вроде, но опять же, не нашел где про них почитать.

В итоге задача. Сделать так, что бы непривилегированный пользователь мог только выполнять функции, но не мог выполнять обычные запросы select,insert,delete,drop и так далее.

У меня есть единственное решение (наверно в связи с малым опытом) - это сделать еще одного пользователя. дать ему - минимальные права, переназначить OWNER'а всех функций на этого пользователя. И тогда даже при sql-инжекте он, вроде, ничего лишнего не сделает... но все же.. Может есть более грамотное решение?

P.S. Функция, которую я привел, может и не очень грамотная, т.е., не секьюрно передавать целую WHERE в функцию, но, иногда нужно собрать строку в теле функции используя различные входные параметры и sql - инжект, там тоже актуален..

Опции просмотра комментариев

Выберите предпочитаемый вами способ показа комментариев и нажмите "Сохранить настройки" для активации изменений.

так вот же: 34.6.

так вот же: 34.6. Function Volatility Categories

и вот: (CREATE FUNCTION )

[EXTERNAL] SECURITY INVOKER
[EXTERNAL] SECURITY DEFINER

SECURITY INVOKER indicates that the function is to be executed with the privileges of the user that calls it. That is the default. SECURITY DEFINER specifies that the function is to be executed with the privileges of the user that created it.

The key word EXTERNAL is allowed for SQL conformance, but it is optional since, unlike in SQL, this feature applies to all functions not only external ones.

ответ

Можешь привести переделанную мной функцию на секьюрный, на твой взгляд, вариант?
SECURITY INVOKER - ф-я исполняется с правами вызвавшего. Т.е. Что бы в функции организовать апдейт, нужно и на соответствующую relation , которую затрагивает функция, выдать права на апдейт. Если селект, то и селект. Тогда, получается, к черту функции, непривилегированный полтьзователь может в обход функции выполнить тот же апдейт или селект..

SECURITY DEFINER - ok, но тогда, т.к. у привилегированного пользователя есть права на инзерт и прочее, здесь имеет место sql-инжект, т.е. инжект попадает в тело процедуры и там он уже исполняется от привилегированного пользователя...

Volatility - я в запросах, которые не меняют базу(аля SELECT) ставлю IMMUTABLE, и тогда, запрос на дроп делет и прочее, в нем уже не работает, т.к. в non-volatility функциях нельзя делать дроп, апдейт и прочая хрень...
Но, в ней можно выполнять селекты на таблицы, к которым у непривилегированного пользователя нет прав, и, соответственно, дампить данные... И, если функция, делает какой-то апдейт или инзерт, то тут - опять же имеет месть sql-инжект, т.к. функция будет VOLATILITY :(

на постгрес надейся, а сам не плошай :)

лично мне такие варианты приходят на ум:

вариант 1

declare 
  bad_sql BOOLEAN;
Begin
SELECT  position(';' IN sql)>0 INTO bad_sql;
IF bad_sql then raise notice 'Тов. '||user||' хочет получить выговор за шаловливые ручки';end IF;
.....

вариант 2

declare 
  bad_sql BOOLEAN;
Begin
SELECT NOT (SELECT regexp_matches(sql,'drop|update|insert|delete|grant|;')) IS  NULL INTO bad_sql;
IF bad_sql then raise notice 'Тов. '||user||' хочет получить выговор за шаловливые ручки';end IF;
.....

Опции просмотра комментариев

Выберите предпочитаемый вами способ показа комментариев и нажмите "Сохранить настройки" для активации изменений.

Back to top

(С) Виктор Вислобоков, 2008-2023