ヘッダーをスキップ
Oracle Databaseセキュリティ・ガイド
11g リリース1(11.1)
E05730-05
  目次
目次
索引
索引

戻る
戻る
 
次へ
次へ
 

5 アプリケーション開発者のセキュリティの管理

この章の内容は、次のとおりです。

アプリケーション・セキュリティ・ポリシーの概要

安全なデータベース・アプリケーションを作成する最初のステップは、アプリケーション・セキュリティ・ポリシーの作成です。アプリケーション・セキュリティ・ポリシーは、アプリケーション・セキュリティの要件およびデータベース・オブジェクトへのユーザー・アクセスを規制するルールのリストです。

セキュリティ・ポリシーは、データベース・アプリケーションごとに立案する必要があります。たとえば、各データベース・アプリケーションには、アプリケーションの実行時に異なるレベルのセキュリティを提供する1つ以上のデータベース・ロールが必要です。このデータベース・ロールは、ユーザー・ロールに付与するか、または特定のユーザー名に直接付与できます。

(SQL*PlusやSQL Developerなどのツールを使用して)SQL文を制限なしで処理できるアプリケーションの場合でも、機密扱いのスキーマ・オブジェクトや重要なスキーマ・オブジェクトへの不当なアクセスを防ぐために、セキュリティ・ポリシーが必要です。特に、使用するアプリケーションでパスワードが安全に処理されることを確認する必要があります。

次の各項では、安全なデータベース・アプリケーションの計画と開発に使用できる、アプリケーション・セキュリティとOracle Databaseの機能について説明します。

アプリケーション・ベースのセキュリティの使用に関する考慮事項

アプリケーション・セキュリティを構築および実装する際の2つの主な課題を、次の各項で説明します。

アプリケーション・ユーザーはデータベース・ユーザーでもあるか

できるかぎり、アプリケーション・ユーザーがデータベース・ユーザーであるアプリケーションを作成する必要があります。これによって、データベースに本来備わっているセキュリティ・メカニズムを活用できます。

多くの市販のパッケージ・アプリケーションでは、アプリケーション・ユーザーは、データベース・ユーザーではありません。これらのアプリケーションでは、複数のユーザーがユーザー自身をアプリケーションに対して認証し、次に、アプリケーションが単一の高い権限を持つユーザーとしてデータベースに接続します。 これを、One Big Application Userモデルと呼びます。

この方法で作成されたアプリケーションは、通常、データベースに本来備わっているセキュリティ機能の多くを使用できません。これは、ユーザーの識別情報がデータベースで認識されないためです。

表5-1では、One Big Application Userモデルが様々なOracle Databaseセキュリティ機能にどのように影響するかを説明します。

表5-1 One Big Application Userモデルの影響を受ける機能

Oracle Database機能 One Big Application Userモデルの制限

監査

セキュリティの基本原則の1つは、監査によるアカウンタビリティです。One Big Application Userがデータベース内のすべての操作を実行する場合、データベース監査では、個々のユーザーが実行したアクションに対する責任をそのユーザー自身に持たせることができません。アプリケーションでは、独自の監査メカニズムを実装して、ユーザーのアクションを個別に捕捉する必要があります。

Oracle Advanced Securityの拡張認証

データベースに対するクライアントの認証が、個々のユーザーではなくアプリケーションの場合、Oracle Advanced Securityでサポートされている認証の厳密な形式(たとえば、SSLやトークンなどでのクライアント認証)は使用できません。

ロール

ロールは、データベース・ユーザーに割り当てられます。エンタープライズ・ロールは、データベース内で作成されていない場合でも、エンタープライズ・ユーザーに割り当てられ、データベースに認識されます。アプリケーション・ユーザーがデータベース・ユーザーではない場合、ロールの有用性は低くなります。この場合、アプリケーションでは独自のメカニズムを作成して、様々なアプリケーション・ユーザーがアプリケーション内のデータへアクセスするのに必要とする権限を識別する必要があります。

Oracle Advanced Securityのエンタープライズ・ユーザー管理機能

エンタープライズ・ユーザー管理機能は、Oracle Internet DirectoryなどのLDAPベースのディレクトリにユーザー情報を安全に格納して認可を管理することで、OracleデータベースがOracle Identity Managementインフラストラクチャを使用できるようにします。エンタープライズ・ユーザーは、データベース内に作成する必要はありませんが、データベースに認識される必要があります。One Big Application Userモデルでは、Oracle Identity Managementを利用できません。


アプリケーション内またはデータベース内でのセキュリティ規定

ユーザーがデータベース・ユーザーでもあるアプリケーションでは、セキュリティをアプリケーション内に作成するか、またはデータベースに本来備わっているセキュリティ・メカニズムに依存できます。このセキュリティ・メカニズムには、きめ細かな複数の権限、仮想プライベート・データベース(アプリケーション・コンテキストでのファイングレイン・アクセス・コントロール)、ロール、ストアド・プロシージャおよび監査(ファイングレイン監査を含む)などがあります。アプリケーションでは、できるかぎりデータベースのセキュリティ規定メカニズムを使用することをお薦めします。

セキュリティがアプリケーション内ではなくデータベース自体で規定されている場合は、セキュリティを回避することはできません。アプリケーション・ベースのセキュリティの主なデメリットは、ユーザーがアプリケーションを回避してデータにアクセスすると、セキュリティが回避されることです。たとえば、データベースへのSQL*Plusアクセス権を持つユーザーは、人事管理アプリケーションを介さずに問合せを実行できます。したがって、このユーザーは、アプリケーション内のセキュリティ対策のすべてをバイパスします。

One Big Application Userモデルを使用するアプリケーションでは、データベースのセキュリティ・メカニズムを使用せずに、アプリケーション内にセキュリティ規定を作成する必要があります。ユーザーを認識するのは、データベースではなくアプリケーションであるため、アプリケーション自体が各ユーザーに対してセキュリティ対策を規定する必要があります。

