Logo Oracle Deutschland   Application Express Community
Netzwerkprivilegien für APEX und PL/SQL in Oracle12c
Erscheinungsmonat APEX-Version Datenbankversion Cloud oder on Premise
November 2015 5.0 ab 12.1.0.1 on Premise und Cloud

Möchte mab mit einer APEX-Anwendung oder mit PL/SQL Code auf das Netzwerk zugreifen, so benötigt man schon seit der Version 11g zusätzliche Privilegien; es müssen sog. Netzwerk-ACL eingerichtet werden. Ohne diese Netzwerk-ACL führen Netzwerkzugriff zu Fehlermeldungen, wie das folgende Beispiel zeigt.

SQL> select httpuritype('www.oracle.com').getclob() from dual;
ERROR:
ORA-29273: HTTP-Anforderung nicht erfolgreich
ORA-24247: Netzwerkzugriff von Access Control-Liste (ACL) abgelehnt
ORA-06512: in "SYS.HTTPURITYPE", Zeile 38

Die Netzwerk-ACL muss vom DBA oder einem Datenbankuser mit DBA-Privilegien eingerichtet werden. Eine solche Netzwerk-ACL erlaubt den Zugriff ...

In Oracle12c wurde neue Syntax zum Einrichten der Netzwerk-ACL eingeführt. Die alte 11g-Syntax (Community Tipp) funktioniert auch in Oracle12c noch, so dass alte Skripte weiterhin laufen. Allerdings bringt Oracle12c neben der veränderten Syntax eine wesentlich feiner granulare Erteilung von Netzwerkrechten: so wird nicht einfach nur ein Host und ein Port freigeschaltet, vielmehr können auch einzelne Netzwerkprotokolle wie HTTP oder SMTP freigegeben werden. Allein aus diesem Grund ist sehr zu empfehlen, alte Skripte und Netzwerk-Grants aus Oracle11g-Zeiten auf die neue Syntax, die in diesem Tipp beschrieben wird, zu migrieren.

Damit PL/SQL-Pakete wie DBMS_LDAP, UTL_HTTP, UTL_SMTP oder UTL_MAIL funktionieren, muss die Netzwerk-ACL für den jeweiligen Datenbank-User (das Parsing Schema der Anwendung) eingerichtet werden. Für APEX-Pakete wie APEX_WEB_SERVICE , APEX_MAIL oder APEX_LDAP muss die Netzwerk-ACL dagegen für den User der APEX-Engine, also APEX_050000 (oder APEX_040200 für ältere Installationen), eingerichtet werden.

In Oracle12c ist das Einrichten einer ACL syntaktisch wesentlich leichter als bisher. Es braucht nur den Aufruf von APPEND_HOST_ACE im PL/SQL Paket DBMS_NETWORK_ACL_ADMIN. Die Netzwerk-ACL, die dem Datenbankuser NETWORK den Zugriff auf die Ressource www.oracle.com über das HTTP-Protokoll erlaubt, wird wie folgt aufgesetzt (COMMIT nicht vergessen).

begin
  sys.dbms_network_acl_admin.append_host_ace(
    host        => 'www.oracle.com'
   ,ace         => xs$ace_type(
      privilege_list     => xs$name_list('http')
     ,granted            => true
     ,principal_name     => 'NETWORK'
     ,principal_type     => XS_ACL.PTYPE_DB
    )
  );
end;
/

Die Netzwerkressource wird im Parameter host angegeben. Anstelle konkreter Hostnamen können auch IP-Adressen und auch Wildcards (*) vergeben werden. Ein einzelner Stern geht recht weit; er gibt das gesamte Netzwerk für diesen User frei. Die Angaben zum Grantee werden im Parameter ace gemacht - dort wird festgelegt, welche Netzwerk-Operationen erlaubt sein sollen, an wen das Recht vergeben wird und ob es ein positives oder negatives Recht ist. Nach erfolgreichem Einrichten der ACL kann der User NETWORK seinen HTTPURITYPE-Aufruf machen. Der principal_type, der mit XS_ACL.PTYPE_DB gesetzt ist, legt fest, dass NETWORK ein Datenbankuser ist.

SQL> select httpuritype('www.oracle.com').getclob() from dual

HTTPURITYPE('WWW.ORACLE.COM').GETCLOB()
--------------------------------------------------------------------------------
<!--[if HTML5]><![endif]-->
<!doctype html>
<meta http-equiv="x-ua-compatible"
:

