Skip to content
Homeβ€Ί Databaseβ€Ί ORA-01017: Invalid Username/Password – Common Causes & Solutions

ORA-01017: Invalid Username/Password – Common Causes & Solutions

Where developers are forged. Β· Structured learning Β· Free forever.
πŸ“ Part of: PL/SQL β†’ Topic 16 of 27
Authentication failures that bubble up as ORA-06512 in stored procedures and scripts.
πŸ§‘β€πŸ’» Beginner-friendly β€” no prior Database experience needed
In this tutorial, you'll learn
Authentication failures that bubble up as ORA-06512 in stored procedures and scripts.
  • ORA-01017 is deliberately vague β€” it does not distinguish between a wrong username, a wrong password, a locked account, an expired password, or a wrong authentication method, because confirming username existence would be an information disclosure vulnerability
  • Database links store a static copy of the password at creation time β€” password rotation must include dropping and recreating every affected link, because there is no ALTER DATABASE LINK for credentials
  • Case-sensitive passwords (SEC_CASE_SENSITIVE_LOGON = TRUE, the default since 11g) break connections when the client sends a different case than what was stored β€” ALTER USER without double quotes stores uppercase
✦ Plain-English analogy ✦ Real code with output ✦ Interview questions
⚑Quick Answer
  • ORA-01017 means the Oracle server rejected the credentials β€” the username does not exist, the password is wrong, or the authentication method is misconfigured
  • The error fires at the authentication layer before any SQL executes β€” no session is created, no queries run
  • Common causes: wrong password, case-sensitive password mismatch (11g+ default), expired password, profile-driven lockout, and stale database link credentials
  • Unlike ORA-28000 (account is locked) or ORA-28001 (password has expired), ORA-01017 is deliberately vague β€” it does not tell you WHICH credential component failed, because confirming whether a username exists would be an information disclosure vulnerability
  • Production insight: roughly 40% of ORA-01017 incidents in post-migration environments are caused by case-sensitivity mismatches or password file desynchronization β€” not genuinely wrong passwords
  • Biggest mistake: assuming the username is wrong when the real issue is a case-sensitive password that was stored as uppercase via ALTER USER without double quotes, or an external authentication fallback masking a password mismatch
Production IncidentPassword Rotation Broke All Database Links β€” 4 Hours of Silent ETL FailuresA quarterly password rotation updated the remote database credentials but left every database link definition pointing to the old password. Every link query raised ORA-01017 inside ORA-06512 stacks, and the ETL pipeline processed zero records for 4 hours.
SymptomAt 01:00 AM, the nightly ETL pipeline began failing. Every procedure that queried a remote database via a database link raised ORA-06512. The deepest error in every stack was ORA-01017: invalid username/password; logon denied. No client-side application connections were affected β€” only database link queries from inside PL/SQL procedures. The pipeline processed zero records for 4 hours before the on-call team identified the root cause.
AssumptionThe on-call team assumed the remote database account was locked or expired. They checked DBA_USERS.ACCOUNT_STATUS and DBA_USERS.EXPIRY_DATE on the remote database. The account was OPEN, not expired, and not locked. They also checked the audit trail for failed login attempts and found none from direct connections β€” only from the database link sessions. This led them down a 2-hour investigation into potential network authentication issues (Kerberos tickets, OS authentication fallback) that were irrelevant.
Root causeThe security team had executed their quarterly password rotation 30 minutes before the ETL window. The remote database password for the service account was changed with ALTER USER app_user IDENTIFIED BY "NewQ2Password". The new password was set correctly and worked for direct connections. However, the 11 database link definitions on the local ETL database still contained the old password in their CONNECT TO ... IDENTIFIED BY clause. Database links store a static copy of the password at creation time β€” they do not inherit changes from the remote database. Every link query attempted to authenticate with the old password and was rejected with ORA-01017.
FixImmediate fix: dropped and recreated all 11 database links with the new password. Each link was tested with SELECT 1 FROM dual@link_name before the ETL pipeline was restarted. Permanent changes: 1. Added database link credential updates as a mandatory step in the password rotation runbook. Before rotating a password, the DBA must query DBA_DB_LINKS across all databases to identify every link that uses the target account. 2. Added a post-rotation verification procedure that tests every database link with SELECT 1 FROM dual@link_name and blocks the rotation ticket from closing until all links pass. 3. Evaluated migrating critical links to Oracle Credential Store (Oracle Wallet) so that password updates are centralized rather than embedded in individual link definitions. 4. Scheduled a daily link connectivity check at 05:30 AM that tests every database link and alerts if any return ORA-01017.
Key Lesson
Password rotation must include database link credential updates β€” links store a static copy of the password that is never automatically updatedORA-01017 inside an ORA-06512 stack trace almost always means stale database link credentials β€” check the link's CONNECT TO clause before investigating the remote accountPost-rotation verification must test every database link, not just direct connections β€” the link uses a different credential store than the connection poolDatabase links have no ALTER command for passwords β€” they must be dropped and recreated, which means the rotation runbook must include the full DDLThere is no shortcut: if you rotate passwords without updating links, you will hit ORA-01017 every rotation cycle
Production Debug GuideFrom authentication failure to root cause
Direct connection fails with ORA-01017 (application or tool cannot connect)β†’Verify the username exists: SELECT username, account_status FROM dba_users WHERE username = UPPER('myuser'). If no rows: the username does not exist in this database. If account_status is not OPEN: the account is locked or expired (see account status section). If OPEN: the password is wrong β€” reset it with ALTER USER myuser IDENTIFIED BY "newpass" (double quotes to preserve case).
ORA-01017 appears inside an ORA-06512 stack trace from a PL/SQL procedure→A database link's embedded credentials are wrong. Query the link definition: SELECT db_link, username, host FROM user_db_links. Verify the remote account is OPEN on the remote database. If the remote password was recently rotated, the link still has the old password — drop and recreate the link with the new password.
ORA-01017 appeared after an Oracle upgrade to 11g or later→Oracle 11g introduced case-sensitive passwords by default (SEC_CASE_SENSITIVE_LOGON = TRUE). The password hash includes case information. If the password was set without double quotes (ALTER USER x IDENTIFIED BY pass), it was stored as uppercase. If the client sends mixed case or lowercase, the hash comparison fails. Check: SELECT value FROM v$parameter WHERE name = 'sec_case_sensitive_logon'. If TRUE, ensure the client sends the exact case that was used when the password was set.
ORA-01017 with the correct password — works in SQL*Plus but fails in the application→The application may be using a different authentication method. Check sqlnet.ora for SQLNET.AUTHENTICATION_SERVICES. If set to (NTS) on Windows, the application may be attempting OS authentication (which uses the Windows domain account) instead of password authentication. If set to (KERBEROS5), Kerberos ticket issues could cause fallback failures. Set SQLNET.AUTHENTICATION_SERVICES = (NONE) to force password-only authentication and retest.
ORA-01017 for SYSDBA connections but normal connections work fine→SYSDBA connections authenticate against the password file (orapwSID), not the data dictionary. ALTER USER changes the data dictionary hash but does not update the password file. Regenerate the password file: orapwd file=$ORACLE_HOME/dbs/orapwSID password=newpass entries=10 force=y. In RAC environments, copy the regenerated file to all nodes or store it in ASM.
ORA-01017 intermittently — the same credentials sometimes work and sometimes fail→Check whether the account is approaching the FAILED_LOGIN_ATTEMPTS threshold and being temporarily locked (LOCKED(TIMED) status). Also check if the connection pool is cycling through multiple database instances in a RAC cluster where one node has a stale password file. Query DBA_USERS for account_status and lock_date on every RAC node.