このアプローチでは、データにアクセスする各アプリケーションでのセキュリティの再実装が必要になります。組織では複数のアプリケーションに同じセキュリティ・ポリシーを実装する必要があるため、セキュリティが高コストになり、新規アプリケーションごとに高コストな再実装が必要になります。

アプリケーション設計におけるパスワードの保護

この項では、バッチ・ジョブ、スクリプト、インストール・ファイルまたはアプリケーションからパスワードで保護されたサービスを安全に起動する戦略について説明します。パスワード保護に加えて、これらの戦略の多くはその他の機密データ(暗号化鍵など)に適用できます。

この項の内容は、次のとおりです。


関連項目:


アプリケーションでのパスワードの保護に関する一般的なガイドライン

これらのガイドラインは次のカテゴリに分類されます。

プラットフォーム固有のセキュリティへの脅威

次の潜在的なセキュリティへの脅威は、わかりにくい可能性があるので注意してください。

  • UNIXおよびLinuxプラットフォームでは、同じホスト・コンピュータ上のすべてのオペレーティング・システム・ユーザーがコマンド・パラメータを表示できます。結果として、コマンドラインに入力したパスワードは他のユーザーに公開される可能性があります。ただし、UNIXおよびLinux以外のプラットフォームがこの脅威を回避できるとは考えないでください。

  • HP Tru64およびIBM AIXなど一部のUNIXプラットフォームでは、すべてのオペレーティング・システム・ユーザーがあらゆるプロセスの環境変数を表示できます。ただし、UNIXおよびLinux以外のプラットフォームがこの脅威を回避できるとは考えないでください。

  • Microsoft Windowsでは、コマンド・リコール機能(上矢印)に、コマンド起動間のユーザー入力が記憶されます。たとえば、SQL*PlusのCONNECT SYSTEM/password表記法を使用し、終了してから上矢印を押してCONNECTコマンドを繰り返した場合、コマンド・リコール機能により接続文字列が明らかになり、パスワードが表示されます。また、Microsoft Windows以外のプラットフォームがこの脅威を回避できるとは考えないでください。

パスワード入力を処理するアプリケーションの設計

次のガイドラインに従ってください。

  • パスワードの入力を対話形式で求めるアプリケーションを設計します。コマンドライン・ユーティリティの場合、コマンド・プロンプトでパスワードを公開することをユーザーに強制しないでください。

    アプリケーションの設計に使用するプログラミング言語のAPIについて、ユーザーからのパスワードを処理する最適な方法を確認します。この機能を処理するJavaコードの例については、「Javaでのパスワード読取り例」を参照してください。

  • SQLインジェクション攻撃からデータベースを保護します。 SQLインジェクション攻撃では、PL/SQLアプリケーションで想定されない方法でSQL文が追加または変更されます。たとえば、侵入者はWHERE句をTRUEに設定してパスワード認証を回避できます。

    SQLインジェクション攻撃の問題に対処するには、バインド変数引数を使用するか妥当性チェックを作成します。詳細は、『Oracle Database PL/SQL言語リファレンス』を参照してください。

  • 可能な場合は、認証を遅延するアプリケーションを設計します。次に例を示します。

    • ログインに証明書を使用します。

    • オペレーティング・システムで提供される機能を使用してユーザーを認証します。たとえば、Microsoft Windows上で動作するアプリケーションはドメイン認証を使用できます。

  • パスワードをマスクまたは暗号化します。パスワードを格納する必要がある場合、パスワードをマスクまたは暗号化します。たとえば、ログ・ファイルでパスワードをマスクし、リカバリ・ファイルでパスワードを暗号化できます。

  • 各接続を認証します。たとえば、スキーマAがデータベースAに存在する場合、スキーマAのデータベース2が同一のユーザーであるとは考えないでください。同様に、ローカル・オペレーティング・システム・ユーザーpsmithは、必ずしもリモート・ユーザーpsmithと同じユーザーではありません。

  • ファイルまたはリポジトリにクリアテキスト・パスワードを格納しないでください。パスワードをファイルに格納すると、侵入者がパスワードにアクセスする危険性が高まります。

  • 1つのマスター・パスワードを使用します。次に例を示します。

    • 1人のデータベース・ユーザーに、他のデータベース・ユーザーとなるためのプロキシ認証を付与できます。この場合、必要となるデータベース・パスワードは1つのみです。詳細は、「プロキシを介して接続するようにユーザー・アカウントを変更する方法」を参照してください。

    • マスター・パスワードでオープンできるパスワード・ウォレットを作成できます。ウォレットにはその他のパスワードが含められます。Wallet Managerの詳細は、『Oracle Database Advanced Security管理者ガイド』を参照してください。

パスワードの形式と動作の構成

次のガイドラインに従ってください。

SQL*PlusおよびSQLスクリプトにおけるパスワードの処理