Der Syntax nach legt man gar keine Access Control List (ACL) an, vielmehr fügt man nur Einträge (ACE) hinzu. Das Anlegen der "Liste" und das Löschen ggfs. leerer Listen übernimmt die Datenbank automatisch. Mehr als diesen einen Aufruf braucht es zur Freigabe eines Netzwerk-Privilegs nicht. Die Dictionary View DBA_HOST_ACLS enthält alle Access Control Lists - eine Liste wird, wie man sehen kann, einem Netzwerk-Hostnamen (bzw. einer IP-Adresse) zugeordnet.

SQL> select host, lower_port, upper_port, acl from dba_host_acls;

HOST                           LOWER_PORT UPPER_PORT ACL
------------------------------ ---------- ---------- --------------------------------------------
www.oracle.com                                       NETWORK_ACL_266063E20ADB3D31E05396F4A50A2B0F

Die konkreten Listeneinträge finden sich in der View DBA_HOST_ACES.

SQL> select privilege, grant_type, principal, host from dba_host_aces where principal='NETWORK';

PRIVILEGE  GRANT PRINCIPAL            HOST
---------- ----- -------------------- -----------------------------------
HTTP       GRANT NETWORK              www.oracle.com

Oft befindet sich die Datenbank hinter einer Firewall, so dass ein Proxy-Server zum Zugriff auf das Netzwerk nötig ist. Dieser muss dann mit UTL_HTTP.SET_PROXY (oder in der APEX-Anwendung) gesetzt werden, was dann aber wiederum zu einer ACL-Fehlermeldung führt, denn für den Proxy-Server wurden (noch) keine Netzwerk-Rechte vergeben.

SQL> exec utl_http.set_proxy('myproxy.mycompany.de:8080'); 

PL/SQL-Prozedur erfolgreich abgeschlossen.

SQL> select httpuritype('www.oracle.com').getclob() from dual
ERROR:
ORA-29273: HTTP-Anforderung nicht erfolgreich
ORA-24247: Netzwerkzugriff von Access Control-Liste (ACL) abgelehnt
ORA-06512: in "SYS.HTTPURITYPE", Zeile 38

Für den Zugriff auf den Proxy-Server braucht es einen weiteren ACL-Eintrag - richten Sie also (wiederum mit DBA-Privilegien) folgende ACE ein. Achten Sie auf die Parameter upper_port und lower_port; damit kann der Zugriff auch auf ganz bestimmte TCP/IP-Ports beschränkt werden. Fehlen die Parameter, so sind alle Ports freigegeben.

begin
  sys.dbms_network_acl_admin.append_host_ace(
    host        => 'myproxy.mycompany.de'
   ,lower_port  => 8080
   ,upper_port  => 8080
   ,ace         => xs$ace_type(
      privilege_list     => xs$name_list('http_proxy')
     ,granted            => true
     ,principal_name     => 'NETWORK'
     ,principal_type     => XS_ACL.PTYPE_DB
    )
  );
end;
/

Mit dem Privileg http_proxy wird der angegebene Proxy-Server zwar freigegeben, aber allein als Proxy-Server. Ein direkter HTTP-Connect auf den Server ist nicht möglich. Neben http und http_proxy sehen die Netzwerk-ACLs die folgenden Privilegien vor.

Nach dem Hinzufügen des Proxy-Server sieht der Inhalt der Data Dictionary View DBA_HOST_ACES wie folgt aus:

SQL> select privilege, grant_type, principal, host from dba_host_aces where principal='NETWORK';

PRIVILEGE  GRANT PRINCIPAL            HOST
---------- ----- -------------------- -----------------------------------
HTTP       GRANT NETWORK              www.oracle.com
HTTP_PROXY GRANT NETWORK              myproxy.mycompany.de

Positive oder negative Rechte sind vor allem im Zusammenhang mit Wildcards oder mit TCP/IP Port Bereichen (Upper Port und Lower Port) interessant. Das folgende Beispiel erlaubt den Zugriff auf das gesamte Netzwerk, nicht jedoch auf den Server apex.oracle.com.

begin
  sys.dbms_network_acl_admin.append_host_ace(
    host        => 'apex.oracle.com'
   ,ace         => xs$ace_type(
      privilege_list     => xs$name_list('http')
     ,granted            => false
     ,principal_name     => 'NETWORK'
     ,principal_type     => XS_ACL.PTYPE_DB
    )
  );