ORA-01017: invalid username/password; logon denied is Oracle's authentication rejection error. It fires when the server cannot validate the supplied credentials against its user store β€” whether that store is the data dictionary, a password file, an LDAP directory, or an OS account mapping. The error is a hard stop: no session is created, no queries execute, and at the direct connection level, no ORA-06512 stack trace is generated because no PL/SQL was ever invoked.

However, ORA-01017 frequently appears inside ORA-06512 stack traces in production systems when stored procedures use database links with embedded credentials. A procedure that queries a remote database via CREATE DATABASE LINK ... CONNECT TO user IDENTIFIED BY pass will raise ORA-01017 at the remote authentication layer if the embedded credentials are wrong β€” typically because the remote password was rotated but the link definition still holds the old password. The error propagates through the PL/SQL call stack as ORA-06512 entries, and engineers see the stack trace without immediately recognizing that the root cause is an authentication failure on a database link, not a code defect.

The diagnostic path diverges depending on context. Direct connection failures (an application or tool cannot authenticate) require checking the username, password, password case sensitivity, account status, profile policies, and authentication method. Database link failures (a link query raises ORA-01017 inside a procedure) require checking the link's CONNECT TO clause, the remote database's account status, and whether the remote password was recently rotated. Confusing the two β€” or checking the wrong credential store β€” is the most common reason this error wastes hours.

Oracle Authentication: How ORA-01017 Is Raised

Oracle authentication happens at the session establishment layer, before any SQL statement executes. The client initiates a connection through the TNS listener, which redirects the client to a dedicated server process (or a shared server dispatcher). The server process challenges the client for credentials. The client sends the username and a cryptographic hash of the password. The server computes the same hash from its stored password verifier and compares. If the hashes match, a session is created and the client can begin executing SQL. If they do not match, ORA-01017 is raised and the connection is rejected.

Oracle maintains password verifiers (hashes) in the SYS.USER$ table within the data dictionary. The hashing algorithm depends on the Oracle version and the SQLNET.ALLOWED_LOGON_VERSION_SERVER and SQLNET.ALLOWED_LOGON_VERSION_CLIENT parameters: Oracle 10g used a DES-based hash, Oracle 11g added a SHA-1-based hash with case sensitivity, and Oracle 12c and later use SHA-2/SHA-512 with salt. The client and server negotiate which verifier to use during the authentication handshake β€” if the client is older and only supports the 10g verifier but the server has removed 10g verifiers (12c+ default behavior), the authentication fails with ORA-01017.

The password file (orapwSID) is a separate credential store used exclusively for SYSDBA and SYSOPER connections when REMOTE_LOGIN_PASSWORDFILE is set to EXCLUSIVE or SHARED. Normal connections never consult the password file β€” they authenticate against the data dictionary. This dual-store model is the root cause of a common ORA-01017 variant: ALTER USER changes the data dictionary hash but does not update the password file, so SYSDBA connections fail while normal connections succeed.

The error message is deliberately identical for all failure modes: wrong username, wrong password, expired password, locked account (in some configurations), and authentication method mismatch. Oracle does not want to confirm whether a username exists, because that information would allow an attacker to enumerate valid accounts. The diagnostic burden falls on the DBA to check each possible cause independently.

io/thecodeforge/debug/ora01017_diagnostic.sql Β· SQL
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
-- Step 1: Verify the username exists and check account status
SELECT
  username,
  account_status,
  lock_date,
  expiry_date,
  profile,
  authentication_type
FROM dba_users
WHERE username = UPPER('app_user');
-- No rows: username does not exist in this database
-- account_status != 'OPEN': account is locked, expired, or both
-- authentication_type: PASSWORD, EXTERNAL, GLOBAL

-- Step 2: Check the profile's password policy
SELECT
  profile,
  resource_name,
  limit
FROM dba_profiles
WHERE profile = (
  SELECT profile FROM dba_users WHERE username = UPPER('app_user')
)
AND resource_type = 'PASSWORD'
ORDER BY resource_name;
-- Key parameters:
-- FAILED_LOGIN_ATTEMPTS: max consecutive failures before lockout
-- PASSWORD_LIFE_TIME: days before password expires
-- PASSWORD_LOCK_TIME: days the account stays locked after max failures
-- PASSWORD_GRACE_TIME: days after expiry before account is fully locked
-- PASSWORD_REUSE_TIME / PASSWORD_REUSE_MAX: reuse restrictions

-- Step 3: Check case-sensitive password setting
SELECT name, value
FROM v$parameter
WHERE name = 'sec_case_sensitive_logon';
-- TRUE  β€” passwords are case-sensitive (11g+ default)
-- FALSE β€” passwords are case-insensitive (pre-11g behavior)

-- Step 4: Check which password verifiers exist for the user
SELECT username, password_versions
FROM dba_users
WHERE username = UPPER('app_user');
-- 10G       β€” DES-based hash only (case-insensitive)
-- 11G       β€” SHA-1 hash (case-sensitive)
-- 12C       β€” SHA-2/SHA-512 hash (case-sensitive, salted)
-- 10G 11G   β€” both verifiers present
-- 11G 12C   β€” both verifiers present
-- If only 12C exists and the client is old, authentication may fail

-- Step 5: Check password file users
SELECT username, sysdba, sysoper, sysasm
FROM v$pwfile_users;
-- Lists users with privileged connection rights via the password file
-- If the user is listed: SYSDBA auth uses the password file
-- If not listed: SYSDBA auth falls back to OS auth or fails

-- Step 6: Check REMOTE_LOGIN_PASSWORDFILE
SELECT name, value
FROM v$parameter
WHERE name = 'remote_login_passwordfile';
-- NONE      β€” password file is not used; SYSDBA via OS auth only
-- EXCLUSIVE β€” one password file per instance (most common)
-- SHARED    β€” password file shared across instances (RAC with ASM)

-- Step 7: Check allowed logon versions (12c+)
SELECT name, value
FROM v$parameter
WHERE name LIKE '%allowed_logon_version%';
-- SQLNET.ALLOWED_LOGON_VERSION_SERVER: minimum client version accepted
-- SQLNET.ALLOWED_LOGON_VERSION_CLIENT: minimum server version accepted
-- If set to 12 or 12a, clients using 10g or 11g verifiers are rejected

-- Step 8: Check recent failed login attempts in the audit trail
SELECT
  os_username,
  username,
  userhost,
  terminal,
  returncode,
  timestamp#
FROM sys.aud$
WHERE username = UPPER('app_user')
  AND returncode IN (1017, 28000, 28001)
ORDER BY timestamp# DESC
FETCH FIRST 20 ROWS ONLY;
-- returncode 1017 = ORA-01017 (invalid credentials)
-- returncode 28000 = ORA-28000 (account locked)
-- returncode 28001 = ORA-28001 (password expired)
-- Shows which hosts and timestamps generated failures
Mental Model
Oracle Authentication Layers
Oracle has three independent authentication layers β€” each can raise ORA-01017 independently, and they do not share credential stores.
  • Data dictionary (SYS.USER$): standard password authentication β€” used for normal connections (sqlplus user/pass@db)
  • Password file (orapwSID): SYSDBA and SYSOPER authentication β€” used for privileged connections (sqlplus / as sysdba remotely)
  • External authentication: OS accounts (ops$), Kerberos, LDAP, RADIUS β€” bypasses password entirely but can fall back to password auth on failure
  • ALTER USER changes the data dictionary hash but does NOT update the password file β€” SYSDBA can fail while normal connections succeed
  • SEC_CASE_SENSITIVE_LOGON controls whether password comparison includes case (11g+ default: TRUE)
  • SQLNET.ALLOWED_LOGON_VERSION controls which hash algorithms are accepted β€” older clients may be rejected after upgrades