次のガイドラインに従ってください。

  • プログラムまたはスクリプトのコマンドラインにパスワードを指定してSQL*Plusを起動しないでください。パスワードが必須であるにもかかわらず省略した場合、SQL*Plusではパスワードの入力を求めるプロンプトが表示され、パスワードが表示されないようにエコー機能が自動的に無効となります。

    次の各例は、パスワードがコマンドライン上で公開されないため安全です。また、Oracle Databaseではこれらのパスワードがネットワークを介して自動的に暗号化されます。

    sqlplus system
    Enter password: password
    
    SQL> connect system
    Enter password: password
    

    次の例では、パスワードが他のオペレーティング・システム・ユーザーに公開されます。

    sqlplus system/password
    

    次の例は、2つのセキュリティ上のリスクをもたらします。1つ目の例では、覗き込んでいる可能性のある他のユーザーにパスワードを公開してしまいます。2つ目の例では、Microsoft Windowsなどの一部のプラットフォームにおいて、パスワードがコマンドラインのリコール攻撃を受けやすくなります。

    sqlplus /nolog
    SQL> connect system/password
    
  • たとえばパスワードまたは秘密鍵を必要とするSQLスクリプトの場合、アカウントを作成したりあるアカウントでログインするには、置換変数&1、&2などの位置パラメータを使用しないでください。かわりに、ユーザーに値の入力を求めるプロンプトを表示するスクリプトを設計します。また、スクリプトから、またはスプール・モードを使用している場合に出力を表示するエコー機能を無効にする必要があります。エコー機能を無効にするには、次の設定を使用します。

    SET ECHO OFF
    

    スクリプトにより値の目的を明白にする必要があります。 たとえば、値によりアカウントまたは証明書などの新しい値が設定されるかどうか、または既存のアカウントへのログインなど、値が認証されるかどうかを明白にする必要があります。

    次の例は、セキュリティ上のリスクをもたらす方法でユーザーがスクリプトを起動することが回避されるため安全です。パスワードはエコーされず、スプール・ファイルに記録されません。


    1
    2
    3
    4
    
    SET VERIFY OFF
     ACCEPT user CHAR PROMPT 'Enter user to connect to: '
     ACCEPT password CHAR PROMPT 'Enter the password for that user: ' HIDE
     CONNECT &user/&password
    

    この例の説明は、次のとおりです。

    • 1行目: パスワードの表示を防止します。(SET VERIFYは、置換の前後にスクリプトの各行をリストします。)SET VERIFY OFFコマンドをHIDEコマンド(3行目)と結合する方法は、パスワードおよびその他の機密入力データを非表示にする便利なテクニックです。

    • 3行目: ACCEPT passwordプロンプトにHIDEオプションを含めます。これによって入力パスワードのエコーが回避されます。

    次の例では位置パラメータを使用しており、ユーザーがコマンドライン上でパスワードを渡すことでスクリプトを起動できるため、セキュリティ上のリスクをもたらします。ユーザーがパスワードを入力せず、入力を求めるプロンプトが表示される場合、ユーザーが入力した内容がすべて画面およびスプール・ファイル(スプールが有効な場合)にエコーされるため危険です。

    CONNECT &1/&2
    
  • バッチ・スクリプトのログイン時間を制御します。パスワードを必要とするバッチ・スクリプトの場合、スクリプトが実行されるはずの時間にのみログインできるようアカウントを設定します。たとえば、毎晩午後8時から1時間実行されるバッチ・スクリプトがあるとします。スクリプトがこの時間にのみログインできるようアカウントを設定します。侵入者がアクセス権を獲得した場合、漏洩したアカウントを悪用できる機会はほとんどありません。

  • パスワードの入力を求めるDML文またはDDL SQL文を使用する場合は注意してください。この場合、機密情報がネットワークを介してクリアテキストで渡されます。Oracle Advanced Securityを使用してこの問題を解決できます。詳細は、『Oracle Database Advanced Security管理者ガイド』を参照してください。

    次のパスワード変更例は、パスワードが公開されないため安全です。

    password psmith
    Changing password for psmith
    New password: password
    Retype new password: password
    

    この例は、パスワードがコマンドラインおよびネットワーク上の両方に公開されるため、セキュリティ上のリスクをもたらします。

    ALTER USER psmith IDENTIFIED BY password
    

外部パスワード・ストアを使用したパスワードの保護

データベースに接続するためのパスワード資格証明は、クライアント側のOracleウォレットを使用して格納できます。Oracleウォレットは、ユーザーのログインに必要な認証および署名用証明書を格納する安全性の高いソフトウェア・コンテナです。

安全性の高い外部パスワード・ストアの詳細は、「パスワード資格証明用の安全性の高い外部パスワード・ストアの管理」を参照してください。また、Oracle Wallet Managerを使用したOracleウォレットの構成方法は、『Oracle Database Advanced Security管理者ガイド』を参照してください。

orapwdユーティリティを使用したパスワードの保護

ネットワークを介してSYSDBAまたはSYSOPER権限を使用してアプリケーションに接続する必要があるユーザーについて、パスワード・ファイルを作成できます。パスワード・ファイルを作成するには、ORAPWDユーティリティを使用します。パスワード・ファイルの作成およびメンテナンスの詳細は、『Oracle Database管理者ガイド』を参照してください。

Javaでのパスワード読取り例

例5-1では、パスワードの読取りに使用できるJavaパッケージの作成方法を示しています。

例5-1 パスワードを読み取るためのJavaコード

// Change the following line to a name for your version of this package
package passwords.sysman.emSDK.util.signing;

import java.io.IOException;
import java.io.PrintStream;
import java.io.PushbackInputStream;
import java.util.Arrays;

/**
 * The static readPassword method in this class issues a password prompt
 * on the console output and returns the char array password
 * entered by the user on the console input.
 */
