В процессе переезда на Oracle Database 19c одной из задач возник вопрос - а как перевезти пользователя с Oracle 9 на Oracle 19c с сохранением пароля? Квест оказался ещё тот! Запрос в Oracle Support дал немного - нужно было использовать старый добрый exp. Попутно возник вопрос - а можно ли перенести пользователя с текущим паролем? Обсуждение на sql.ru показало, что таким путём придётся идти самому, немногие в 2021 году переходят с 9i на 19c.

Oracle Support подсказал следующие документы:

На sql.ru коллега дал ссылку на интересную статью на исследование Sean D. Stuber функции хэширования, используемой Oracle. Мой перевод статьи следует.

How Oracle Stores Passwords

Sean D. Stuber

Аннотация Администратору баз данных может потребоваться репликация пользователя из одной системы в другую, сохраняя пароль, или восстановить пароль после обновления системы. В старых системах (8.0.5 и ранее), чтобы временно разрешить пользователю входить в систему под другим именем, администратору базы данных или другому привилегированному пользователю потребуется изменить пароль. До 9i даже администратор баз данных не мог предоставлять разрешения на объекты схемы, не войдя в систему как владелец схемы. Поэтому нередко обнаруживалась необходимость сохранять пароли, временно изменять их для входа в систему, а затем восстанавливать старую версию, когда работа была завершена. С появлением прокси-пользователей (8i) и GRANT ANY OBJECT PRIVILEGE (9i) необходимость в выполнении этих шагов была в значительной степени устранена; но администраторам по-прежнему полезно понимать, где и как Oracle хранит пароли пользователей.

Original publication:  2009-06-09
Updated through Oracle 21c:  2021-11-01

I. АУТЕНТИФИКАЦИЯ

Имя пользователя хранится в виде обычного текста, а информация о пароле хранится в виде хеша. Когда пользователь входит в систему, аутентификационная информация хешируется в соответствии с правилами версии пароля. Если сгенерированный хеш и сохраненный хеш совпадают, пользователь аутентифицируется.

Возникает очевидный вопрос: почему Oracle использует хеширование вместо шифрования? Хеширование не является обратимой операцией; но шифрование является. Последствия этого просты. Поскольку вы не можете перевернуть хеш, вы не можете извлечь пароль из хеша; но, если пароль зашифрован, он может быть незашифрованным (хотя и с трудом, но все же возможно). Теоретически чрезвычайно удачная догадка случайных символов или перебор всех возможных комбинаций может пройти с ложноположительным результатом, поскольку хеш-алгоритмы можно дублировать. Однако простейший алгоритм Oracle дает 18 446 744 073 709 551 616 возможных хэшей. Таким образом, хотя возможно, что две разные строки могут иметь одно и то же значение, вероятность найти его мала.

II. Алгоритм хеширования и стойкость для 10G и ниже

А. Стойкость

Для версий базы данных до 11g пароли хранятся в виде 16-значного шестнадцатеричного числа в столбце PASSWORD таблицы SYS.USER $. Хэш публикуется из базовой таблицы USER $ через представление SYS.DBA_USERS.

SQL> connect dbauser/dbapassword@mydatabase
Connected.
SQL> select username,password from dba_users where username = 'TESTUSER';

USERNAME   PASSWORD
---------- ------------------
TESTUSER   AEB6397C8E7598A7

SQL> select name,password from sys.user$ where name = 'TESTUSER';

NAME       PASSWORD
---------- ------------------------
TESTUSER   AEB6397C8E7598A7


Б. Алгоритм
Хеширование представляет собой многоэтапный процесс. Сначала имя пользователя и пароль объединяются в одну строку, которая принудительно вводится в верхнем регистре. Затем эта строка преобразуется в многобайтовое необработанное значение с ведущими нулями для каждого байта. Затем это необработанное значение шифруется стандартным алгоритмом DES с использованием ключа по умолчанию. Затем последние 16 байтов вывода используются в качестве нового ключа для шифрования необработанного значения во второй раз. Последние 16 байт второго шифрования становятся сохраненным хэш-значением имени пользователя и пароля.

В методологии есть заметные недостатки. Во-первых, у конкатенации нет внешней соли, поэтому пользователи с идентичными именами и паролями, за исключением смещения символов, могут стать одними и теми же при конкатенации. Например: Пользователь ABCD с паролем EFGH будет объединен в форму: ABCDEFGH. Другой пользователь ABC с паролем DEFGH будет объединен в одну строку: ABCDEFGH. Это открывает (хотя и с экстремальным потреблением ресурсов) вектор атаки. Кроме того, алгоритм DES устарел по сравнению с современными стандартами шифрования и, как таковой, подвергается вычислительной атаке с достаточными ресурсами, особенно когда атака сочетается с радужными таблицами. Что еще более важно, принудительное использование верхнего регистра при начальной конкатенации означает, что этот алгоритм не может поддерживать пароли с учетом регистра.