πŸ“Š Production Insight
ORA-01017 is deliberately vague β€” it will not confirm whether the username exists, whether the password is wrong, or whether the account is in a non-OPEN state.
This is a security feature, not a bug β€” but it makes debugging harder.
Rule: always check the username, account status, password case, authentication method, and password verifier versions β€” Oracle will not tell you which one is the problem.
🎯 Key Takeaway
Oracle authenticates at the session layer before any SQL executes β€” three independent credential stores exist: data dictionary, password file, and external authentication.
ALTER USER updates the data dictionary but not the password file β€” SYSDBA connections can break while normal connections work perfectly.
Bottom line: if normal connections work but SYSDBA fails with ORA-01017, the password file is out of sync β€” regenerate it with orapwd.

Case-Sensitive Passwords: The 11g+ Gotcha

Oracle 11g introduced case-sensitive passwords by default. The parameter SEC_CASE_SENSITIVE_LOGON controls this behavior. When set to TRUE (the default since 11g), the password verifier stored in the data dictionary includes case information: 'MyPassword' and 'mypassword' produce different hashes and are treated as different passwords.

This causes ORA-01017 in two common scenarios. First, applications or scripts that convert the password to uppercase before sending it to Oracle β€” a practice that was harmless in Oracle 10g β€” will fail if the actual password contains lowercase characters. Second, migrations from Oracle 10g to 11g or later can break all connections if the passwords were originally set without case awareness.

The mechanism is straightforward: the password hash stored in SYS.USER$ is computed from the exact character sequence including case. The client computes the same hash from the password the user supplies. If the user supplies 'Password123' but the stored hash was computed from 'PASSWORD123' (because ALTER USER was run without double quotes, which causes Oracle to uppercase the value), the hashes do not match and ORA-01017 is raised.

The subtlety that catches most engineers: ALTER USER app_user IDENTIFIED BY MyPassword (without double quotes) stores the password as MYPASSWORD in uppercase. This is because Oracle treats the unquoted identifier as a regular SQL identifier and uppercases it. ALTER USER app_user IDENTIFIED BY "MyPassword" (with double quotes) stores the password as MyPassword with the exact case preserved. The quoting behavior during password creation determines the case of the stored verifier β€” and the user must supply the same case when connecting.

Oracle 12c and later further complicated this by introducing the SQLNET.ALLOWED_LOGON_VERSION_SERVER parameter, which controls which password verifiers are accepted. If set to 12 or 12a, only SHA-2 verifiers are accepted, and clients that only support the older 10g DES-based verifier are rejected with ORA-01017. After an upgrade, if the user's password was never reset (so only the old verifier exists), authentication fails even though the password itself is correct.

io/thecodeforge/debug/case_sensitive_passwords.sql Β· SQL
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950
-- Diagnostic 1: Check current case sensitivity setting
SELECT name, value
FROM v$parameter
WHERE name = 'sec_case_sensitive_logon';
-- TRUE  β€” case-sensitive (11g+ default)
-- FALSE β€” case-insensitive (pre-11g behavior)

-- Diagnostic 2: Check which password verifiers the user has
SELECT username, password_versions
FROM dba_users
WHERE username = UPPER('app_user');
-- 10G       β€” only the old DES hash exists (case-insensitive)
-- 11G       β€” SHA-1 hash with case sensitivity
-- 12C       β€” SHA-2/SHA-512 hash with case sensitivity and salt
-- 10G 11G   β€” both old and new verifiers present
-- 11G 12C   β€” both verifiers present
-- After upgrade, reset the password to generate the new verifier:
-- ALTER USER app_user IDENTIFIED BY "SamePassword";

-- Diagnostic 3: Check allowed logon versions
SELECT name, value
FROM v$parameter
WHERE name LIKE '%allowed_logon_version%';
-- If ALLOWED_LOGON_VERSION_SERVER = 12 and user only has 10G verifier:
-- ORA-01017 even with the correct password
-- Fix: reset the password to generate the 12C verifier

-- Diagnostic 4: Set password with exact case preserved
ALTER USER app_user IDENTIFIED BY "MyExactPassword123";
-- Double quotes: stores 'MyExactPassword123' with exact case
-- User must login with: sqlplus app_user/MyExactPassword123@DB

-- Diagnostic 5: Set password WITHOUT case preservation (uppercase)
ALTER USER app_user IDENTIFIED BY MyPassword;
-- No quotes: Oracle uppercases to 'MYPASSWORD'
-- User must login with: sqlplus app_user/MYPASSWORD@DB
-- Or any case if SEC_CASE_SENSITIVE_LOGON = FALSE

-- Diagnostic 6: Temporarily disable case sensitivity (emergency only)
ALTER SYSTEM SET sec_case_sensitive_logon = FALSE;
-- Both 'Password' and 'PASSWORD' will now work
-- Weakens security β€” use only as a temporary workaround during migration
-- Re-enable after fixing all client connection strings:
-- ALTER SYSTEM SET sec_case_sensitive_logon = TRUE;

-- Diagnostic 7: Verify the password works by testing the connection
-- $ sqlplus app_user/"MyExactPassword123"@DB
-- Connected.  β€” password and case are correct
-- ORA-01017   β€” password or case is wrong
-- Note: quote the password in the shell if it contains special characters
⚠ Case-Sensitive Password Pitfalls
πŸ“Š Production Insight
Case-sensitive passwords are the root cause of roughly 30% of ORA-01017 incidents during and after database migrations.
The most common scenario: the password was set with ALTER USER without double quotes (stored as uppercase), but the application sends mixed case.
Rule: always use double quotes when setting passwords to make the stored case explicit β€” and verify the client sends the exact same case.
🎯 Key Takeaway
Oracle 11g and later use case-sensitive passwords by default β€” 'Password' and 'PASSWORD' produce different hashes and are treated as completely different credentials.
ALTER USER without double quotes stores uppercase; ALTER USER with double quotes stores exact case.
Bottom line: if ORA-01017 appears after a migration or upgrade, check SEC_CASE_SENSITIVE_LOGON, verify the client sends the correct case, and ensure the user's password verifier version is compatible with ALLOWED_LOGON_VERSION_SERVER.

Account Lockout and Password Expiry

Oracle profiles enforce password policies that control login behavior: maximum failed login attempts before lockout, password lifetime before expiry, lockout duration, grace period after expiry, and password reuse restrictions. When any policy is violated, the account status changes from OPEN to a non-OPEN state, and all connection attempts receive ORA-01017 (or in some cases ORA-28000 for locked accounts, depending on the Oracle version and configuration).

The profile parameter FAILED_LOGIN_ATTEMPTS controls automatic lockout after consecutive failed login attempts. The DEFAULT profile ships with FAILED_LOGIN_ATTEMPTS set to 10 in most Oracle versions. After 10 consecutive wrong-password attempts, the account status becomes LOCKED(TIMED) for the duration specified by PASSWORD_LOCK_TIME (default: 1 day). During the lock period, even correct credentials are rejected.

The profile parameter PASSWORD_LIFE_TIME controls password expiry. After the specified number of days since the last password change, the password expires. The account status transitions to EXPIRED(GRACE) for PASSWORD_GRACE_TIME days, during which the user can still connect but Oracle prompts for a password change. After the grace period, the status becomes EXPIRED and all connections are rejected until the DBA resets the password.