public final class ReadPassword {
  //----------------------------------
  /**
   * Test driver for readPassword method.
   * @param args the command line args
   */
  public static void main(String[] args) {
    char[] pass = ReadPassword.readPassword("Enter password: ");
    System.out.println("The password just entered is \""
      + new String(pass) + "\"");
    System.out.println("The password length is " + pass.length);
  }
   * Issues a password prompt on the console output and returns
   * the char array password entered by the user on the console input.
   * The password is not displayed on the console (chars are not echoed).
   * As soon as the returned char array is not needed,
   * it should be erased for security reasons (Arrays.fill(charArr, ' '));
   * A password should never be stored as a java String.
   *
   * Note that Java 6 has a Console class with a readPassword method,
   * but there is no equivalent in Java 5 or Java 1.4.
   * The readPassword method here is based on Sun's suggestions at
   * http://java.sun.com/developer/technicalArticles/Security/pwordmask.
   *
   * @param prompt the password prompt to issue
   * @return new char array containing the password
   * @throws RuntimeException if some error occurs
   */
  public static final char[] readPassword(String prompt)
  throws RuntimeException {
    try {
      StreamMasker masker = new StreamMasker(System.out, prompt);
      Thread threadMasking = new Thread(masker);
      int firstByte = -1;
      PushbackInputStream inStream = null;
      try {
        threadMasking.start();
        inStream = new PushbackInputStream(System.in);
        firstByte = inStream.read();
      } finally {
        masker.stopMasking();
      }
      try {
        threadMasking.join();
      } catch (InterruptedException e) {
        throw new RuntimeException("Interrupt occurred when reading password");
      }
      if (firstByte == -1) {
        throw new RuntimeException("Console input ended unexpectedly");
      }
      if (System.out.checkError()) {
        throw new RuntimeException("Console password prompt output error");
      }
      inStream.unread(firstByte);
      return readLineSecure(inStream);
    }
    catch (IOException e) {
      throw new RuntimeException("I/O error occurred when reading password");
    }
  }
  //----------------------------------
  /**
   * Reads one line from an input stream into a char array in a secure way
   * suitable for reading a password.
   * The char array will never contain a '\n' or '\r'.
   *
   * @param inStream the pushback input stream
   * @return line as a char array, not including end-of-line-chars;
   *  never null, but may be zero length array
   * @throws RuntimeException if some error occurs
   */
  private static final char[] readLineSecure(PushbackInputStream inStream)
  throws RuntimeException {
    if (inStream == null) {
      throw new RuntimeException("readLineSecure inStream is null");
    }
    try {
      char[] buffer = null;
      try {
        buffer = new char[128];
        int offset = 0;
        // EOL is '\n' (unix), '\r\n' (windows), '\r' (mac)
        loop:
        while (true) {
          int c = inStream.read();
          switch (c) {
          case -1:
          case '\n':
            break loop;
          case '\r':
            int c2 = inStream.read();
            if ((c2 != '\n') && (c2 != -1))
              inStream.unread(c2);
            break loop;
          default:
            buffer = checkBuffer(buffer, offset);
            buffer[offset++] = (char) c;
            break;
          }
        }
        char[] result = new char[offset];
        System.arraycopy(buffer, 0, result, 0, offset);
        return result;
      }
      finally {
        if (buffer != null)
          Arrays.fill(buffer, ' ');
      }
    }
    catch (IOException e) {
      throw new RuntimeException("I/O error occurred when reading password");
    }
  }
  //----------------------------------
  /**
   * This is a helper method for readLineSecure.
   *
   * @param buffer the current char buffer
   * @param offset the current position in the buffer
   * @return the current buffer if it is not yet full;
   *  otherwise return a larger buffer initialized with a copy
   *  of the current buffer and then erase the current buffer
   * @throws RuntimeException if some error occurs
   */
  private static final char[] checkBuffer(char[] buffer, int offset)
  throws RuntimeException
  {
    if (buffer == null)
      throw new RuntimeException("checkBuffer buffer is null");
    if (offset < 0)
      throw new RuntimeException("checkBuffer offset is negative");
    if (offset < buffer.length)
      return buffer;
    else {
      try {
        char[] bufferNew = new char[offset + 128];
        System.arraycopy(buffer, 0, bufferNew, 0, buffer.length);
        return bufferNew;
      } finally {
        Arrays.fill(buffer, ' ');
      }
    }
  }
  //----------------------------------
  /**
   * This private class prints a one line prompt
   * and erases reply chars echoed to the console.
   */
  private static final class StreamMasker
  extends Thread {
    private static final String BLANKS = StreamMasker.repeatChars(' ', 10);
    private String m_promptOverwrite;
    private String m_setCursorToStart;
    private PrintStream m_out;
    private volatile boolean m_doMasking;
    //----------------------------------
    /**
     * Constructor.
     * @throws RuntimeException if some error occurs
     */
    public StreamMasker(PrintStream outPrint, String prompt)
    throws RuntimeException {
      if (outPrint == null)
        throw new RuntimeException("StreamMasker outPrint is null");
      if (prompt == null)
        throw new RuntimeException("StreamMasker prompt is null");
      if (prompt.indexOf('\r') != -1)
        throw new RuntimeException("StreamMasker prompt contains a CR");
      if (prompt.indexOf('\n') != -1)
        throw new RuntimeException("StreamMasker prompt contains a NL");
      m_out = outPrint;
      m_setCursorToStart = StreamMasker.repeatChars('\010',
        prompt.length() + BLANKS.length());
      m_promptOverwrite = m_setCursorToStart + prompt + BLANKS
        + m_setCursorToStart + prompt;
    }
    //----------------------------------
    /**
     * Begin masking until asked to stop.
     * @throws RuntimeException if some error occurs
     */
    public void run()
    throws RuntimeException {
      int priorityOriginal = Thread.currentThread().getPriority();
      Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
      try {
        m_doMasking = true;
        while (m_doMasking) {
          m_out.print(m_promptOverwrite);
          if (m_out.checkError())
            throw new RuntimeException("Console output error writing prompt");
          try {
            Thread.currentThread().sleep(1);
          } catch (InterruptedException ie) {
            Thread.currentThread().interrupt();
            return;
          }
        }
        m_out.print(m_setCursorToStart);
      } finally {
        Thread.currentThread().setPriority(priorityOriginal);
      }
    }
    //----------------------------------
    /**
     * Instructs the thread to stop masking.
     */
    public void stopMasking() {
      m_doMasking = false;
    }
    //----------------------------------
    /**
     * Returns a repeated char string.
     *
     * @param c the char to repeat
     * @param length the number of times to repeat the char
     * @throws RuntimeException if some error occurs
     */
    private static String repeatChars(char c, int length)
    throws RuntimeException {
      if (length < 0)
        throw new RuntimeException("repeatChars length is negative");
      StringBuffer sb = new StringBuffer(length);
      for (int i = 0; i < length; i++)
        sb.append(c);
      return sb.toString();
    }
  }
}

アプリケーション権限の管理

ほとんどのデータベース・アプリケーションでは、異なるスキーマ・オブジェクトごとに異なる権限が関与します。各アプリケーションに必要な権限の追跡は、複雑な場合があります。また、アプリケーションを実行するユーザーの認可には、多くのGRANT操作が関与する場合があります。