C. Код
Приведенный ниже код не является реальной функцией, которую использует Oracle; но он имитирует функциональность Oracle. При вводе имени пользователя и пароля функция вернет то же значение, которое Oracle генерирует и сохраняет в столбце SYS.USER$.PASSWORD.

CREATE OR REPLACE FUNCTION create_10g_password_hash(p_name IN VARCHAR2,
                                                    p_password IN VARCHAR2)
    RETURN VARCHAR2
IS
    c_raw_zero            RAW(1) := HEXTORAW('0');
    c_encryption_method   INTEGER := 
                 DBMS_CRYPTO.encrypt_des + DBMS_CRYPTO.chain_cbc + DBMS_CRYPTO.pad_zero;
    v_str                 VARCHAR2(100);
    v_raw                 RAW(4096) := NULL;
    v_default_key         RAW(15) := HEXTORAW('0123456789ABCDEF');
    v_new_key             RAW(4096);
    v_encrypted           RAW(4096);
BEGIN
    -- Concatenate the username and password
    v_str := UPPER(p_name || p_password);

    -- Convert plain ascii string to multi-byte string with 0x00 in the high byte
    FOR i IN 1 .. LENGTH(v_str)
    LOOP
        v_raw := UTL_RAW.CONCAT(v_raw, c_raw_zero, 
                                UTL_RAW.cast_to_raw(SUBSTR(v_str, i, 1)));
    END LOOP;

    -- Encrypt with DES chipher block chaining (cbc) using default key
    -- Pad with 0's to the next even block length
    v_encrypted := DBMS_CRYPTO.encrypt(v_raw, c_encryption_method, v_default_key);

    -- Use the last 16 digits of the default encryption to generate a new key
    v_new_key := HEXTORAW(SUBSTR(RAWTOHEX(v_encrypted), -16));

    -- Re-encrypt with the new key
    v_encrypted := DBMS_CRYPTO.encrypt(v_raw, c_encryption_method, v_new_key);

    -- The last 16 digits are the hash
    RETURN SUBSTR(RAWTOHEX(v_encrypted), -16);
END create_10g_password_hash;

III. АЛГОРИТМ ХЕШИРОВАНИЯ И СТАБИЛЬНОСТЬ ДЛЯ 11G

А. Стойкость

В 11g добавлен новый алгоритм хеширования, и с этим изменением сохраненный хэш перемещен из столбца PASSWORD в USER $ в столбец SPARE4. Хэш 10g, если он все еще используется, сохраняется в старом столбце PASSWORD, как и раньше. Ни один из этих хэшей не публикуется через представление DBA_USERS. Вместо этого столбец PASSWORD представления возвращает NULL, а новый столбец PASSWORD_VERSIONS указывает, какие типы хэшей пароля хранятся.

SQL> select username,password,password_versions from dba_users where username = 'TESTUSER';

USERNAME   PASSWORD             PASSWORD_VERSIONS                                                                                                     
---------- -------------------- --------------------                                                                                                  
TESTUSER                        10G 11G                                                                                                               

SQL> select name,password,spare4 from sys.user$ where name = 'TESTUSER';

NAME       PASSWORD             SPARE4                                                                                                                
---------- -------------------- ---------------------------------------------------------------                                                       
TESTUSER   AEB6397C8E7598A7     S:17F9149EFD0BDD9DBA305D6910D5928640F7727B29F261D851C58D37FA9A

Б. Алгоритм

В 11g алгоритм хеширования улучшен двумя способами. Во-первых, используется более современный алгоритм хеширования (SHA-1) вместо получения хеша из подстрок зашифрованного вывода. Во-вторых, вместо имени пользователя в качестве соли используется рандомизированная соль. Эти изменения позволяют поддерживать чувствительность к регистру в именах пользователей и паролях.

Хотя алгоритм более надежен, он также упрощен за счет этих изменений. Сначала с помощью рандомизатора генерируется 10-байтовое (20 шестнадцатеричных цифр) значение соли. Затем это значение присоединяется к необработанной версии пароля. Это необработанное значение затем хешируется с помощью алгоритма SHA-1. Результат представлен в виде шестнадцатеричной строки с префиксом «S:» и суффиксом с шестнадцатеричным представлением соли.

Таким образом, в приведенном выше примере запись spare4 может быть разделена на части:
Это хеш пароля: 17F9149EFD0BDD9DBA305D6910D5928640F7727B
Это соль: 29F261D851C58D37FA9A

К сожалению, хотя алгоритм хеширования 11g более безопасен, чем его более старая версия, к тому времени, когда 11g был впервые выпущен в 2007 году, коллизионные атаки на SHA-1 уже были продемонстрированы двумя годами ранее.

C. Код