The most dangerous production scenario is a shared service account with a low FAILED_LOGIN_ATTEMPTS value. If one misconfigured application instance sends the wrong password 10 times β€” perhaps because a configuration file has a stale password β€” the account locks for all consumers. Every other application, ETL job, and reporting query that uses the same account is blocked until the lockout expires or the DBA manually unlocks it. This cascading lockout is common in microservices environments and connection pool configurations where multiple processes share a single database account.

The fix has two parts: immediate (unlock the account) and permanent (prevent recurrence). For service accounts, create a dedicated profile with FAILED_LOGIN_ATTEMPTS set to UNLIMITED and PASSWORD_LIFE_TIME set to UNLIMITED. For human user accounts, keep the limits but ensure the lockout duration (PASSWORD_LOCK_TIME) is reasonable β€” 1/1440 (one minute) is often more appropriate than the default 1 day for environments with automated retry logic.

io/thecodeforge/debug/account_lockout.sql Β· SQL
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
-- Diagnostic 1: Check account status and lock/expiry dates
SELECT
  username,
  account_status,
  lock_date,
  expiry_date,
  profile,
  created,
  last_login
FROM dba_users
WHERE username = UPPER('app_user');
-- account_status values:
-- OPEN               β€” active, connections allowed
-- LOCKED(TIMED)      β€” auto-locked after FAILED_LOGIN_ATTEMPTS failures
-- LOCKED             β€” manually locked by DBA (ALTER USER ACCOUNT LOCK)
-- EXPIRED            β€” PASSWORD_LIFE_TIME exceeded, grace period ended
-- EXPIRED(GRACE)     β€” password expired, within grace period
-- EXPIRED & LOCKED   β€” both conditions simultaneously

-- Diagnostic 2: Check the profile's password policy limits
SELECT
  resource_name,
  limit
FROM dba_profiles
WHERE profile = (
  SELECT profile FROM dba_users WHERE username = UPPER('app_user')
)
AND resource_type = 'PASSWORD'
ORDER BY resource_name;
-- FAILED_LOGIN_ATTEMPTS: consecutive failures before lockout (default: 10)
-- PASSWORD_LIFE_TIME: days before expiry (default: 180 in some versions)
-- PASSWORD_LOCK_TIME: days locked after max failures (default: 1)
-- PASSWORD_GRACE_TIME: days after expiry to change password (default: 7)
-- PASSWORD_REUSE_TIME: days before a password can be reused
-- PASSWORD_REUSE_MAX: number of changes before a password can be reused

-- Fix 1: Unlock a locked account
ALTER USER app_user ACCOUNT UNLOCK;
-- Takes effect immediately β€” no restart required

-- Fix 2: Reset an expired password
ALTER USER app_user IDENTIFIED BY "NewSecurePassword";
-- Changes the password AND clears the EXPIRED status
-- Use double quotes to preserve exact case

-- Fix 3: Unlock AND reset simultaneously
ALTER USER app_user IDENTIFIED BY "NewSecurePassword" ACCOUNT UNLOCK;

-- Prevention 1: Create a profile for service accounts
CREATE PROFILE svc_account_profile LIMIT
  FAILED_LOGIN_ATTEMPTS UNLIMITED
  PASSWORD_LIFE_TIME    UNLIMITED
  PASSWORD_LOCK_TIME    UNLIMITED
  PASSWORD_GRACE_TIME   UNLIMITED
  PASSWORD_REUSE_TIME   UNLIMITED
  PASSWORD_REUSE_MAX    UNLIMITED;
-- No lockout, no expiry β€” appropriate for service accounts only
-- Human user accounts should retain policy limits

-- Prevention 2: Assign the profile to the service account
ALTER USER app_user PROFILE svc_account_profile;

-- Prevention 3: For human users, use a reasonable lock time instead of UNLIMITED
CREATE PROFILE human_user_profile LIMIT
  FAILED_LOGIN_ATTEMPTS 10
  PASSWORD_LOCK_TIME    1/1440  -- 1 minute (1/1440 of a day)
  PASSWORD_LIFE_TIME    90
  PASSWORD_GRACE_TIME   7;

-- Diagnostic 3: Check the audit trail for failed login patterns
SELECT
  os_username,
  username,
  userhost,
  terminal,
  returncode,
  TO_CHAR(timestamp#, 'YYYY-MM-DD HH24:MI:SS') AS failure_time
FROM sys.aud$
WHERE username = UPPER('app_user')
  AND returncode IN (1017, 28000, 28001)
ORDER BY timestamp# DESC
FETCH FIRST 30 ROWS ONLY;
-- returncode 1017 = ORA-01017 (wrong credentials)
-- returncode 28000 = ORA-28000 (account locked)
-- returncode 28001 = ORA-28001 (password expired)
-- userhost shows which machine is sending the wrong password
Mental Model
Account Status States and Transitions
Oracle has five account states β€” each raises ORA-01017 (or ORA-28000) but the fix is different for each.
  • OPEN: normal state β€” connections allowed with correct credentials
  • LOCKED(TIMED): auto-locked after FAILED_LOGIN_ATTEMPTS consecutive failures β€” auto-unlocks after PASSWORD_LOCK_TIME, or unlock manually with ALTER USER ACCOUNT UNLOCK
  • LOCKED: manually locked by DBA β€” only unlocked manually with ALTER USER ACCOUNT UNLOCK
  • EXPIRED(GRACE): password exceeded PASSWORD_LIFE_TIME β€” user can still connect but must change password within PASSWORD_GRACE_TIME days
  • EXPIRED: grace period ended β€” all connections rejected until DBA resets with ALTER USER IDENTIFIED BY
  • EXPIRED & LOCKED: both conditions β€” must both unlock AND reset the password
πŸ“Š Production Insight
Service accounts with FAILED_LOGIN_ATTEMPTS = 10 can be locked by a single misconfigured application instance sending the wrong password 10 times.
Every other application sharing the same account is immediately blocked β€” the lockout cascades across the entire infrastructure.
Rule: set FAILED_LOGIN_ATTEMPTS to UNLIMITED for service accounts and use separate database accounts per application where possible.
🎯 Key Takeaway
Account lockout and password expiry both manifest as ORA-01017 β€” the error message does not distinguish between them.
Check DBA_USERS.ACCOUNT_STATUS to determine the actual state: LOCKED(TIMED) vs. EXPIRED vs. EXPIRED & LOCKED.
Bottom line: service accounts must have FAILED_LOGIN_ATTEMPTS = UNLIMITED β€” one misconfigured consumer should not lock out every other consumer sharing the account.

Database links store credentials statically in the CONNECT TO ... IDENTIFIED BY clause at creation time. The password embedded in the link definition is a frozen snapshot β€” it is never automatically updated when the remote database password changes. When the remote password is rotated, the link still authenticates with the old password, and every query through the link raises ORA-01017.

This is the most common cause of ORA-01017 in production environments with password rotation policies. The pattern repeats every rotation cycle: the security team rotates the remote password, the database links still hold the old password, the nightly ETL or reporting pipeline fails with ORA-01017 inside ORA-06512 stacks, and the team spends hours checking the remote account status (which is OPEN and healthy) before realizing the link credentials are stale.

There is no ALTER DATABASE LINK command to update the password. The link must be dropped and recreated with the new password. This means the password rotation process must include an explicit step to enumerate all database links that reference the rotated account, drop each link, and recreate it with the new password. Failing to include this step guarantees an ORA-01017 incident on the next execution of any procedure that uses the link.

For environments that rotate passwords frequently, Oracle Wallet (External Password Store) can centralize credentials outside the link definition. The link is created with CONNECT TO user IDENTIFIED BY VALUES ':wallet_entry' (or simply without an IDENTIFIED BY clause if the wallet is configured for the connect string). Password updates are made to the wallet, and the link inherits the new password without DDL changes. However, wallet configuration adds operational complexity and must be managed carefully in RAC and Data Guard environments.

The immediate diagnostic for database link ORA-01017 is to query USER_DB_LINKS or DBA_DB_LINKS, identify the link's username and host (TNS alias), and test the credentials directly against the remote database. If direct authentication with the same username and password succeeds, the link definition has a different (older) password.

io/thecodeforge/debug/database_link_ora01017.sql Β· SQL
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
-- Diagnostic 1: List all database links and their embedded credentials
SELECT
  db_link,
  username AS remote_user,
  host     AS tns_alias,
  created
FROM user_db_links
ORDER BY db_link;
-- USERNAME shows the remote account the link authenticates as
-- If this account's password was rotated, the link will fail

-- Diagnostic 2: List all links across every schema (run as DBA)
SELECT
  owner,
  db_link,
  username AS remote_user,
  host     AS tns_alias,
  created
FROM dba_db_links
ORDER BY owner, db_link;
-- Shows ALL links in ALL schemas that might reference the rotated account

-- Diagnostic 3: Find all links that authenticate as a specific user
SELECT
  owner,
  db_link,
  host AS tns_alias
FROM dba_db_links
WHERE username = UPPER('app_user')
ORDER BY owner, db_link;
-- These are the links that will break when app_user's password is rotated

-- Diagnostic 4: Test a specific database link
SELECT 1 FROM dual@remote_prod;
-- Returns 1: link credentials are valid
-- ORA-01017: link credentials are stale (wrong password)
-- ORA-12154: link's TNS alias cannot be resolved (different problem)
-- ORA-28000: remote account is locked (different problem)

-- Diagnostic 5: Test ALL links in the current schema
SET SERVEROUTPUT ON SIZE UNLIMITED
BEGIN
  FOR rec IN (
    SELECT db_link, username FROM user_db_links ORDER BY db_link
  ) LOOP
    BEGIN
      EXECUTE IMMEDIATE 'SELECT 1 FROM dual@' || rec.db_link;
      DBMS_OUTPUT.PUT_LINE('OK:      ' || rec.db_link || ' (user: ' || rec.username || ')');
    EXCEPTION
      WHEN OTHERS THEN
        DBMS_OUTPUT.PUT_LINE('FAIL:    ' || rec.db_link || ' (user: ' || rec.username || ') β€” ' || SQLERRM);
    END;
  END LOOP;
END;
/

-- Fix: Drop and recreate the database link with the new password
DROP DATABASE LINK remote_prod;

CREATE DATABASE LINK remote_prod
  CONNECT TO app_user IDENTIFIED BY "NewRotatedPassword"
  USING 'REMOTE_PROD';
-- Use double quotes around the password to preserve exact case
-- Replace REMOTE_PROD with the TNS alias or inline descriptor

-- Verify the recreated link works
SELECT 1 FROM dual@remote_prod;

-- Alternative: Inline TNS descriptor (no tnsnames.ora dependency)
CREATE DATABASE LINK remote_prod
  CONNECT TO app_user IDENTIFIED BY "NewRotatedPassword"
  USING '(DESCRIPTION=
    (ADDRESS=(PROTOCOL=TCP)(HOST=remote.example.com)(PORT=1521))
    (CONNECT_DATA=(SERVICE_NAME=REMOTE_PROD))
  )';