アプリケーションの権限管理を簡素化するために、アプリケーションごとに作成した1つのロールに、1人のユーザーがそのアプリケーションを実行するために必要なすべての権限を付与できます。実際には、1つのアプリケーションに複数のロールがある可能性があり、各ロールには、アプリケーションの実行中に使用できる機能の多少を決める権限の特定サブセットが付与されます。

たとえば、すべての管理アシスタントが休暇アプリケーションを使用して、部門のメンバーが取得した休暇を記録するとします。このアプリケーションを効率的に管理するには、次の操作手順が必要です。

  1. VACATIONロールを作成します。

  2. 休暇アプリケーションに必要なすべての権限をVACATIONロールに付与します。

  3. VACATIONロールをすべての管理アシスタントに付与します。より効率的な方法は、管理アシスタントが持つ権限を定義したロールを作成し、VACATIONロールをそのロールに付与します。

複数のアプリケーション権限を1つのロールにグループ化すると、権限の管理に役立ちます。次の管理オプションを考えてみます。


関連項目:


アプリケーションへのアクセスを制御するセキュア・アプリケーション・ロールの作成

「セキュア・アプリケーション・ロールを使用したロール権限の保護」で説明したように、セキュア・アプリケーション・ロールは、ロールに対応付けられているPL/SQLパッケージを介してのみ有効化できるロールです。このパッケージは、アプリケーションへのアクセスを制御するために必要なポリシーを定義します。

この項の内容は、次のとおりです。


関連項目:


セキュア・アプリケーション・ロールの作成方法の例は、『Oracle Database 2日でセキュリティ・ガイド』を参照してください。

手順1: セキュア・アプリケーション・ロールの作成

セキュア・アプリケーション・ロールは、IDENTIFIED USING句が指定されたCREATE ROLE SQL文を使用して作成します。この文を実行するには、CREATE ROLEシステム権限が必要です。

たとえば、sec_mgr.hr_adminパッケージに対応付けられるhr_adminというセキュア・アプリケーション・ロールを作成するには、次の手順を実行します。

  1. 次のようにセキュア・アプリケーション・ロールを作成します。

    CREATE ROLE hr_admin IDENTIFIED USING sec_mgr.hr_admin_role_check;
    

    この例から次のようなことがわかります。

  2. セキュア・アプリケーション・ロールに対して、このロールに通常対応付ける権限を付与します。

    たとえば、HR.EMPLOYEES表に対するSELECTINSERTUPDATEおよびDELETE権限をhr_adminロールに付与するには、次の文を入力します。

    GRANT SELECT, INSERT, UPDATE, DELETE ON hr.employees TO hr_admin;
    

    このロールをユーザーに直接付与しないでください。ユーザーがセキュリティ・ポリシーを通過した場合は、PL/SQLプロシージャまたはパッケージによってこのロールが自動的に付与されます。ユーザーにロールを直接付与する必要があるサイトの場合は、そのユーザーに対してこのロールを使用禁止にする必要があります。これは、パッケージのセキュリティ・ポリシーによるチェックの実行を開始するためには、このロールが最初は使用禁止である必要があるためです。たとえば、ユーザーpsmithに対してデフォルトのロールを使用禁止にするには、次の文を入力します。

    ALTER USER psmith DEFAULT ROLE NONE
    

手順2: アプリケーションに対するアクセス・ポリシーを定義するPL/SQLパッケージの作成

セキュア・アプリケーション・ロールを使用可能または使用禁止にするには、PL/SQLパッケージ内にロールのセキュリティ・ポリシーを作成します。個別のプロシージャを作成してこれを実行することもできますが、パッケージを使用すると、一連のプロシージャをグループ化できます。これにより、一緒に使用するポリシーのグループを作成して、アプリケーションを保護するための強固なセキュリティ戦略を提示できます。セキュリティ・ポリシーに失敗したユーザー(潜在的な侵入者)については、監査チェックをパッケージに追加して、その失敗を記録できます。通常、このパッケージは、セキュリティ管理者のスキーマに作成します。

このパッケージまたはプロシージャは、次の内容を実行する必要があります。

  • 実行者権限を使用してロールを使用可能にする必要があります。実行者権限を使用してパッケージを作成するには、AUTHIDプロパティをCURRENT_USERに設定する必要があります。定義者権限を使用してパッケージを作成することはできません。

    実行者権限と定義者権限の詳細は、『Oracle Database PL/SQL言語リファレンス』を参照してください。

  • ユーザーを検証するためのセキュリティ・チェックを1つ以上組み込む必要があります。ユーザーを検証する方法の1つは、SYS_CONTEXT SQLファンクションを使用することです。SYS_CONTEXTの詳細は、『Oracle Database SQL言語リファレンス』を参照してください。ユーザーに関するセッション情報を検索するために、アプリケーション・コンテキストでSYS_CONTEXTを使用できます。詳細は、第6章「アプリケーション・コンテキストを使用したユーザー情報の取得」を参照してください。

  • ユーザーがセキュリティ・チェックを通過した場合にSET ROLE SQL文を発行する必要があります。実行者権限を使用してパッケージを作成するため、SET ROLE SQL文を発行してロールを設定する必要があります。PL/SQL埋込みSQL構文ではSET ROLE文がサポートされていませんが、EXECUTE IMMEDIATEなどで動的SQLを使用することでSET ROLEを起動できます。

    EXECUTE IMMEDIATEの詳細は、『Oracle Database PL/SQL言語リファレンス』を参照してください。

このパッケージまたはプロシージャの作成方法が原因で、セキュア・アプリケーション・ロールを使用可能または使用禁止にする際にログイン・トリガーを使用できません。かわりに、ユーザーがセキュリティ・アプリケーション・ロールで付与された権限を使用する前のユーザー・ログイン時に、アプリケーションからパッケージを直接起動します。