Приведенный ниже код имитирует функциональность хеширования Oracle 11g. Ввод пароля и солт-значения вернет то же значение, которое Oracle создает и сохраняет в столбце SYS.USER$.SPARE4.

CREATE OR REPLACE FUNCTION create_11g_password_hash(p_password IN VARCHAR2, 
                                                    p_hex_salt IN VARCHAR2)
    RETURN VARCHAR2
IS
    v_hash   RAW(80);
BEGIN
    -- The salt can be any value that is exactly 10 bytes (80 bits)
    -- i.e. the input salt must be 20 hex characters.
    IF NVL(LENGTH(p_hex_salt),0) != 20
    THEN
        raise_application_error(-20001, 'Salt must be 20 hex digits (0-9,A-F');
    END IF;

    -- Concatenate the password with the salt
    v_hash := UTL_RAW.CONCAT(UTL_RAW.cast_to_raw(p_password), HEXTORAW(p_hex_salt));

    -- Hash the concatenation
    v_hash := sys.DBMS_CRYPTO.hash(v_hash, sys.DBMS_CRYPTO.hash_sh1);

    -- Append the salt to the resulting hash and tag with "S:" prefix
    RETURN 'S:' || RAWTOHEX(v_hash) || p_hex_salt;
END create_11g_password_hash;

D. Использование 12c

Первоначальный выпуск 12c (версия 12.1.0.1) также использовал алгоритм хеширования 11g. Первый набор исправлений для 12c, 12.1.0.2, представил более новый и более безопасный алгоритм [4]. 

IV. АЛГОРИТМ ХЕШИРОВАНИЯ И УСТОЙЧИВОСТЬ ДЛЯ 12C (12.1.0.2 И ВЫШЕ)

А. Стойкость

Использование столбца SPARE4 расширено в 12.1.0.2 за счет включения хэша 12c, а также хэша 11g. Различные хэши обозначаются их префиксами (S для 11g, T для 12c) и разделяются точкой с запятой. Другой тип хэша, для XDB, также может быть включен (с префиксом «H»), но он не связан с входами обычного пользователя и как таковой выходит за рамки данной статьи. В столбце PASSWORD все еще хранятся старые хэши паролей 10g; но ни одна из версий не отображается в представлении DBA_USERS. 18c использует те же алгоритмы и конфигурацию. Сложность алгоритма хеширования 12c приводит к значительно большей хэш-строке: 160 символов, как показано ниже. Из-за длины хэш-строк приведенный ниже код будет извлекать каждую из них по отдельности, а не все в одной строке запроса.

SQL> SELECT username, password, password_versions
  2    FROM dba_users
  3   WHERE username = 'TESTUSER';

USERNAME   PASSWORD   PASSWORD_VERSIONS
---------- ---------- ---------------------
TESTUSER              10G 11G 12C

SQL> SELECT password pwd_10g
  2    FROM sys.user$
  3   WHERE name = 'TESTUSER';

PWD_10G
----------------
AEB6397C8E7598A7

SQL> SELECT REGEXP_SUBSTR(spare4, 'S:[^;]+') pwd_11g
  2    FROM sys.user$
  3   WHERE name = 'TESTUSER';

PWD_11G
--------------------------------------------------------------
S:7233E3B91B45F6B813BCFFB5D8669167CB4F498D0642558A8A3BB39948C0

SQL> SELECT REGEXP_SUBSTR(spare4, 'T:[^;]+') pwd_12c
  2    FROM sys.user$
  3   WHERE name = 'TESTUSER';

PWD_12C
------------------------------------------------------------------------------------------------------------------------------------------------------------------
T:381A70048CBB5B531196CDD2CB51393E05E3FBFB0CB019DB39AB4AAB717BB23CA7FB2EA0AD4F60B34C38C9B8CF97BB0C6A4A7530362FBF23492FB02139442AB758645C9EA1D1E33C33CB9454D0468BF9

Б. Алгоритм

Алгоритм хеширования 12c имеет несколько улучшений. Во-первых, само хеширование повторяется 4096 раз в соответствии со спецификацией PBKDF2[5] с SHA-512 в качестве функции HMAC для получения окончательного ключа. Подобно исходному алгоритму 10g и ниже, по умолчанию используется ключ хеширования или соль. Однако, в отличие от старой версии, значение по умолчанию составляет только часть полной соли, а остальная часть поступает из 128-битного входного параметра соли (представленного в виде 32 шестнадцатеричных цифр). В базе данных нет встроенной функции PBKDF2. поэтому приведенная ниже реализация включает его версию. Некоторые параметры, описанные в RFC2898, в этой версии объявлены как константы, поскольку их использование не зависит от правил хеширования паролей.