⚠ Database Link Credential Lifecycle
πŸ“Š Production Insight
Database links store a frozen copy of the password β€” password rotation breaks them every single time unless the rotation process explicitly updates the links.
There is no ALTER DATABASE LINK for passwords β€” links must be dropped and recreated, which means the rotation runbook must include the full DDL.
Rule: if your password rotation process does not include database link credential updates as a mandatory step, you will hit ORA-01017 on every rotation cycle.
🎯 Key Takeaway
Database link ORA-01017 is caused by stale embedded credentials β€” the remote password was rotated but the link definition still holds the old password.
The link must be dropped and recreated β€” there is no ALTER DATABASE LINK for credential updates.
Bottom line: password rotation without link updates is an incomplete rotation β€” add link enumeration, recreation, and testing to every rotation runbook.

Password File and SYSDBA Authentication

SYSDBA and SYSOPER connections use the password file (orapwSID) as their credential store, not the data dictionary. When REMOTE_LOGIN_PASSWORDFILE is set to EXCLUSIVE or SHARED, the password file is consulted for privileged connections. If the password file contains a different hash than the one computed from the supplied password, ORA-01017 is raised β€” even though the data dictionary password (used for normal connections) is correct.

The password file is a binary file located at $ORACLE_HOME/dbs/orapwSID on Linux or %ORACLE_HOME%\database\PWDsid.ora on Windows. It stores the username and password hash for users who have been granted SYSDBA or SYSOPER privileges. The critical detail is that ALTER USER does not automatically update the password file. When a DBA runs ALTER USER sys IDENTIFIED BY newpass, the data dictionary hash in SYS.USER$ is updated, but the password file retains the old hash. SYSDBA connections using the password file still authenticate against the old hash and fail.

The fix is to regenerate the password file using the orapwd utility after any password change for a SYSDBA-privileged user. In Oracle 12.2 and later, ALTER USER for the SYS user does update the password file automatically when REMOTE_LOGIN_PASSWORDFILE = EXCLUSIVE and the password file is in the default location. However, this automatic update does not work for non-SYS users with SYSDBA grants, for SHARED password files, or when the password file is stored in a non-default location.

In RAC environments, the password file must be identical on every node. After regenerating the file on one node, it must be copied to all other nodes β€” or the password file must be stored in ASM (Oracle 12c+), which provides automatic sharing across all nodes. A common production failure is: the DBA regenerates the password file on node 1, SYSDBA works on node 1, but SYSDBA fails on nodes 2 and 3 because they still have the old file.

In Data Guard environments, the password file must be synchronized between the primary and standby databases. If the primary's password file is updated but the standby's is not, a switchover or failover to the standby will result in ORA-01017 for all SYSDBA connections.

io/thecodeforge/debug/password_file.sql Β· SQL
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
-- Diagnostic 1: Check REMOTE_LOGIN_PASSWORDFILE setting
SELECT name, value
FROM v$parameter
WHERE name = 'remote_login_passwordfile';
-- NONE      β€” password file not used; SYSDBA only via OS auth
-- EXCLUSIVE β€” one password file per instance (most common)
-- SHARED    β€” one password file shared across instances

-- Diagnostic 2: List all users in the password file
SELECT username, sysdba, sysoper, sysasm
FROM v$pwfile_users;
-- Shows which users have privileged access via the password file
-- If a user is listed here, their SYSDBA auth uses this file
-- If not listed, SYSDBA attempts will use OS auth or fail

-- Diagnostic 3: Find the password file location
-- Linux:
-- $ ls -la $ORACLE_HOME/dbs/orapw*
-- -rw-r----- 1 oracle oinstall 3584 Apr 14 01:00 orapwPROD
--
-- Windows:
-- > dir %ORACLE_HOME%\database\PWD*
-- PWDprod.ora    3584  04/14/2026  01:00 AM
--
-- ASM (12c+):
-- SQL> SELECT name FROM v$passwordfile_info;
-- +DATA/PROD/PASSWORD/pwdprod.256.1234567890

-- Diagnostic 4: Regenerate the password file (Linux)
-- $ orapwd file=$ORACLE_HOME/dbs/orapwPROD \
--         password=new_sys_password \
--         entries=30 \
--         force=y \
--         format=12.2
-- file: path to the password file
-- password: the SYS password
-- entries: maximum number of SYSDBA/SYSOPER users
-- force=y: overwrite the existing file
-- format=12.2: use the 12c format (supports longer passwords)