end;
/

begin
  sys.dbms_network_acl_admin.append_host_ace(
    host        => '*'
   ,ace         => xs$ace_type(
      privilege_list     => xs$name_list('http')
     ,granted            => true
     ,principal_name     => 'NETWORK'
     ,principal_type     => XS_ACL.PTYPE_DB
    )
  );
end;
/

Die Reihenfolge, in der die Einträge gemacht werden, ist wichtig, denn die Datenbank wertet sie in genau der gleichen Reihenfolge aus. Es ist wichtig, dass das Verbot für apex.oracle.com vor der Erlaubnis für das ganze Netzwerk erfolgt; ansonsten würde diese zuerst ausgewertet und der Zugriff würde freigegeben, bevor die Datenbank sich mit dem Verbot überhaupt beschäftigt. Wenn Sie unsicher sind, ob die Einträge in der richtigen Reihenfolge vorliegen, schauen Sie in der View DBA_HOST_ACES in die Spalte ACE_ORDER.

select ace_order, privilege, grant_type, principal, host 
from dba_host_aces 
where principal='NETWORK';

 ACE_ORDER PRIVILEGE  GRANT PRINCIPAL            HOST
---------- ---------- ----- -------------------- -----------------------------------
         1 HTTP       DENY  NETWORK              apex.oracle.com
         1 HTTP       GRANT NETWORK              www.oracle.com
         2 HTTP       GRANT NETWORK              *
         3 HTTP_PROXY GRANT NETWORK              myproxy.mycompany.de

Möchte man Einträge löschen, so verwendet man die Prozedur DBMS_NETWORK_ACL_ADMIN.REMOVE_HOST_ACE wie folgt.

begin
  sys.dbms_network_acl_admin.remove_host_ace(
    host        => 'apex.oracle.com'
   ,ace         => xs$ace_type(
      privilege_list     => xs$name_list('http')
     ,granted            => false
     ,principal_name     => 'NETWORK'
     ,principal_type     => XS_ACL.PTYPE_DB
    )
   ,remove_empty_acl => true
  );
end;
/

Leider bietet die Datenbank aus dem Stand derzeit keine Möglichkeit zum Export bestehender ACL-Definitionen als Skript an. Aber die im folgenden Skript enthaltene Funktion EXPORT_ACLS erledigt diese Aufgabe.

create or replace function export_acls  (
  p_principal       varchar2 default null
) return clob authid definer
 is
  v_sql varchar2(32767) := '';
  v_clob clob;
  v_prevuser varchar2(128) := '""';

  procedure append(p_str in varchar2) is
  begin
    v_sql := v_sql || p_str || chr(10);
  end append;