После получения ключа PBKDF2 пароль и производный ключ снова хешируются с солью пользователя с помощью метода SHA-512. Наконец, как и в случае с методом 11g, к этому хэшу добавляется соль пользователя, а затем он записывается в столбец SYS.USER$.SPARE4. Префикс «T» используется для отличия хэшей 12.1.0.2 от хэшей 11g с префиксом «S». Затем хэши 12C идентифицируются в столбце PASSWORD_VERSIONS представления DBA_USERS.

Таким образом, в приведенном выше примере запись spare4 может быть разделена на части:
Это хэш пароля 11g: 7233E3B91B45F6B813BCFFB5D8669167CB4F498D
Это соль размером 11 г: 0642558A8A3BB39948C0
Это хеш пароля 12c: 381A70048CBB5B531196CDD2CB51393E05E3FBFB0CB019DB39AB4AAB717BB23CA7FB2EA0AD4F60B34C38C9B8CF97BB0C6A4A7530362FBF23492FB02139442AB758645C9EA1D1E33C33CB9454D0468BF9
Это соль 12c: 58645C9EA1D1E33C33CB9454D0468BF9

С. Код

Функциональность Oracle 12.1.0.2 воспроизведена в приведенном ниже коде. Ввод пароля и значения соли вернет то же значение, которое Oracle создает и сохраняет в столбце SYS.USER$.SPARE4.

CREATE OR REPLACE FUNCTION create_12c_password_hash(
    p_password   IN VARCHAR2,
    p_hex_salt   IN VARCHAR2
)
    RETURN VARCHAR2