たとえば、hr_adminロールを使用するユーザーを、午前8時から午後5時の時間にサイトにいる(つまり、特定の端末を使用する)従業員に制限するとします。システム管理者またはセキュリティ管理者は、次の手順を実行します。

  1. 次のようにプロシージャを作成します。


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
    CREATE OR REPLACE PROCEDURE hr_admin_role_check
     AUTHID CURRENT_USER
     AS
     BEGIN
      IF (SYS_CONTEXT ('userenv','ip_address')
        BETWEEN '192.0.2.10' and '192.0.2.20'
         AND
        TO_CHAR (SYSDATE, 'HH24') BETWEEN 8 AND 17)
      THEN
        EXECUTE IMMEDIATE 'SET ROLE hr_admin';
      END IF;
     END;
    /
    

    この例の説明は、次のとおりです。

    • 2行目: 実行者権限を使用できるように、AUTHIDプロパティをCURRENT_USERに設定します。

    • 5行目: ユーザー・セッション情報を取得するSYS_CONTEXT SQLファンクションを使用してユーザーを検証します。

    • 6–8行目: アクセス権を付与または拒否するテストを作成します。このテストでは、午前8時から午後5時の時間にサイトで(つまり、特定の端末を使用して)作業するユーザーにアクセスを制限します。ユーザーがこのチェックを通過すると、hr_adminロールが付与されます。

    • 9–10行目: ユーザーがこのテストを通過した場合は、EXECUTE IMMEDIATEコマンドを使用してSET ROLE文を発行し、そのユーザーにロールを付与します。

  2. ロールが割り当てられたユーザーに対して、hr_admin_role_checkプロシージャのEXECUTE権限を付与します。

    次に例を示します。

    GRANT EXECUTE ON hr_admin_role_check TO psmith;
    

セキュア・アプリケーション・ロールをテストするには、そのユーザーでSQL*Plusにログインし、ロールを使用可能にして、ロールで付与される権限が必要なアクションを実行してみます。

次に例を示します。

CONNECT PSMITH
Enter password: password

EXECUTE sec_admin.hr_admin_role_check;

-- actions requiring privileges granted by the role

権限とユーザー・データベース・ロールの関連付け

ユーザーの権限が、現在のデータベース・ロールに関連した権限のみであることを確認します。

この項の内容は、次のとおりです。

ユーザーの権限が現在のデータベース・ロールのみである理由

1人のユーザーが、多数のアプリケーションおよび多数の対応付けられたロールを使用できます。ただし、このユーザーの権限は、現在のデータベース・ロールに関連した権限のみであることを確認する必要があります。次の使用例を考えてみます。

  • (「受注」というアプリケーションの)ORDERロールには、INVENTORY表に対するUPDATE権限が含まれています。

  • (「在庫」というアプリケーションの)INVENTORYロールには、INVENTORY表に対するSELECT権限が含まれています。

  • 何人かの受注入力担当には、ORDERロールとINVENTORYロールの両方が付与されています。

この使用例では、両方のロールを付与された受注入力担当は、INVENTORYアプリケーションの実行時に、ORDERロールの権限を使用してINVENTORY表を更新できます。問題は、INVENTORY表の更新がINVENTORYアプリケーションでは許可されていないアクションであることです。この更新は、ORDERアプリケーションで許可されているアクションです。この問題を回避するには、次の項で説明するSET ROLE文を使用します。

ロールを自動的に使用可能または使用禁止にするSET ROLE文の使用

各アプリケーションの開始時にSET ROLE文を使用して、各アプリケーションに対応付けられているロールを自動的に使用可能にし、他のすべてのロールを使用禁止にします。この方法によって、各アプリケーションでは、必要な場合にのみ、ユーザーの特定の権限を動的に使用可能にします。

SET ROLE文は、権限の管理を簡素化します。ユーザーがどのような情報にアクセスできるかと、その情報にいつアクセスできるかを制御します。また、このSET ROLE文によって、ユーザーは、明確に定義された権限ドメイン内で操作を続行できます。あるユーザーがロールからのみ権限を取得している場合、そのユーザーはこれらの権限を組み合せて不正な操作を実行することはできません。


関連項目:


スキーマを使用したデータベース・オブジェクトの保護

スキーマとは、データベース・オブジェクトを格納できるセキュリティ・ドメインです。各ユーザーまたはロールに付与された権限によって、これらのデータベース・オブジェクトへのアクセスが制御されます。

この項の内容は、次のとおりです。

一意スキーマでのデータベース・オブジェクトの保護

ほとんどのスキーマはユーザー名と考えることができます。つまり、ユーザーがデータベースに接続してそのデータベース・オブジェクトにアクセスすることを可能にするアカウントです。ただし、一意スキーマではデータベースへの接続は許可されませんが、関連する一連のオブジェクトを格納するために使用されます。この種のスキーマは通常のユーザーとして作成されますが、CREATE SESSIONシステム権限は(明示的にもロールを介しても)付与されません。ただし、CREATE SCHEMA文を使用して、1つのトランザクション内に複数の表とビューを作成する場合は、一意スキーマにCREATE SESSIONおよびRESOURCE権限を一時的に付与する必要があります。

たとえば、所定のスキーマが特定のアプリケーションのスキーマ・オブジェクトを所有する場合があります。アプリケーション・ユーザーに権限がある場合、そのユーザーは、一般的なデータベース・ユーザー名を使用してデータベースに接続し、アプリケーションとそれに対応するオブジェクトを使用できます。ただし、ユーザーはアプリケーションに設定されたスキーマを使用して、データベースに接続することはできません。この構成は、対応付けられたオブジェクトへのスキーマを介したアクセスを防ぎ、スキーマ・オブジェクトの保護を強化します。この場合、アプリケーションではALTER SESSION SET CURRENT_SCHEMA文を発行して、ユーザーを適切なアプリケーション・スキーマに接続できます。

共有スキーマでのデータベース・オブジェクトの保護

