I am working on an Oracle SQL query. The query is getting flagged by Fortify SCA for Privilege Management: Default Function or Procedure Rights. Can someone help me with the correct way of using the query?
The query I want to use:
CREATE OR REPLACE PROCEDURE "reset_sequence"
IS
l_value NUMBER;
BEGIN
EXECUTE IMMEDIATE 'SELECT "ordering_seq".nextval FROM dual' INTO l_value;
EXECUTE IMMEDIATE 'ALTER SEQUENCE "ordering_seq" INCREMENT BY -' || l_value || ' MINVALUE 0';
EXECUTE IMMEDIATE 'SELECT "ordering_seq".nextval FROM dual' INTO l_value;
EXECUTE IMMEDIATE 'ALTER SEQUENCE "ordering_seq" INCREMENT BY 1 MINVALUE 0';
END;
I can spot a couple of issues here:
There's no authid clause, so it defaults to definer
execute immediate with string concatenation
This means anyone with execute privileges on the procedure is running with the full rights of the procedure owner. And with string concatenation, there's the risk of SQL injection. Yes, even with numbers.
Also you can get the next value of a sequence by assigning it. No need for execute immediate.
To be safe, I'd make the following changes:
Add authid current_user
Explicitly to_char the increment, avoiding attacks on this
Giving:
create sequence ordering_seq
start with 100;
select ordering_seq.nextval from dual;
NEXTVAL
100
CREATE OR REPLACE PROCEDURE reset_sequence
authid current_user
IS
l_value NUMBER;
BEGIN
l_value := ordering_seq.nextval;
EXECUTE IMMEDIATE 'ALTER SEQUENCE ordering_seq INCREMENT BY -' ||
to_char ( l_value, 'TM', 'NLS_Numeric_Characters = ''.,''' ) ||
' MINVALUE 0';
l_value := ordering_seq.nextval;
EXECUTE IMMEDIATE 'ALTER SEQUENCE ordering_seq INCREMENT BY 1 MINVALUE 0';
END;
/
exec reset_sequence;
select ordering_seq.nextval from dual;
NEXTVAL
1
Of course, using invoker's rights means you have to give out alter sequence rights to whoever calls. Which brings its own issues. To overcome this you could use Code-Based Access Control.
From the Fortify docs:
Privilege Management: Default Package Rights
PLSQL/TSQL
Abstract
Packages without an AUTHID clause default to AUTHID DEFINER.
Explanation
PL/SQL packages can be either AUTHID DEFINER or AUTHID CURRENT_USER. Functions and procedures in a package with definer's rights execute under the privileges of the user that defines the package. This can allow updates and access to specific pieces of data without granting access to entire tables or schemas. In a package with invoker's rights, or AUTHID CURRENT_USER, functions and procedures execute under the privileges of the user who invokes them. This does not allow a user to gain access to data it didn't already have access to. If no AUTHID clause is provided, the package defaults to definer's rights.
Packages are usually defined by SYS or another highly privileged user, making any exploits of the code potentially more dangerous.
So it seems you just need to add an AUTHID clause, even if that is just explicitly stating the default value again (though you should establish the correct value, of course).
Not relevant, but neither select needs to be dynamic - you may have chosen to do that so it looks more consistent with the alter statements, but it isn't necessary; and they don't even need to be selects any more - you could do:
CREATE OR REPLACE PROCEDURE "reset_sequence"
AUTHID CURRENT_USER
IS
l_value NUMBER;
BEGIN
l_value := "ordering_seq".nextval;
EXECUTE IMMEDIATE 'ALTER SEQUENCE "ordering_seq" INCREMENT BY -' || l_value || ' MINVALUE 0';
l_value := "ordering_seq".nextval;
EXECUTE IMMEDIATE 'ALTER SEQUENCE "ordering_seq" INCREMENT BY 1 MINVALUE 0';
END;
Related
I wrote a Procedure which essentially used to drop table.
It works as expected , but the problem is As this Procedure is in "DW" schema and dropping table is in "SCRATCH".
Because of this, we get insufficient privileges.
Any way we can achieve this and solve the "privilege" issue?
CREATE OR REPLACE PROCEDURE DW.PROC AUTHID CURRENT_USER IS
V_TABLE_NAME VARCHAR2(255);
V_DELETE_DT NUMBER(33);
V_LIST SYS_REFCURSOR;
BEGIN
SELECT TO_NUMBER(VALUE) INTO V_DELETE_DT FROM DW.LIST_OF_TABLE
OPEN V_LIST FOR
SELECT OBJECT_NAME FROM ALL_OBJECTS WHERE OBJECT_TYPE= 'TABLE' AND
OBJECT_NAME LIKE '%DLY_BKP%' AND CREATED <=SYSDATE - V_DELETE_DT;
LOOP
FETCH V_LIST
INTO V_TABLE_NAME;
EXIT WHEN V_LIST%NOTFOUND;
EXECUTE IMMEDIATE 'DROP TABLE SCRATCH.'||V_TABLE_NAME ;
END LOOP;
CLOSE V_LIST;
END;
To drop a table in another schema, you must do one of the following:
Be granted the "DROP ANY TABLE" system privilege. This is highly elevated and it is likely your DBAs will balk at it, but it'll work.
Create a procedure owned by the table owner that accepts a table name as a parameter and issues the drop DDL on your behalf. Grant execute privs on the procedure to your connect user. This is usually the better choice. You can further enhance it to table-drive a list of tables or table name patterns that are white-listed or black-listed for drop, whatever makes sense.
At it's most simplest:
CREATE OR REPLACE PROCEDURE scratch.drop_table(in_table_name IN varchar2)
AS
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE "'||in_table_name||'"';
END;
/
GRANT EXECUTE ON scratch.drop_table TO dw;
/
Then DW calls it:
BEGIN
scratch.drop_table('TABLE_TO_DROP');
END;
If for any reason you cannot create this procedure under the SCRATCH schema (which is an unreasonable restriction), you can create it under a privileged account like SYSTEM and also add the table owner name as an additional parameter. Then you most definitely will want to table-drive a set of white-listed owners to prevent misuse of the procedure.
I have written a procedure to grant permissions on all the tables of a particular schema to rest other schemas.
create or replace PROCEDURE GRANTS_PROC
IS
CURSOR GRANTS_CURSOR
IS
SELECT 'GRANT SELECT, INSERT, UPDATE, DELETE ON "'
||T.OWNER
||'"."'
||TABLE_NAME
||'" TO '
||(SELECT rtrim(listagg(U.username||',')
within group (order by U.username),',') USERNAME
FROM ALL_USERS U
WHERE U.USERNAME!=T.OWNER
AND U.USERNAME IN
('AAA','BBB','CCC','DDD','EEE','FFF','GGG','HHH','III'))||';' FINAL_TXT
FROM ALL_TABLES T
WHERE T.OWNER IN
('AAA','BBB','CCC','DDD','EEE','FFF','GGG','HHH','III')
ORDER BY T.OWNER,UPPER(T.TABLE_NAME);
BEGIN
--DBMS_OUTPUT.PUT_LINE('CURSOR_GRANTS.FINAL_TXT');
--QRY_TEXT:='ABC';
FOR CURSOR_GRANTS IN GRANTS_CURSOR
LOOP
DBMS_OUTPUT.PUT_LINE(CURSOR_GRANTS.FINAL_TXT);
EXECUTE IMMEDIATE CURSOR_GRANTS.FINAL_TXT;
END LOOP;
END;
/
The above procedure compiled successfully, but while execution it is not getting into FOR loop to run the EXECUTE IMMEDIATE block but PL/SQL procedure compiled successfully.
What could be done to fix my procedure and make it work?
All_USERS and ALL_TABLES should be changed to DBA_USERS and DBA_TABLES, Hence my procedure works.
Because the all_tables view shows the tables that the owner of the stored procedure has privileges on, so, unless the stored procedure is being created by a privileged user, or a user that already has those grants (with grant option so they can actually give the privileges), there may be nothing in all_tables that qualifies.
I want to create temporary table in stored procedure and access it in the same but I got error that ORA-00942:Table or view does not exists.
Following is the procedure that i tried,
Create procedure myproc
IS
stmt varchar2(1000);
BEGIN
stmt:='CREATE GLOBAL TEMPORARY table temp(list if columns) ON COMMIT DELETE ROWS';
execute immediate stmt;
insert into temp values('list of column values');
END;
This is the way I used to create temporary table but I got error, is there any other way to perform this task?
Just create it first (once, outside of your procedure), and then use it in your procedure. You don't want to (try to) create it on every call of the procedure.
create global temporary table tmp(x clob)
on commit delete rows;
create or replace procedure...
-- use tmp here
end;
Create or replace procedure myprocedure
is
stmt varchar2(1000);
stmt2 varchar2(1000);
begin
stmt := 'create global temporary table temp(id number(10))';
execute immediate stmt;
stmt2 := 'insert into temp(id) values (10)';
execute immediate stmt2;
end;
I have edited this answer as it was wrong. I am a recent MSSQL convert and because of the way oracle implements global temp tables, if you really DO need to use temp tables, creating them once and leaving them there is the way to go. Unless you use dynamic sql in your procs exclusively (have fun debugging), you will not be able to successfully compile your package unless the tables referenced already exist. Oracle validates any objects referenced in methods that you attempt to compile, which is why you got the 942 error. I love the way Oracle manages scope with these global temp tables. That, alone, sold me on the idea.
Use this
Create of replace procedure myprocedure
is
stmt varchar2(1000);
stmt2 varchar2(1000);
begin
stmt := 'create global temporary table temp(id number(10))';
execute immediate stmt;
stmt2 := 'insert into temp(id) values (10)';
execute immediate stmt2;
end;
CREATE OR REPLACE PROCEDURE myproc IS
BEGIN
CREATE GLOBAL TEMPORARY TABLE temp (id NUMBER(10)) ON COMMIT DELETE ROWS AS
SELECT 10 FROM dual;
END;
/
I have a PL/SQL package in an Oracle 10g database and I want to write a function that returns the name of the schema that the package (and hence the function) is defined in. Anyone know how to do this?
create function xcurr return varchar2 is
v_curr varchar2(32);
begin
SELECT SYS_CONTEXT ('USERENV', 'CURRENT_USER') into v_curr from dual;
return v_curr;
end;
This will work as long as the PL/SQL object doesn't have AUTHID CURRENT_USER.
From Oracle 10g CURRENT_USER, as used in Gary Myers' answer, is deprecated. Oracle recommends that you use the SESSION_USER parameter instead, which:
For enterprises users, returns the schema. For other users, returns
the database user name by which the current user is authenticated.
This value remains the same throughout the duration of the session.
I would use CURRENT_SCHEMA. There are subtle differences between the two as CURRENT_SCHEMA changes if an ALTER SESSION SET CURRENT_SCHEMA statement is issued.
There's also no need to SELECT from dual; you can assign the return value of SYS_CONTEXT directly to the variable.
DECLARE
v_current_schema varchar2(30);
BEGIN
v_current_schema := SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');
dbms_output.put_line('I am using the schema [' || v_current_schema || ']');
END;
There is probably an easier way but you could use dbms_utility.format_call_stack and parse the results to get the schema name. This works in Oracle 9i.
CREATE OR REPLACE PROCEDURE test
AS
sql_stmt VARCHAR2(200);
BEGIN
sql_stmt := 'ALTER TABLE daily_table PARTITIONS p1 , p2 into PARTITION p2';
EXECUTE IMMEDIATE sql_stmt;
END ;
/
The above procedure is giving me the following error -
ORA-01031: insufficient privileges
ORA-06512: at "test", line 8
ORA-06512: at line 6
But if I run the ALTER Command directly on the sql prompt, I am not receiving any error..
I am wondering what permission I need to provide the user to perform the merge from the procedure.
I fixed the issue by using AUTHID CURRENT_USER
CREATE OR REPLACE PROCEDURE test AUTHID CURRENT_USER
AS
sql_stmt VARCHAR2(200);
BEGIN
sql_stmt := 'ALTER TABLE daily_table PARTITIONS p1 , p2 into PARTITION p2';
EXECUTE IMMEDIATE sql_stmt;
END ;
/
Note: you should never use DDL in stored procedures. Below is the sample of very, very badly designed code which should be avoided.
CREATE OR REPLACE PROCEDURE test
AS
sql_stmt VARCHAR2(200);
BEGIN
sql_stmt := 'GRANT ALTER ON daily_table TO your_user';
EXECUTE IMMEDIATE sql_stmt;
sql_stmt := 'ALTER TABLE daily_table PARTITIONS p1 , p2 into PARTITION p2';
EXECUTE IMMEDIATE sql_stmt;
END ;
/
I'm sure that if someone creates a stored proc SPECIFICALLY for DDL, then they realize that it is commited automatically.
I do use DDL in stored procedures: most commonly to truncate summary tables that the procedure will then re-populate; now and then for DDLish tasks such as renaming columns of an imported table to conform to Oracle's standard rules for an identifier, or for creating primary keys and sequences for named tables. Generally I use
dbms_utility.exec_ddl_statement(blah);
rather than
EXECUTE IMMEDIATE blah;
a prejudice I won't attempt to justify. I will say, that having packaged procedure supplied and documented by Oracle suggests that it is not to be prohibited across the board.