IS
    --                    .///.
    --                   (0 o)
    ---------------0000--(_)--0000---------------
    --
    --  Sean D. Stuber
    --  Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript.
    --
    --             oooO      Oooo
    --------------(   )-----(   )---------------
    --             \ (       ) /
    --              \_)     (_/

    -- derived from notes by https://www.trustwave.com/Resources/SpiderLabs-Blog

    c_dummy_salt   VARCHAR2(44) := RAWTOHEX(UTL_RAW.cast_to_raw('AUTH_PBKDF2_SPEEDY_KEY'));

    v_key          RAW(64);
    v_hash         RAW(80);

    FUNCTION pbkdf2(p_password IN VARCHAR2, p_salt IN VARCHAR2)
        RETURN VARCHAR2
    IS
        -- Simplified implementation of algorithm described in section 5.2 of RFC2898
        -- https://tools.ietf.org/html/rfc2898
        -- Iterations and key length are constant for this embedded version

        c_dk_length      CONSTANT PLS_INTEGER := 64;
        c_interations    CONSTANT PLS_INTEGER := 4096;

        -- hLen refers to number of octets (bytes) returned from HMAC function
        -- for SH512, that length is 64
        c_hmac           CONSTANT PLS_INTEGER := DBMS_CRYPTO.hmac_sh512;
        c_hlen           CONSTANT PLS_INTEGER := 64;

        c_octet_blocks   CONSTANT PLS_INTEGER := CEIL(c_dk_length / c_hlen);

        v_raw_password            RAW(32767) := UTL_RAW.cast_to_raw(p_password);
        v_raw_salt                RAW(64) := HEXTORAW(p_salt);

        v_u                       RAW(32767);
        v_f_xor_sum               RAW(32767);
        v_t_concat                RAW(32767) := NULL;
        v_block_iterator          PLS_INTEGER := 1;
    BEGIN
        -- Loop one block of hlen-octets at a time of the derived key.
        -- If we build a key past the desired length then exit early, no need to continue
        WHILE v_block_iterator <= c_octet_blocks 
          AND (v_t_concat IS NULL OR UTL_RAW.LENGTH(v_t_concat) < c_dk_length)
        LOOP
            -- The RFC describes the U(1)...U(c) values recursively
            -- but the implementation below simply loops with a stored value
            -- to achieve the same functionality.

            v_u := UTL_RAW.CONCAT(v_raw_salt, UTL_RAW.cast_from_binary_integer(v_block_iterator, UTL_RAW.big_endian));

            v_u := DBMS_CRYPTO.mac(src => v_u, typ => c_hmac, key => v_raw_password);
            v_f_xor_sum := v_u;

            FOR c IN 2 .. c_interations
            LOOP
                v_u := DBMS_CRYPTO.mac(src => v_u, typ => c_hmac, key => v_raw_password);
                v_f_xor_sum := UTL_RAW.bit_xor(v_f_xor_sum, v_u);
            END LOOP;

            v_t_concat := UTL_RAW.CONCAT(v_t_concat, v_f_xor_sum);
            v_block_iterator := v_block_iterator + 1;
        END LOOP;

        RETURN RAWTOHEX(UTL_RAW.SUBSTR(v_t_concat, 1, c_dk_length));
    END;
BEGIN
    -- The salt can be any value that is exactly 16 bytes (128 bits)
    -- i.e. the input salt must be 32 hex characters.
    IF NVL(LENGTH(p_hex_salt),0) != 32
    THEN
        raise_application_error(-20001, 'Salt must be 32 hex digits (0-9,A-F)');
    END IF;

    -- Generate key from the password using an extended salt
    v_key := pbkdf2(p_password, p_hex_salt || c_dummy_salt);

    -- Hash the key concatenated with the salt
    v_hash := sys.DBMS_CRYPTO.hash(HEXTORAW(v_key || p_hex_salt), sys.DBMS_CRYPTO.hash_sh512);

    -- Append the salt to the resulting hash and tag with "T:" prefix
    RETURN 'T:' || RAWTOHEX(v_hash) || p_hex_salt;
END create_12c_password_hash;

V. ПРИСВОЕНИЕ ПАРОЛЯ ПО ХЕШУ

A. Назначение одной версии

База данных Oracle поддерживает хорошо известную; но официально не документированный синтаксис для прямого назначения хэшей паролей. Обычное назначение пароля выполняется с помощью ALTER USER имя пользователя IDENTIFIED BY password. Если у вас есть действительные хэши паролей, вы можете использовать ALTER USER username IDENTIFIED BY VALUES hashstring. Итак, в 10g или ниже пароль обычно назначается следующим образом:

alter user testuser identified by testpwd;

Хэш версии 10G может быть назначен напрямую следующим образом:

alter user testuser identified by values 'AEB6397C8E7598A7';

Тот же синтаксис может использоваться и для хэшей паролей 11g или 12c. Хэш версии 11G будет выглядеть примерно так:

alter user testuser identified by values 'S:7233E3B91B45F6B813BCFFB5D8669167CB4F498D0642558A8A3BB39948C0';

Хэш 12c будет назначен таким же образом. Обратите внимание, что обтекание текстом связано с ограничениями ширины страницы. Хэш-значение представляет собой одну строку.

alter user testuser identified by values 'T:381A70048CBB5B531196CDD2CB51393E05E3FBFB0CB019DB39AB4AAB717BB23CA7FB2EA0AD4F60B34C38C9B8CF97BB0C6A4A7530362FBF23492FB02139442AB758645C9EA1D1E33C33CB9454D0468BF9';

Б. Назначение нескольких версий

Приведенный выше синтаксис присваивания подходит для пользователей с одним типом хэша пароля; но если PASSWORD_VERSIONS пользователя имеет несколько значений, он удалит неназначенные значения. Чтобы назначить несколько версий хэшей одновременно, хэши должны быть объединены в одну строку; разделенные точкой с запятой (;). Порядок хэшей не имеет значения. База данных разделит хэш 10g на столбец SYS.USER$.PASSWORD и расположит хэш 11g перед хэшем 12c в столбце SPARE4, если присутствуют оба. Если используется хэш XDB, упомянутый ранее [IV.A], он также может быть включен в объединенную строку.

В приведенном ниже примере с несколькими хэшами пароли объединены в следующем порядке: «12c;10g;XDB;11g», но их можно переставить в любом порядке. Опять же, хэш-строка не должна иметь разрывов строк. Обтекание текстом связано с ограничениями ширины страницы.

alter user testuser identified by values 'T:381A70048CBB5B531196CDD2CB51393E05E3FBFB0CB019DB39AB4AAB717BB23CA7FB2EA0AD4F60B34C38C9B8CF97BB0C6A4A7530362FBF23492FB02139442AB758645C9EA1D1E33C33CB9454D0468BF9;AEB6397C8E7598A7;H:55C984560887F4CE3A0F926B2A50C7DC;S:7233E3B91B45F6B813BCFFB5D8669167CB4F498D0642558A8A3BB39948C0';

VI. ЧУВСТВИТЕЛЬНОСТЬ К РЕГИСТРУ

Как отмечалось выше, хэширование 10g не поддерживает пароли с учетом регистра; но в 11g новый алгоритм может их поддерживать, если в базе данных включена эта функция. Чувствительность к регистру включается и отключается с помощью системного параметра sec_case_sensitive_logon. Если TRUE, то пароли будут поддерживать чувствительность к регистру (с использованием хеширования 11g/12c), если FALSE, то нет. Значение по умолчанию верно. В версиях с 12c по 19c этот параметр устарел, но все еще поддерживается. В многопользовательской архитектуре этот параметр можно задать только на корневом уровне контейнера, а не в подключаемых базах данных.

SQL> show parameters sec_case;

NAME                                 TYPE        VALUE                                                                                                
------------------------------------ ----------- ------------------------------                                                                       
sec_case_sensitive_logon             boolean     TRUE   
                                                                                              
SQL> alter user testuser identified by testpwd;

User altered.

SQL> select username,password,password_versions from dba_users where username = 'TESTUSER';

USERNAME   PASSWORD             PASSWORD_VERSIONS                                                                                                     
---------- -------------------- --------------------                                                                                                  
TESTUSER                        10G 11G                                                                                                               

SQL> select name,password,spare4 from sys.user$ where name = 'TESTUSER';


NAME       PASSWORD         SPARE4                                                                                                                
---------- ---------------- ------------------------------------------------------------                                       
TESTUSER   AEB6397C8E7598A  S:17F9149EFD0BDD9DBA305D6910D5928640F7727B29F261D851C58D37FA9A                                                        

SQL> connect testuser/testpwd;
Connected.

В следующем примере пароль учетной записи TESTUSER изменен с «testpwd» на «TestPwd». И мы используем предложение «идентифицировано по значениям», чтобы убедиться, что у нас есть только 10-гигабайтный хэш. Сначала мы проверяем состояние параметра, затем меняем пароль и проверяем логин пользователя. Затем убедитесь, что пароль смешанного регистра соблюдается на основе настройки V$PARAMETER, показанной выше.

SQL> alter user testuser identified by values 'AEB6397C8E7598A7';

User altered.

SQL> select username,password,password_versions from dba_users where username = 'TESTUSER';

USERNAME   PASSWORD             PASSWORD_VERSIONS                                                                                                     
---------- -------------------- --------------------                                                                                                  
TESTUSER                        10G                                                                                                                   

SQL> select name,password,spare4 from sys.user$ where name = 'TESTUSER';

NAME       PASSWORD             SPARE4                                                                                                                
---------- -------------------- ---------------------------------------------------------------                                                       
TESTUSER   AEB6397C8E7598A7                                                                                                                           

SQL> connect testuser/testpwd;
Connected.
SQL> connect testuser/TestPwd;
Connected.
SQL> connect dbauser/dbapassword;
Connected.
SQL> alter user testuser identified by values 'S:C7C0B0D97F60CA87C0CEB1663522C76509BD2FF84624774EAED94982A453';

User altered.

SQL> select username,password,password_versions from dba_users where username = 'TESTUSER';

USERNAME   PASSWORD             PASSWORD_VERSIONS                                                                                                     
---------- -------------------- --------------------                                                                                                  
TESTUSER                        11G                                                                                                                   

SQL> select name,password,spare4 from sys.user$ where name = 'TESTUSER';

NAME       PASSWORD             SPARE4                                                                                                                
---------- -------------------- ---------------------------------------------------------------                                                       
TESTUSER                        S:C7C0B0D97F60CA87C0CEB1663522C76509BD2FF84624774EAED94982A453                                                        

SQL> connect testuser/testpwd;
ERROR:
ORA-01017: invalid username/password; logon denied 


Warning: You are no longer connected to ORACLE.
SQL> connect testuser/TestPwd;
Connected.

В следующем примере пароль будет изменен с нижнего регистра на смешанный. Обратите внимание, что 10-граммовый хеш в поле PASSWORD USER$ не изменится; но хэш SPARE4 изменится. Таким образом, хотя 10-граммовый хэш сохраняется, его нельзя использовать, пока включена чувствительность к регистру и действуют хешированные пароли 11-го поколения. Однако, если хэш 10g является единственным доступным хэшем, то параметр sec_case_sensitive_logon для этого пользователя не применяется.

После подтверждения правильности соблюдения чувствительности к регистру хэш 11g удаляется, остается только хэш 10g. Чувствительность к регистру по-прежнему; но пользователь может войти в систему с разными случаями.

SQL> connect dbauser/dbapassword
Connected.
SQL> alter user testuser identified by testpwd;

User altered.

SQL> select password,spare4 from sys.user$ where name = 'TESTUSER';

PASSWORD                       SPARE4
------------------------------ --------------------------------------------------------------
AEB6397C8E7598A7               S:34391551DB0AADE86B6A1A7263B8F217C8B65FF66E4829BA7AA0B846653E

SQL> alter user testuser identified by TestPwd;

User altered.

SQL> select password,spare4 from sys.user$ where name = 'TESTUSER';

PASSWORD                       SPARE4
------------------------------ --------------------------------------------------------------
AEB6397C8E7598A7               S:777A9BE09BBAF3C7D3AD7D964471C15B948FB710870627D38B84EA9668EF

SQL> connect testuser/testpwd
ERROR:
ORA-01017: invalid username/password; logon denied

Warning: You are no longer connected to ORACLE.
SQL> connect testuser/TestPwd
Connected.
SQL> connect dbauser/dbapassword
Connected.
SQL> alter user testuser identified by values 'AEB6397C8E7598A7';

User altered.

SQL> select username,password,password_versions from dba_users where username = 'TESTUSER';

USERNAME                       PASSWORD                       PASSWORD
------------------------------ ------------------------------ --------
TESTUSER                                                      10G

SQL> select name,password,spare4 from sys.user$ where name = 'TESTUSER';

NAME               PASSWORD                       SPARE4
------------------ ------------------------------ -----------------------------------
TESTUSER           AEB6397C8E7598A7

SQL> show parameters sec_case;

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
sec_case_sensitive_logon             boolean     TRUE
SQL> connect testuser/testpwd
Connected.
SQL> connect testuser/TestPwd
Connected.

Интересная особенность безопасности возникает, если чувствительность к регистру отключена, когда нет хэша 10g. Если это произойдет, то пользователь будет заблокирован, потому что хеш 10G — единственный хэш, который используется (или может использоваться) для паролей, нечувствительных к регистру. В приведенном ниже примере у пользователя testuser все еще есть пароль «TestPwd», как указано выше, но он не сможет войти в систему, поскольку учет регистра отключен.

SQL> connect dbauser/dbapassword
Connected.
SQL> alter user testuser identified by values 'S:C7C0B0D97F60CA87C0CEB1663522C76509BD2FF84624774EAED94982A453';

User altered.

SQL> select username,password,password_versions from dba_users where username = 'TESTUSER';

USERNAME                       PASSWORD                       PASSWORD
------------------------------ ------------------------------ --------
TESTUSER                                                      11G

SQL> select name,password,spare4 from sys.user$ where name = 'TESTUSER';

NAME            PASSWORD               SPARE4
--------------- ---------------------- --------------------------------------------------------------
TESTUSER                               S:C7C0B0D97F60CA87C0CEB1663522C76509BD2FF84624774EAED94982A453

SQL> alter system set sec_case_sensitive_logon=FALSE;

System altered.

SQL> show parameters sec_case;

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
sec_case_sensitive_logon             boolean     FALSE
SQL> connect testuser/testpwd
ERROR:
ORA-01017: invalid username/password; logon denied


Warning: You are no longer connected to ORACLE.
SQL> connect testuser/TestPwd
ERROR:
ORA-01017: invalid username/password; logon denied

Начиная с версии 21c, параметр sec_case_sensitive_logon больше не поддерживается. По сути, это всегда ИСТИНА. Вышеупомянутые операторы отображения параметров ничего не вернут в базе данных 21c, а попытки установить значение вернут ошибку, что приведет к выводу, как показано ниже.

SQL> show parameters sec_case;
SQL>

SQL> alter system set sec_case_sensitive_logon=FALSE;
alter system set sec_case_sensitive_logon=FALSE
*
ERROR at line 1:
ORA-25138: SEC_CASE_SENSITIVE_LOGON initialization parameter has been made obsolete

VII. ПРАВИЛА ВЕРСИИ ПАРОЛЯ SQLNET.ORA

В версии 11gR1 файл SQLNET.ORA получил новый* параметр: SQLNET.ALLOWED_LOGON_VERSION. Хотя этот параметр напрямую не влиял на хеширование паролей; это может привести к несовместимости версий, в результате чего пользователь с действительным паролем не сможет войти в систему. То есть, если для параметра было установлено значение 10 или 11, то можно использовать только 10G хэши паролей без применения критического обновления к клиенту. С процессором можно было бы использовать хэши 10g или 11g. Если для параметра установлено значение 12, то будет работать только хэш 11g, так как поддерживается только протокол 11g.

12cR1 расширил эту функциональность, разделив параметр на два: SQLNET.ALLOWED_LOGON_VERSION_CLIENT и SQLNET.ALLOWED_LOGON_VERSION_SERVER. Параметр «клиент» определяет, какие протоколы поддерживаются при подключении в качестве клиента. Параметр «сервер» управляет тем, какие протоколы поддерживаются при приеме соединений в качестве сервера. В обоих случаях управление версиями повлияет на то, какие версии хеширования будут использоваться при аутентификации. 10 или ниже будет поддерживать любой из 3 алгоритмов хеширования паролей. 11 будет поддерживать только хэши 11g и 12c. Установка параметра на 12 может немного ввести в заблуждение, потому что это заставит клиентов 12c, но по-прежнему разрешать хэши 11g. Чтобы принудительно использовать хеширование 12c, параметр должен быть установлен на 12a. Как отмечалось ранее, начиная с версии 12cR1, параметр базы данных sec_case_sensitive_logon был объявлен устаревшим в пользу использования параметров SQLNET.ORA для управления поддерживаемыми алгоритмами хеширования.

Как отмечалось ранее, начиная с версии 12cR1, параметр базы данных sec_case_sensitive_logon был объявлен устаревшим в пользу использования параметров SQLNET.ORA для управления поддерживаемыми алгоритмами хеширования.

В версии 21c значение по умолчанию для SQLNET.ALLOWED_LOGON_VERSION_SERVER равно 12. Это соответствует прекращению поддержки sec_case_sensitive_logon и вынуждает всех пользователей использовать алгоритмы хеширования паролей с учетом регистра. Однако это только значение по умолчанию; если установить более низкий уровень, старые хэши 10g будут по-прежнему поддерживаться.

* В версии 10gR1 Oracle представила параметр SQLNET_ALLOWED_LOGON_VERSION, который позже был заменен параметром SQLNET.ALLOWED_LOGON_VERSIONS. Однако, поскольку был доступен только один алгоритм хеширования паролей, этот параметр не имел никакого отношения к использованию хеширования паролей.

VIII. СПЕЦИАЛЬНЫЕ ФОРМАТЫ

У некоторых пользователей вообще нет хэша, вместо этого они хранятся с фиксированными значениями.

ANONYMOUS  – SYS.USER$.PASSWORD содержит ровно 16 пробелов. Столбец SPARE4 имеет значение NULL. Поскольку значение не является результатом хеширования, невозможно напрямую войти в систему с пользователем, так как пароль не будет хешировать это значение. При чтении из представления DBA_USERS значение PASSWORD_VERSIONS равно NULL. Можно создать такого пользователя самостоятельно, используя предложение IDENTIFIED BY VALUES ровно из 16 пробелов; но это не особенно полезно делать это. Возможно, в базе данных до 18 ее можно было использовать для создания учетной записи только для схемы, поскольку ее нельзя было использовать для входа в систему.

XS$NULL — эта учетная запись не является реальным пользователем. Согласно документации Oracle, это «внутренняя учетная запись, которая представляет отсутствие пользователя в сеансе». Значение SYS.USER$.PASSWORD равно NULL. Значение SPARE4 представляет собой 11G заполнитель «S:», за которым следуют 60 символов пробела.

18c представил новый тип пользователя, называемый пользователем «Только схема». Это означает, что у него нет прямого входа в систему. Намерение состоит в том, что эти учетные записи будут владеть объектами. Их нельзя использовать для входа пользователей или приложений, что обеспечивает дополнительный уровень изоляции и безопасности. Значение SYS.USER$.PASSWORD равно NULL, значение SPARE4 содержит хэш-заполнители 11g и 12c, состоящие только из нулей и соли. Таким образом, за «S:» следуют 40 нулей и 20 шестнадцатеричных символов для обозначения соли. Затем «T:», за которым следуют 128 нулей и 32-символьная шестнадцатеричная соль.

Опция Multi-Tenant также вводит специальные форматы паролей. В пределах CDB$ROOT обычные пользователи будут следовать стандартным правилам хеширования и хранения. Однако при просмотре из подключаемых баз данных обычные пользователи будут иметь значение NULL для SYS.USER$.PASSWORD. Значение SPARE4 будет иметь заполнители «S:» и «T:», но значения будут полностью состоять из пробелов - 60 пробелов для значения 11g и 160 пробелов для значения 12c.

IX. НАСТРОЙКА

Как указано выше, эта статья не является практическим руководством по взлому учетных записей пользователей Oracle. На демонстрациях показано изменение пароля пользователя. Однако эти изменения предполагают, что существует пользователь базы данных с необходимыми привилегиями, уже предоставленными законными способами. Следующие команды, если они будут запущены SYS, создадут такого пользователя; а также соответствующий тестовый пользователь, использованный в приведенных выше примерах.

create user dbauser identified by dbapassword;
grant create session to dbauser;
grant select on dba_users to dbauser;
grant select on sys.user$ to dbauser;
grant select on sys.v_$parameter to dbauser;
grant alter user to dbauser;
grant alter system to dbauser;

create user testuser identified by testpwd;
grant create session to testuser;

IX. ПРЕДОСТЕРЕЖЕНИЯ

Хотя у администратора баз данных может быть возможность или даже полномочия на изменение пароля пользователя, это не означает, что он или она должен это делать, так как это может привести к нежелательным последствиям.

ИСТОЧНИКИ

  1. SQL Language Reference
  2. PL/SQL Packages and Types Reference
  3. Xiaoyun Wang, Yiqun Lisa Yin, Hongbo Yu, (2005, February 13) “Collision Search Attacks on SHA1
  4. Martin Rakhmanov, (2015, June 1) Changes in Oracle Database 12c password hashes
  5. RSA Laboratories, (2000, September) Password-Based Cryptography Specification

Об авторе

Шон Д. Стубер - администратор баз данных и разработчик с более чем 25-летним опытом работы, большая часть из которых работает с базами данных Oracle. В его основные обязанности входит кодирование, настройка и обучение других разработчиков тому, как наилучшим образом использовать свои базы данных Oracle. Он неоднократно выступал на конференции Collaborate для IOUG, а также на конференции KScope для ODTUG. Он является тематическим советником и регулярно отвечает на вопросы по Oracle и базам данных на сайте Experts-Exchange (www.experts-exchange.com). С Шоном можно связаться по адресу Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript. 

PDF-файл этой статьи можно загрузить из моего Dropbox здесь.