多くのアプリケーションでは、ユーザーはデータベースで自分自身のアカウントつまりスキーマを必要としません。これらのユーザーに必要なのは、アプリケーション・スキーマへのアクセスのみです。たとえば、ユーザーJohn、FiruzehおよびJaneはすべて給与アプリケーションのユーザーで、financeデータベースのpayrollスキーマにアクセスする必要があるとします。この場合、データベースに自分自身のオブジェクトを作成する必要があるユーザーはいません。これらのユーザーに必要なのは、payrollオブジェクトへのアクセスのみです。 この問題に対処するために、Oracle Advanced Securityではエンタープライズ・ユーザー(スキーマに依存しないユーザー)が提供されています。

エンタープライズ・ユーザー、つまりディレクトリ・サービスで管理されるユーザーは、共有データベース・スキーマを使用するため、データベース・ユーザーとして作成する必要はありません。管理コストを削減するために、管理者はディレクトリに1つのエンタープライズ・ユーザーを1回作成し、他の多数のユーザーもアクセスできる共有スキーマで、そのユーザーを指し示すことができます。

エンタープライズ・ユーザー管理の詳細は、『Oracle Databaseエンタープライズ・ユーザー・セキュリティ管理者ガイド』を参照してください。

アプリケーションでのオブジェクト権限の管理

アプリケーション設計の一部として、そのアプリケーションで作業するユーザーのタイプ、およびユーザーが担当のタスクを達成するために必要なアクセスのレベルを決定する必要があります。これらのユーザーを複数のロール・グループに分類し、各ロールに付与する権限を決定する必要があります。

この項の内容は、次のとおりです。

アプリケーション開発者に必要なオブジェクト権限に関する知識

通常、エンド・ユーザーにはオブジェクト権限が付与されています。オブジェクト権限によって、ユーザーは、特定の表、ビュー、順序、プロシージャ、ファンクションまたはパッケージに対して特定のアクションを実行できます。

表5-2に、オブジェクトの各タイプで使用できるオブジェクト権限の概要を示します。

表5-2 権限とスキーマ・オブジェクトとの関連

オブジェクト権限 表への適用 ビューへの適用 順序への適用 プロシージャへの適用脚注1 

ALTER

あり

なし

あり

なし

DELETE

あり

あり

なし

なし

EXECUTE

なし

なし

なし

あり

INDEX

あり脚注2 

なし

なし

なし

INSERT

あり

あり

なし

なし

REFERENCES

あり脚注2

なし

なし

なし

SELECT

あり

あり脚注3 

あり

なし

UPDATE

あり

あり

なし

なし


脚注1スタンドアロンのストアド・プロシージャ、ファンクションおよびパブリック・パッケージ構成

脚注2ロールに付与できない権限

脚注3スナップショットにも付与可能

スキーマ・オブジェクトの監査方法の詳細は、「スキーマ・オブジェクトの監査」も参照してください。

オブジェクト権限によって許可されるSQL文

アプリケーションの実装時およびテスト時には、必要な各ロールを作成する必要があります。各ロールの使用例をテストし、データベースへのアクセス権がアプリケーション・ユーザーに正しく付与されることを確認します。テスト終了後は、アプリケーションの管理者と共同で各ユーザーに適切なロールが割り当てられていることを確認します。

表5-3に、表5-2で示したオブジェクト権限によって許可されるSQL文を示します。

表5-3 データベース・オブジェクト権限によって許可されるSQL文

オブジェクト権限 許可されるSQL文

ALTER

ALTERオブジェクト(表または順序)

CREATE TRIGGER ONオブジェクト(表のみ)

DELETE

DELETE FROMオブジェクト(表、ビューまたはシノニム)

EXECUTE

EXECUTEオブジェクト(プロシージャまたはファンクション)

パブリック・パッケージ変数への参照

INDEX

CREATE INDEX ONオブジェクト(表、ビューまたはシノニム)

INSERT

INSERT INTOオブジェクト(表、ビューまたはシノニム)

REFERENCES

オブジェクト(表のみ)に対するFOREIGN KEY整合性制約を定義するCREATEまたはALTER TABLE

SELECT

SELECT...FROMオブジェクト(表、ビュー、シノニムまたはスナップショット)

順序を使用するSQL文


オブジェクト権限の詳細は、「権限とロールの概要」を参照してください。SQL文の監査方法の詳細は、「SQL文の監査」も参照してください。

データベース通信のセキュリティを強化するためのパラメータ

データベース管理者は、この項の次の手順に従ってアプリケーションのセキュリティを管理できます。

プロトコル・エラーによってデータベースで受信した不正パケットのレポート

サーバーが不正なパケット、順序に誤りがあるパケット、プライベートまたは未使用のリモート・プロシージャ・コールを受信した場合、Oracle Call Interface(OCI)やTwo-Task Common(TTC)などのネットワーキング通信ユーティリティでは、スタック・トレースおよびヒープ・ダンプを格納する大規模なディスク・ファイルを生成できます。通常、このディスク・ファイルは、非常に大規模になる可能性があります。 侵入者は、サーバーに不正なパケットを繰り返し送信し、ディスクあふれやサービスの拒否を発生させることで、システムを使用できないようにする可能性があります。認証されていないクライアントが、この種の攻撃を仕掛ける可能性もあります。

これらの攻撃は、SEC_PROTOCOL_ERROR_TRACE_ACTION初期化パラメータを次の値のいずれかに設定することで防止できます。

  • None: サーバーが不正なパケットを無視し、トレース・ファイルまたはログ・メッセージを生成しないように構成します。サーバーの可用性が不正なパケットの受信を認識することよりも圧倒的に重要な場合は、この設定を使用します。

    次に例を示します。

    SEC_PROTOCOL_ERROR_TRACE_ACTION = None
    
  • Trace(デフォルト設定): トレース・ファイルを作成します。これは、ネットワーク・クライアントが不具合の結果として不正なパケットを送信している場合など、デバッグを目的とする場合に便利です。

    次に例を示します。

    SEC_PROTOCOL_ERROR_TRACE_ACTION = Trace
    
  • Log: サーバー・トレース・ファイルに1行の短いメッセージを書き込みます。この選択肢では、一定レベルの監査とシステムの可用性とのバランスがとれます。

    次に例を示します。

    SEC_PROTOCOL_ERROR_TRACE_ACTION = Log
    
  • Alert: サーバー・トレース・ファイルとアラート・ログに1行の短いエラー・メッセージを書き込みます。

    次に例を示します。

    SEC_PROTOCOL_ERROR_TRACE_ACTION = Alert
    