-- Diagnostic 5: Regenerate the password file (Windows)
-- > orapwd file=%ORACLE_HOME%\database\PWDprod.ora \
--          password=new_sys_password \
--          entries=30 \
--          force=y

-- Diagnostic 6: Verify SYSDBA connection after regeneration
-- $ sqlplus sys/new_sys_password@PROD as sysdba
-- Connected.
-- If still ORA-01017: check the file path matches what Oracle expects

-- Diagnostic 7: Copy password file to all RAC nodes
-- $ scp $ORACLE_HOME/dbs/orapwPROD node2:$ORACLE_HOME/dbs/orapwPROD
-- $ scp $ORACLE_HOME/dbs/orapwPROD node3:$ORACLE_HOME/dbs/orapwPROD
-- Verify on each node:
-- $ sqlplus sys/new_sys_password@PROD_NODE2 as sysdba

-- Diagnostic 8: Store password file in ASM (12c+ β€” eliminates RAC sync)
-- $ srvctl modify database -db PROD -pwfile +DATA/PROD/PASSWORD/pwdprod
-- All RAC nodes automatically share the same file

-- Diagnostic 9: Copy password file to Data Guard standby
-- $ scp $ORACLE_HOME/dbs/orapwPROD standby_host:$ORACLE_HOME/dbs/orapwSTBY
-- Required for switchover/failover to work with SYSDBA auth
Mental Model
Password File vs. Data Dictionary Authentication
Oracle uses two different credential stores β€” which one is consulted depends entirely on the connection type.
  • Normal connections (sqlplus user/pass@db): authenticate against the data dictionary (SYS.USER$) β€” the password file is not consulted
  • SYSDBA/SYSOPER connections (sqlplus sys/pass@db as sysdba): authenticate against the password file (orapwSID) β€” the data dictionary may not be consulted
  • ALTER USER changes the data dictionary hash β€” it does NOT automatically update the password file in most configurations
  • After changing a SYSDBA user's password, regenerate the password file with orapwd and distribute to all RAC/Data Guard nodes
  • In RAC, the password file must be identical on every node β€” or store it in ASM for automatic synchronization
  • In Data Guard, the standby's password file must match the primary's β€” or switchover/failover breaks SYSDBA auth
πŸ“Š Production Insight
A password file out of sync with the data dictionary causes ORA-01017 for SYSDBA connections only β€” normal connections are completely unaffected.
This is the most confusing ORA-01017 variant because the DBA tests a normal connection, it works, and they conclude the password is correct.
Rule: after changing any SYSDBA-privileged user's password, always regenerate the password file with orapwd and distribute it to every RAC node and Data Guard standby.
🎯 Key Takeaway
SYSDBA authentication uses the password file, not the data dictionary β€” these are separate credential stores that can become desynchronized.
ALTER USER updates the dictionary but typically not the password file β€” regenerate it with orapwd after every SYSDBA password change.
Bottom line: if normal connections work but SYSDBA fails with ORA-01017, the password file is out of sync β€” regenerate it and distribute to all nodes.

External Authentication Fallback Failures

Oracle supports multiple external authentication methods: OS authentication (ops$ accounts on Linux, NTS on Windows), Kerberos, LDAP, and RADIUS. When external authentication is configured as the primary method but the external credential check fails, Oracle may silently fall back to password authentication. If the password is also wrong β€” or the user does not have a database password at all β€” ORA-01017 is raised. The error message gives no indication that an external authentication attempt was made and failed before the password fallback.

The sqlnet.ora parameter SQLNET.AUTHENTICATION_SERVICES controls which authentication methods are active. On Windows, the default value is (NTS), which enables Windows domain authentication. On Linux, the default is typically (NONE) or unset, which means password authentication only. If the parameter includes multiple methods, Oracle tries each in order.

The most confusing scenario is: a DBA connects with sqlplus / as sysdba on the database server and succeeds (OS authentication works because the OS user matches the oracle account). They assume the password is therefore correct. But a remote application connecting with sqlplus user/pass@DB fails with ORA-01017 because it uses password authentication, and the password in the connection string does not match the data dictionary hash. OS authentication and password authentication are completely independent paths β€” success in one does not prove the other will work.

Another common scenario is ops$ accounts. On Linux, if SQLNET.AUTHENTICATION_SERVICES includes BEQ, Oracle maps the OS username to a database user named OPS$<osuser>. If the OS user is 'oracle', Oracle looks for a database user named OPS$ORACLE. If this user does not exist in the database, the OS authentication fails silently and Oracle falls back to password authentication β€” which may also fail, producing ORA-01017 with no indication that the real problem is a missing ops$ account.