begin
  dbms_lob.createtemporary(v_clob, true, dbms_lob.call);
  v_sql := '';
  append('/********************************************************************');
  append(' * PL/SQL Network ACL');
  append(' *'); 
  append(' * exported as of '||to_char(sysdate, 'YYYY-MM-DD HH24:MI'));
  append(' *'); 
  append(' * Database hostname: '||utl_inaddr.get_host_name);
  append(' * Database host IP:  '||utl_inaddr.get_host_address);
  append(' * Database name      '||sys_context('userenv','DB_NAME'));
  append(' * Database domain    '||sys_context('userenv','DB_DOMAIN'));
  append(' ********************************************************************/');
  append('');
  dbms_lob.writeappend(v_clob, length(v_sql), v_sql);
  v_sql := '';
  for ace in (
    select 
      host,
      principal,
      case when
        privilege = 'http-proxy' then 'http_proxy' 
        else privilege
      end as privilege,
      grant_type,
      to_char(START_DATE, 'YYYY-MM-DD HH24:MI:SS.FFFFFF6 TZH:TZM') start_date, 
      to_char(END_DATE, 'YYYY-MM-DD HH24:MI:SS.FFFFFF6 TZH:TZM')   end_date,
      upper_port,
      lower_port
    from dba_host_aces 
    where principal = p_principal or p_principal is null
    order by ace_order asc
  ) loop  

    if v_prevuser != ace.principal then
      if v_prevuser != '""' then 
        append('end;');
        append('/');
        append('');  

      end if;
      append('begin');
      v_prevuser := ace.principal;
    end if;  

    append('  sys.dbms_network_acl_admin.append_host_ace(');
    append('    host        => '''||ace.host||'''');
    if not lower(ace.privilege) = 'resolve' then
      if ace.lower_port is not null then
        append('   ,lower_port  => '||nvl(to_char(ace.lower_port), 'null')||'');
      end if;
      if ace.upper_port is not null then
        append('   ,upper_port  => '||nvl(to_char(ace.upper_port), 'null'));
      end if;
    end if;
    append('   ,ace         => xs$ace_type(');
    append('      privilege_list     => xs$name_list('''||ace.privilege||''')');
    append('     ,granted            => '||case when ace.grant_type='DENY' then 'false' else 'true' end);
    append('     ,principal_name     => '''||ace.principal||'''');
    append('     ,principal_type     => XS_ACL.PTYPE_DB');
    if ace.start_date is not null then
      append('     ,start_date  => to_timestamp_tz('''||ace.start_date||''',''YYYY-MM-DD HH24:MI:SS.FFFFFF6 TZH:TZM'')'); 
    end if;
    if ace.end_date is not null then
      append('     ,end_date    => to_timestamp_tz('''||ace.end_date||''',''YYYY-MM-DD HH24:MI:SS.FFFFFF6 TZH:TZM'')'); 
    end if;
    append('    )');
    append('  );');
  end loop;
  append('end;');
  append('/');
  append('sho err');
  append('');
  append('');

  if v_sql is not null then 
    dbms_lob.writeappend(v_clob, length(v_sql), v_sql);
  end if;
  return v_clob;
end;
/
sho err

Als DBA lassen sich die Netzwerk-Privilegien für einen bestimmten User dann wie folgt als Skript exportieren.

SQL> select export_acls('NETWORK') from dual;

EXPORT_ACLS('NETWORK')
-------------------------------------------------------------------------
/********************************************************************
 * PL/SQL Network ACL
 *
 * exported as of 2015-12-08 11:12
 *
 * Database hostname: sccloud001.de.oracle.com
 * Database host IP:  10.165.244.150
 * Database name      orcl
 * Database domain
 ********************************************************************/

begin
  sys.dbms_network_acl_admin.append_host_ace(
    host        => 'apex.oracle.com'
   ,ace         => xs$ace_type(
      privilege_list     => xs$name_list('HTTP')
     ,granted            => false
     ,principal_name     => 'NETWORK'
     ,principal_type     => XS_ACL.PTYPE_DB
    )
  );

  sys.dbms_network_acl_admin.append_host_ace(
    host        => 'www.oracle.com'
   ,ace         => xs$ace_type(
      privilege_list     => xs$name_list('HTTP')
   :

Neben den Host-ACLs kennt Oracle12c noch Wallet ACLs. Ein Datenbankuser muss in eine Wallet ACL eingetragen sein, wenn er Zugriff auf ein Wallet auf dem Datenbankserver nehmen möchte - aber nur dann, wenn er Zertifikate in diesem Wallet zur Authentifizierung gegenüber einem externen Server nutzen will. Wird ein Wallet einfach nur benötigt, um eine SSL-Verbindung zu einem externen Server zu öffnen, so braucht es keine Wallet ACL.

Das PL/SQL Paket DBMS_NETWORK_ACL_ADMIN enthält eine ganze Reihe an Funktionen und Prozeduren; nur die wenigsten sind aber in Oracle12c tatsächlich nötig - die meisten stammen noch aus Oracle11g und sind daher deprecated. Die folgende Übersicht zeigt die Funktionen, die Sie zum Erstellen von Netzwerk-ACLs verwenden sollten. Alle anderen Funktionen sollten in Oracle12c nicht mehr genutzt werden.

Das Einrichten von Netzwerk-Privilegien für PL/SQL-Code oder APEX-Anwendungen ist mit Oracle12c wesentlich leichter geworden als bisher. Letztlich muss man nur eine Host ACE eintragen, das Verwalten der eigentlichen Access Control Listen macht die Datenbank automatisch.

Sind noch Skripte zum Erstellen von Netzwerk-ACL für Oracle11g vorhanden, so sollten diese auf die neue Oracle12c-Syntax umgestellt werden. Die Skripte werden nicht nur einfacher, auch die konkreten Rechte können viel feiner granular vergeben werden.

Zurück zur Community-Seite