不正パケット受信後のサーバー実行の終了または再開

Oracle Databaseは、クライアントまたはサーバー・プロトコルからエラーを検出した後も実行を継続する必要があります。ただし、これによって、ディスクあふれまたはサービス拒否攻撃を発生させる可能性のある不正なパケットを、サーバーがさらに受信する可能性があります。

サーバーが悪意のあるクライアントから不正なパケットを受信しているときに、サーバー・プロセスの実行を詳細に制御するには、SEC_PROTOCOL_ERROR_FURTHER_ACTION初期化パラメータを次の値のいずれかに設定します。

  • Continue(デフォルト設定): サーバーの実行を継続します。ただし、サーバーがさらに攻撃を受ける可能性があることに注意してください。

    次に例を示します。

    SEC_PROTOCOL_ERROR_FURTHER_ACTION = Continue
    
  • Delay,m: クライアントをm秒間遅延させます。この秒数を経過すると、サーバーは同じクライアント接続から次のリクエストを受け入れます。この設定は、悪意のあるクライアントがサーバー・リソースを過度に使用することを防ぎます。正当なクライアントはパフォーマンス低下の影響を受けますが、機能は続行できます。

    次に例を示します。

    SEC_PROTOCOL_ERROR_FURTHER_ACTION = Delay,3
    
  • Drop,n: クライアント接続は、n個の不正パケットを受信した後に強制的に終了します。この設定を使用すると、トランザクションの損失など、クライアントを犠牲にしてサーバー自体を保護できます。ただし、クライアントは再度接続して、同じ操作を再試行できます。

    次に例を示します。

    SEC_PROTOCOL_ERROR_FURTHER_ACTION = Drop,10
    

認証の最大試行回数の構成

Oracle Databaseでは、サーバー・プロセスが最初に起動し、次にクライアントがこのサーバー・プロセスで認証を受けます。侵入者は、最初にサーバー・プロセスを起動し、様々なユーザー名とパスワードを使用して認証リクエストを無制限に発行し、データベースへのアクセスを試みます。

アプリケーション接続に対するログイン失敗回数を制限するには、SEC_MAX_FAILED_LOGIN_ATTEMPTS初期化パラメータを設定して、接続に対する認証試行回数を制限します。指定した認証試行回数を失敗すると、データベース・プロセスは接続を切断します。デフォルトでは、SEC_MAX_FAILED_LOGIN_ATTEMPTSは10に設定されます。

SEC_MAX_FAILED_LOGIN_ATTEMPTS初期化パラメータは、潜在的な侵入者によるアプリケーションの攻撃を防止するために設計されていることに留意してください。つまり、有効なユーザーには適用されません。sqlnet.ora INBOUND_CONNECT_TIMEOUTパラメータおよびFAILED_LOGIN_ATTEMPTS初期化パラメータもログインの失敗回数を制限しますが、これら2つのパラメータは有効なユーザー・アカウントにのみ適用される点が異なります。

たとえば、最大試行回数を5に制限するには、initsid.ora初期化パラメータ・ファイルで、次のようにSEC_MAX_FAILED_LOGIN_ATTEMPTSを設定します。

SEC_MAX_FAILED_LOGIN_ATTEMPTS = 5

データベース・バージョン・バナーの表示制御

クライアント接続(Oracle Call Interfaceクライアントを含む)が認証されてから、詳細な製品バージョン情報にアクセスできるようにする必要があります。侵入者は、データベース・バージョンを使用して、データベース・ソフトウェアに存在するセキュリティの脆弱性に関する情報を検出する可能性があります。

認証されていないクライアントに対してデータベース・バージョン・バナーの表示を制限するには、initsid.ora初期化パラメータ・ファイルでSEC_RETURN_SERVER_RELEASE_BANNER初期化パラメータをYESまたはNOのいずれかに設定します。デフォルトでは、SEC_RETURN_SERVER_RELEASE_BANNERNOに設定されます。

たとえば、YESに設定すると、Oracle Databaseに正確なデータベース・バージョンが表示されます。

Oracle Database 11g Enterprise Edition Release 11.1.0.0 - Production

今後、たとえばOracle Database 11.2.0.2をインストールした場合は、次のバナーが表示されます。

Oracle Database 11g Enterprise Edition Release 11.2.0.2 - Production

ただし、同じリリースでこのパラメータをNOに設定すると、このバナーは制限されて、リリース11.1で開始する次の固定テキストで表示されます。

Oracle Database 11g Release 11.1.0.0.0 - Production

不正なアクセスおよびユーザー・アクションの監査に関するバナーの構成

不正なアクセスおよびユーザー・アクション監査をユーザーに警告するには、バナーを作成して構成する必要があります。この通知は、クライアント・アプリケーションがデータベースにログインすると使用可能になります。

これらのバナーを構成して表示するには、データベース・サーバー側で次のsqlnet.oraパラメータを設定して、バナー情報が含まれるテキスト・ファイルを指し示します。

  • SEC_USER_UNAUTHORIZED_ACCESS_BANNER.次に例を示します。

    SEC_USER_UNAUTHORIZED_ACCESS_BANNER = /opt/Oracle/11g/dbs/unauthaccess.txt
    
  • SEC_USER_AUDIT_ACTION_BANNER.次に例を示します。

    SEC_USER_AUDIT_ACTION_BANNER = /opt/Oracle/11g/dbs/auditactions.txt
    

デフォルトでは、これらのパラメータは設定されません。

これらのパラメータを設定した後に、Oracle Call Interfaceアプリケーションでは、適切なOCI APIを使用してこれらのバナーを取得し、エンド・ユーザーに表示する必要があります。