The diagnostic approach is to determine exactly which authentication method the failing connection is using. Check SQLNET.AUTHENTICATION_SERVICES in the sqlnet.ora that the failing process reads (not the DBA's sqlnet.ora). If external authentication is configured, verify the external credential source (OS account, Kerberos ticket, LDAP bind). If password authentication is the intended method, set SQLNET.AUTHENTICATION_SERVICES = (NONE) to disable external methods and force password-only authentication.

io/thecodeforge/debug/external_auth_fallback.sql Β· SQL
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354
-- Diagnostic 1: Check which authentication methods are configured
-- File: $TNS_ADMIN/sqlnet.ora
-- $ grep -i AUTHENTICATION_SERVICES $TNS_ADMIN/sqlnet.ora
-- Common values:
-- SQLNET.AUTHENTICATION_SERVICES = (NONE)       -- password only
-- SQLNET.AUTHENTICATION_SERVICES = (BEQ, NONE)  -- OS auth first, password fallback
-- SQLNET.AUTHENTICATION_SERVICES = (NTS)         -- Windows domain auth
-- SQLNET.AUTHENTICATION_SERVICES = (KERBEROS5)   -- Kerberos only
-- SQLNET.AUTHENTICATION_SERVICES = (ALL)          -- try everything

-- Diagnostic 2: Check for ops$ accounts (OS authentication)
SELECT username, account_status, authentication_type
FROM dba_users
WHERE username LIKE 'OPS$%'
ORDER BY username;
-- If the OS user is 'oracle', Oracle looks for OPS$ORACLE
-- If no matching ops$ account exists: OS auth fails silently

-- Diagnostic 3: Check if the user is configured for external auth
SELECT username, authentication_type
FROM dba_users
WHERE username = UPPER('app_user');
-- PASSWORD: uses data dictionary password
-- EXTERNAL: uses OS / external authentication
-- GLOBAL:   uses LDAP / directory authentication

-- Diagnostic 4: Test OS authentication explicitly
-- On the database server, as the oracle OS user:
-- $ sqlplus /
-- Connected.  β€” OS auth works (ops$ account exists and matches)
-- ORA-01017   β€” OS auth failed (ops$ account missing or mismatch)

-- Diagnostic 5: Test password authentication explicitly
-- From any machine:
-- $ sqlplus app_user/"MyPassword"@DBNAME
-- Connected.  β€” password auth works
-- ORA-01017   β€” password is wrong, case mismatch, or account locked

-- Diagnostic 6: Force password-only authentication
-- File: $TNS_ADMIN/sqlnet.ora
-- SQLNET.AUTHENTICATION_SERVICES = (NONE)
-- This disables all external auth methods
-- Restart the application after changing sqlnet.ora

-- Diagnostic 7: Check if Kerberos ticket is valid
-- $ klist
-- If no valid ticket: Kerberos auth fails, may fall back to password
-- Renew ticket: $ kinit username@REALM

-- Diagnostic 8: Create an ops$ account for OS authentication
CREATE USER "OPS$ORACLE" IDENTIFIED EXTERNALLY;
GRANT CREATE SESSION TO "OPS$ORACLE";
-- The double quotes preserve the exact case of the username
-- The OS username must match exactly (including case on Linux)
πŸ’‘External Authentication Diagnostic Strategy
  • sqlplus / as sysdba succeeding does NOT prove that password authentication works β€” it uses OS authentication, which is a completely separate code path
  • Check SQLNET.AUTHENTICATION_SERVICES to determine which auth methods are active for the failing process
  • If external auth fails, Oracle may silently fall back to password auth β€” but only if the password is also correct does the connection succeed
  • On Windows, NTS is often the default β€” remove it from AUTHENTICATION_SERVICES to force password-only auth
  • ops$ accounts must exist in the database with the exact OS username β€” OS auth fails silently if the mapping is missing
  • Test the exact authentication method your application uses β€” do not assume one works because a different method succeeds
πŸ“Š Production Insight
External authentication succeeding does not prove password authentication works β€” they use completely independent credential stores and code paths.
OS auth uses the ops$ account mapping; password auth uses the data dictionary hash. Success in one tells you nothing about the other.
Rule: test the exact authentication method your application uses β€” sqlplus / and sqlplus user/pass@db are different tests that prove different things.
🎯 Key Takeaway
External authentication (OS, Kerberos, LDAP) and password authentication are independent code paths with independent credential stores.
If one succeeds and the other raises ORA-01017, they are authenticating against different sources.
Bottom line: always test the exact authentication method your application uses β€” a successful sqlplus / does not prove that sqlplus user/pass@db will work.
πŸ—‚ ORA-01017 vs. Related Authentication Errors
Oracle has multiple authentication-related errors β€” each means something different and requires a different fix
ErrorMeaningRoot CauseFix
ORA-01017Invalid username/password; logon deniedWrong credentials, case mismatch, wrong auth method, stale password file, or stale database link credentialsVerify username exists, check password case, check account status, check auth method, regenerate password file if SYSDBA
ORA-28000The account is lockedFAILED_LOGIN_ATTEMPTS threshold exceeded (auto-lock) or manual lock by DBAALTER USER account ACCOUNT UNLOCK; investigate which host sent wrong passwords
ORA-28001The password has expiredPASSWORD_LIFE_TIME exceeded and PASSWORD_GRACE_TIME endedALTER USER account IDENTIFIED BY "newpassword"; consider extending PASSWORD_LIFE_TIME for service accounts
ORA-28002The password will expire within N daysPassword is within PASSWORD_GRACE_TIME of expiry β€” warning, not a failureChange the password before the grace period ends; this is a warning, not an error
ORA-28003The password verification failed for the specified passwordNew password does not meet the profile's password complexity function (PASSWORD_VERIFY_FUNCTION)Use a password that meets complexity requirements (length, mixed case, special characters, not a dictionary word)
ORA-28007The password cannot be reusedPASSWORD_REUSE_TIME or PASSWORD_REUSE_MAX policy prevents reusing a recent passwordUse a password not used within the reuse window defined by the profile
ORA-12154TNS: could not resolve the connect identifier specifiedClient cannot resolve the TNS alias to a network address β€” this is NOT an auth errorCheck tnsnames.ora, TNS_ADMIN, and sqlnet.ora (completely different diagnostic path)

🎯 Key Takeaways

  • ORA-01017 is deliberately vague β€” it does not distinguish between a wrong username, a wrong password, a locked account, an expired password, or a wrong authentication method, because confirming username existence would be an information disclosure vulnerability
  • Database links store a static copy of the password at creation time β€” password rotation must include dropping and recreating every affected link, because there is no ALTER DATABASE LINK for credentials
  • Case-sensitive passwords (SEC_CASE_SENSITIVE_LOGON = TRUE, the default since 11g) break connections when the client sends a different case than what was stored β€” ALTER USER without double quotes stores uppercase
  • SYSDBA authentication uses the password file, not the data dictionary β€” ALTER USER updates the dictionary but typically not the file, so regenerate it with orapwd after every SYSDBA password change
  • Service accounts must use a profile with FAILED_LOGIN_ATTEMPTS = UNLIMITED β€” one misconfigured consumer should not cascade a lockout to every other consumer sharing the account
  • OS authentication (sqlplus /) and password authentication (sqlplus user/pass@db) are independent code paths β€” success in one proves nothing about the other

⚠ Common Mistakes to Avoid

    βœ•Assuming the username is wrong when the password is wrong
    Symptom

    ORA-01017 does not distinguish between a nonexistent username and a wrong password. Engineers waste time verifying the username exists (it does) when the actual problem is a wrong password or a case mismatch.

    Fix

    Verify the username exists first: SELECT username, account_status FROM dba_users WHERE username = UPPER('myuser'). If it exists and is OPEN, the password is wrong. Reset it with ALTER USER myuser IDENTIFIED BY "NewPassword" (double quotes to preserve case). Verify the client sends the exact case.

    βœ•Setting passwords with ALTER USER without double quotes and expecting mixed-case authentication
    Symptom

    ALTER USER app_user IDENTIFIED BY MyPassword stores the password as MYPASSWORD (uppercase, because Oracle treats unquoted identifiers as uppercase). The application then sends MyPassword (mixed case), the hash does not match, and ORA-01017 is raised.

    Fix

    Always use double quotes when setting passwords to make the stored case explicit: ALTER USER app_user IDENTIFIED BY "MyPassword". Verify the client connection string sends the exact same case. Document the password case in the credential vault.

    βœ•Not updating database links after password rotation
    Symptom

    After rotating the remote database password per security policy, all database links fail with ORA-01017 inside ORA-06512 stacks. The remote account is OPEN, not locked, not expired β€” direct connections with the new password work. The link definition still holds the old password.

    Fix

    Enumerate all links that reference the rotated account: SELECT owner, db_link FROM dba_db_links WHERE username = UPPER('rotated_user'). Drop and recreate each link with the new password. Test each link with SELECT 1 FROM dual@link_name. Add link updates as a mandatory step in the rotation runbook.

    βœ•Not regenerating the password file after changing a SYSDBA account password
    Symptom

    Normal connections (sqlplus user/pass@db) work perfectly with the new password. SYSDBA connections (sqlplus sys/newpass@db as sysdba) fail with ORA-01017. The data dictionary has the new hash, but the password file still has the old hash.

    Fix

    Regenerate the password file: orapwd file=$ORACLE_HOME/dbs/orapwSID password=newpass entries=30 force=y format=12.2. Copy the file to all RAC nodes and Data Guard standbys. Verify SYSDBA connectivity on every node after regeneration.

    βœ•Using a low FAILED_LOGIN_ATTEMPTS value for shared service accounts
    Symptom

    A misconfigured application instance sends the wrong password 10 times during startup retry. The service account locks (LOCKED(TIMED) status). Every other application, ETL job, and reporting query sharing the same account is immediately blocked for PASSWORD_LOCK_TIME hours.

    Fix

    Create a dedicated profile for service accounts: CREATE PROFILE svc_profile LIMIT FAILED_LOGIN_ATTEMPTS UNLIMITED PASSWORD_LIFE_TIME UNLIMITED. Assign it: ALTER USER app_user PROFILE svc_profile. Better long-term fix: use separate database accounts per application to prevent cascading lockouts.

    βœ•Testing OS authentication (sqlplus /) and concluding that password authentication works
    Symptom

    The DBA runs sqlplus / as sysdba on the database server and connects successfully. They tell the application team the password is correct. The application, which uses password authentication over the network (sqlplus user/pass@db), still fails with ORA-01017. OS authentication and password authentication use different credential stores.

    Fix

    Test the exact authentication method the application uses. If the application connects with a username and password over the network, test with sqlplus user/"password"@db β€” not sqlplus / which uses OS authentication. The two methods are independent.

Interview Questions on This Topic

  • QWhat causes ORA-01017 and how do you diagnose it?JuniorReveal
    ORA-01017 means the Oracle server rejected the supplied credentials. The error is deliberately vague β€” it does not distinguish between a nonexistent username, a wrong password, a case mismatch, an expired password, or a locked account. Diagnostic steps: first, verify the username exists in DBA_USERS. Second, check account_status (OPEN vs. LOCKED vs. EXPIRED). Third, check SEC_CASE_SENSITIVE_LOGON for password case sensitivity. Fourth, verify which authentication method the client is using (password vs. OS vs. Kerberos) by checking SQLNET.AUTHENTICATION_SERVICES in sqlnet.ora. Fifth, if the error appears inside an ORA-06512 stack, check database link credentials β€” the link may hold a stale password.
  • QWhy does ORA-01017 appear inside ORA-06512 stack traces?Mid-levelReveal
    ORA-01017 appears in ORA-06512 stacks when a database link's embedded credentials are wrong. A PL/SQL procedure queries a remote database through a database link. The link authenticates using the credentials stored in its CONNECT TO ... IDENTIFIED BY clause. If the remote password was rotated but the link still holds the old password, ORA-01017 is raised at the remote authentication layer. This error propagates through the PL/SQL call stack as ORA-06512 entries. The fix is to drop and recreate the database link with the new password β€” there is no ALTER DATABASE LINK command for credential updates.
  • QHow do case-sensitive passwords cause ORA-01017, and what is the interaction with ALTER USER quoting?Mid-levelReveal
    Oracle 11g introduced SEC_CASE_SENSITIVE_LOGON = TRUE by default, making password verification case-sensitive. ALTER USER without double quotes normalizes the password to uppercase (MYPASSWORD). ALTER USER with double quotes preserves exact case (MyPassword). If the stored verifier was created with uppercase but the client sends mixed case, the hashes do not match and ORA-01017 is raised. After upgrading to 12c or later, the SQLNET.ALLOWED_LOGON_VERSION_SERVER parameter may also reject connections from clients that only support older hash algorithms (10g DES verifier), even with the correct password. The fix is to reset the password with double quotes to control the case and generate the current verifier version.
  • QWhat is the difference between the password file and the data dictionary for authentication, and how do they become desynchronized?SeniorReveal
    Normal database connections authenticate against the data dictionary (SYS.USER$). SYSDBA and SYSOPER connections authenticate against the password file (orapwSID). ALTER USER changes the data dictionary hash but, in most configurations, does not automatically update the password file. This creates desynchronization: normal connections succeed with the new password, but SYSDBA connections fail with ORA-01017 because the password file still has the old hash. The fix is to regenerate the password file with the orapwd utility after every SYSDBA-privileged user password change. In RAC environments, the regenerated file must be copied to all nodes (or stored in ASM for automatic sharing). In Data Guard environments, it must be synchronized with standbys for switchover/failover to work.
  • QHow would you design a prevention system that eliminates ORA-01017 caused by password rotation in a production environment?SeniorReveal
    Four-layer prevention. First, include database link credential updates as a mandatory gated step in the password rotation runbook β€” enumerate all links referencing the rotated account before rotation, drop and recreate each link after rotation, test each link with SELECT 1 FROM dual@link_name before closing the rotation ticket. Second, regenerate the password file with orapwd and distribute to all RAC and Data Guard nodes after any SYSDBA password change. Third, create a dedicated profile for service accounts with FAILED_LOGIN_ATTEMPTS UNLIMITED and PASSWORD_LIFE_TIME UNLIMITED to prevent cascading lockouts from misconfigured consumers. Fourth, schedule a daily job that tests every database link and alerts on ORA-01017 before business hours. Optionally, evaluate Oracle Wallet (External Password Store) to centralize credentials outside link definitions so password updates do not require DDL.

Frequently Asked Questions

What is the difference between ORA-01017 and ORA-28000?

ORA-01017 means the credentials are wrong β€” wrong password, wrong username, case mismatch, or wrong authentication method. ORA-28000 means the account is locked β€” either automatically after FAILED_LOGIN_ATTEMPTS was exceeded (LOCKED(TIMED)) or manually by a DBA (LOCKED). Both prevent login, but the fix is different: ORA-01017 requires correcting the credentials or authentication configuration; ORA-28000 requires unlocking with ALTER USER ACCOUNT UNLOCK and investigating which host or application is sending wrong passwords.

How do I check if my password is case-sensitive?

Check the SEC_CASE_SENSITIVE_LOGON parameter: SELECT value FROM v$parameter WHERE name = 'sec_case_sensitive_logon'. If TRUE (11g+ default), passwords are case-sensitive. Also check the user's password_versions: SELECT password_versions FROM dba_users WHERE username = UPPER('myuser'). If it contains '11G' or '12C', the stored verifier includes case information. If it contains only '10G', the verifier is case-insensitive (DES hash).

Can I change a database link's password without dropping and recreating it?

No. Oracle does not provide an ALTER DATABASE LINK command for credential changes. The link must be dropped with DROP DATABASE LINK name and recreated with CREATE DATABASE LINK name CONNECT TO user IDENTIFIED BY "newpassword" USING 'alias'. This is why password rotation runbooks must explicitly include database link updates as a step.

Why does sqlplus / as sysdba work but sqlplus user/pass@db fails with ORA-01017?

They use different authentication methods. sqlplus / as sysdba uses OS authentication β€” it checks the OS user against the password file or the OS account mapping, without a password. sqlplus user/pass@db uses password authentication β€” it checks the supplied password against the data dictionary hash. If the data dictionary password is wrong but the OS account is correctly mapped, you get this exact symptom. Test the exact method your application uses.

How do I prevent account lockout for service accounts that are shared across multiple applications?

Create a dedicated profile with FAILED_LOGIN_ATTEMPTS UNLIMITED: CREATE PROFILE svc_profile LIMIT FAILED_LOGIN_ATTEMPTS UNLIMITED PASSWORD_LIFE_TIME UNLIMITED. Assign it to the service account: ALTER USER app_user PROFILE svc_profile. This prevents one misconfigured application from locking out every consumer. Long-term, consider using separate database accounts per application to fully isolate authentication failures.

After upgrading to Oracle 12c or 19c, why does ORA-01017 fire even though the password is correct?

Oracle 12c and later can be configured to reject older password verifiers via the SQLNET.ALLOWED_LOGON_VERSION_SERVER parameter. If set to 12 or 12a, only SHA-2 (12C) verifiers are accepted. If the user's password was never reset after the upgrade, only the old 10G or 11G verifier exists, and the server rejects the authentication. The fix is to reset the password (ALTER USER myuser IDENTIFIED BY "samePassword") to generate the 12C verifier. Check the user's current verifiers with SELECT password_versions FROM dba_users.

πŸ”₯
Naren Founder & Author

Developer and founder of TheCodeForge. I built this site because I was tired of tutorials that explain what to type without explaining why it works. Every article here is written to make concepts actually click.

← PreviousORA-00942: Table or View Does Not Exist – Full TroubleshootingNext β†’ORA-12154: TNS: Could Not Resolve the Connect Identifier
Forged with πŸ”₯ at TheCodeForge.io β€” Where Developers Are Forged