¡Hola, chicos!
En este post me gustaría hablar de la parte de Seguridad de SQL Server centrada en las contraseñas: Políticas de contraseñas, Caducidad de contraseñas, Cambio de contraseña obligatorio y Bloqueo de inicio de sesión después de N intentos.

Vídeo con resumen del artículo

Si te gustó este vídeo y quieres profundizar en el aspecto de la seguridad de los datos, no dejes de consultar mi curso. Seguridad en SQL Server

Políticas de contraseña (CHECK_POLICY)

Haga clic aquí para ver este contenido
La función Política de contraseñas de SQL Server (CHECK_POLICY) tiene como objetivo garantizar que las contraseñas de inicio de sesión sean contraseñas complejas, para reducir la posibilidad de ataques de fuerza bruta. Vale recordar que esta característica solo aplica para usuarios con autenticación SQL.

Uno de los objetivos de la política de contraseñas es evitar este tipo de escenario a continuación, donde alguien intenta autenticarse en el banco mediante prueba y error, sin ser bloqueado, hasta encontrar una combinación donde pueda iniciar sesión en el banco.

Además de restringir contraseñas simples o vacías, la política de contraseñas puede bloquear automáticamente los inicios de sesión después de N intentos incorrectos de usuario/contraseña (este número es configurable), además de permitir que las contraseñas caduquen después de N días y deban cambiarse periódicamente.

Importante: Para obtener más información sobre los ataques de fuerza bruta, visite el artículo. SQL Server: cómo evitar ataques de fuerza bruta a su base de datos

Para crear un usuario con la política de contraseñas habilitada, puede usar este comando:

USE [master]
GO

CREATE LOGIN [teste_politica_senha] 
WITH 
    PASSWORD = 'BdP@BPptxENu',
    CHECK_POLICY = ON
GO

Para verificar los inicios de sesión de SQL que no tienen la política de contraseña habilitada, utilice la siguiente consulta:

SELECT
    A.[name],
    A.[type_desc],
    A.is_disabled,
    A.create_date,
    A.modify_date,
    A.is_policy_checked,
    A.is_expiration_checked,
    LOGINPROPERTY(A.[name],'BadPasswordCount') AS [BadPasswordCount],
    LOGINPROPERTY(A.[name],'BadPasswordTime') AS [BadPasswordTime],
    LOGINPROPERTY(A.[name],'DaysUntilExpiration') AS [DaysUntilExpiration],
    LOGINPROPERTY(A.[name],'HistoryLength') AS [HistoryLength],
    LOGINPROPERTY(A.[name],'IsExpired') AS [IsExpired],
    LOGINPROPERTY(A.[name],'IsLocked') AS [IsLocked],
    LOGINPROPERTY(A.[name],'IsMustChange') AS [IsMustChange],
    LOGINPROPERTY(A.[name],'LockoutTime') AS [LockoutTime],
    LOGINPROPERTY(A.[name],'PasswordLastSetTime') AS [PasswordLastSetTime],
    LOGINPROPERTY(A.[name],'PasswordHashAlgorithm') AS [PasswordHashAlgorithm]
FROM 
    sys.sql_logins A
    JOIN sys.server_principals B ON A.[sid] = B.[sid]
WHERE
    A.is_disabled = 0
    AND B.is_fixed_role = 0
    AND A.is_policy_checked = 0

Resultado:

También puede ver las propiedades de inicio de sesión para confirmar que esta opción está habilitada.

Hablando de política de contraseñas, puedes consultar en este enlace aquí la política de contraseñas de Windows, utilizada por SQL Server para inicios de sesión con autenticación SQL.

Al habilitar la política de contraseñas para un inicio de sesión SQL, debe seguir la siguiente política:

  • La contraseña no puede contener el nombre de la cuenta del usuario.
  • La contraseña debe tener al menos 8 (ocho) caracteres
  • Las contraseñas pueden tener hasta 128 caracteres. Utilice contraseñas largas y complejas
  • No se permiten contraseñas nulas o en blanco
  • No está permitido utilizar el mismo nombre de computadora o iniciar sesión como contraseña.
  • Contraseñas que no están permitidas: “contraseña”, “admin”, “administrador”, “sa”, “sysadmin”
  • La contraseña debe contener caracteres de al menos tres de las cuatro categorías siguientes:
    – Letras mayúsculas latinas (A a Z)
    – Letras minúsculas latinas (a a z)
    – Números (0 a 9)
    – Caracteres no alfanuméricos como: signo de exclamación (!), signo de dólar ($), signo numérico (#) o porcentaje (%).
Cuando CHECK_POLICY se cambia a ON, se produce el siguiente comportamiento:
  • CHECK_EXPIRATION también está configurado en ON a menos que se establezca explícitamente en OFF
  • El historial de contraseñas se inicializa con el valor hash de la contraseña actual
  • Las opciones de duración del bloqueo de cuenta, límite de bloqueo de cuenta y reinicio del contador de bloqueo de cuenta después de que también estén habilitadas

Cuando CHECK_POLICY se cambia a OFF, se produce el siguiente comportamiento:

  • CHECK_EXPIRATION también se desactivará
  • Se borrará el historial de contraseñas
  • El valor lockout_time se restablece.

Para verificar información adicional en la configuración de Windows, como la cantidad de fallas de inicio de sesión para bloquear un inicio de sesión, puede abrir la pantalla "Política de seguridad local" escribiendo el comando "secpol.msc" en el menú Inicio.

O en la pantalla Ejecutar:

Navegue al directorio "Políticas de cuenta" y luego al directorio "Política de contraseñas"

Hay algunas configuraciones interesantes en esta pantalla con respecto a la complejidad de la contraseña:

  • Hacer cumplir el historial de contraseñas: Número de contraseñas que se almacenarán para garantizar que se vuelva a utilizar una contraseña utilizada anteriormente. Este valor debe estar entre 0 y 24 contraseñas y el valor predeterminado es 0 (cero)
  • Longitud mínima de la contraseña: Bastante obvio aquí. Define el número mínimo de caracteres que debe tener una contraseña para ser aceptada como contraseña válida. Los valores aceptables para este parámetro están entre 1 y 14. El valor predeterminado es 0 (cero), lo que significa que no hay un tamaño mínimo.
  • La contraseña debe cumplir con los requisitos de complejidad: Este parámetro define si se deben utilizar políticas de contraseñas y se debe evitar la creación de contraseñas que no se ajusten a la política. El valor predeterminado es Habilitado y, si está deshabilitado, SQL Server no validará la complejidad de la contraseña incluso si habilita la propiedad CHECK_POLICY en el inicio de sesión de SQL.

Al intentar crear una contraseña con menos caracteres de los definidos en esta pantalla, verá este mensaje de error:

Mensaje 15116, Nivel 16, Estado 1, Línea 15
Error de validación de contraseña. La contraseña no cumple con los requisitos de la política del sistema operativo porque es demasiado corta.
Observación: Para obtener más información sobre estas configuraciones de “Política de contraseñas”, haga clic en este enlace aquí.

Caducidad de la contraseña (CHECK_EXPIRATION)

Haga clic aquí para ver este contenido
Otra característica interesante desde el punto de vista de la seguridad es la posibilidad de establecer una fecha de vencimiento para las contraseñas de inicio de sesión SQL. Una vez habilitada, esta opción (CHECK_EXPIRATION) caducará la contraseña de inicio de sesión después de N días y solo será posible conectarse al cambiar la contraseña.

Para crear un usuario cuya contraseña caduque, puede usar el siguiente comando:

USE [master]
GO

CREATE LOGIN [teste_expiracao_senha] 
WITH 
    PASSWORD = 'BdP@BPptxENu',
    CHECK_POLICY = ON,
    CHECK_EXPIRATION = ON
GO

Para verificar los usuarios caducados o que caducan pronto, puede utilizar la siguiente consulta:

SELECT
    A.[name],
    A.[type_desc],
    A.is_disabled,
    A.create_date,
    A.modify_date,
    A.is_policy_checked,
    A.is_expiration_checked,
    A.password_hash,
    LOGINPROPERTY(A.[name],'BadPasswordCount') AS [BadPasswordCount],
    LOGINPROPERTY(A.[name],'BadPasswordTime') AS [BadPasswordTime],
    LOGINPROPERTY(A.[name],'DaysUntilExpiration') AS [DaysUntilExpiration],
    LOGINPROPERTY(A.[name],'HistoryLength') AS [HistoryLength],
    LOGINPROPERTY(A.[name],'IsExpired') AS [IsExpired],
    LOGINPROPERTY(A.[name],'IsLocked') AS [IsLocked],
    LOGINPROPERTY(A.[name],'IsMustChange') AS [IsMustChange],
    LOGINPROPERTY(A.[name],'LockoutTime') AS [LockoutTime],
    LOGINPROPERTY(A.[name],'PasswordLastSetTime') AS [PasswordLastSetTime],
    LOGINPROPERTY(A.[name],'PasswordHashAlgorithm') AS [PasswordHashAlgorithm]
FROM 
    sys.sql_logins A
    JOIN sys.server_principals B ON A.[sid] = B.[sid]
WHERE
    A.is_disabled = 0
    AND B.is_fixed_role = 0
    AND (
        LOGINPROPERTY( A.[name], 'IsExpired' ) = 1
        OR LOGINPROPERTY( A.[name], 'DaysUntilExpiration' ) < 15
    )

Resultado:

También puede ver las propiedades de inicio de sesión para confirmar que esta opción está habilitada.

Para cambiar la cantidad de días que caducan las contraseñas, puede abrir la pantalla "Política de seguridad local" escribiendo el comando "secpol.msc" en el menú Inicio.

O en la pantalla Ejecutar:

Navegue a la carpeta "Políticas de cuenta" y luego a "Política de contraseñas".

Edite la propiedad "Vigencia máxima de la contraseña" y defina un número del 1 al 999 para definir la cantidad de días que caduca una contraseña. El valor predeterminado es 42 días.

Si establece el valor 0 (cero), esto significa que la contraseña no caducará. Es decir, incluso si habilita esta opción de caducidad de contraseña en SQL Server, no caducará porque la configuración de política de Windows está configurada para no caducar.

Observación: Para obtener más información sobre estas configuraciones de “Política de contraseñas”, haga clic en este enlace aquí.

Cambio de contraseña obligatorio (MUST_CHANGE)

Haga clic aquí para ver este contenido
La propiedad MUST_CHANGE define que el usuario deberá cambiar la contraseña la próxima vez que se conecte a la base de datos.

Para crear un inicio de sesión donde debe cambiar la contraseña en el primer uso, usa algo como esto:

USE [master]
GO

CREATE LOGIN [teste3] 
WITH 
    PASSWORD=N'a*1'
    MUST_CHANGE,
    CHECK_EXPIRATION=ON,
    CHECK_POLICY=ON
GO

Importante: La opción MUST_CHANGE solo se puede utilizar si las opciones CHECK_EXPIRATION y CHECK_POLICY están configuradas en ON.

Para ver los inicios de sesión que tienen esta propiedad MUST_CHANGE habilitada, puede utilizar la siguiente consulta:

SELECT
    A.[name],
    A.[type_desc],
    A.is_disabled,
    A.create_date,
    A.modify_date,
    A.is_policy_checked,
    A.is_expiration_checked,
    LOGINPROPERTY(A.[name],'BadPasswordCount') AS [BadPasswordCount],
    LOGINPROPERTY(A.[name],'BadPasswordTime') AS [BadPasswordTime],
    LOGINPROPERTY(A.[name],'DaysUntilExpiration') AS [DaysUntilExpiration],
    LOGINPROPERTY(A.[name],'HistoryLength') AS [HistoryLength],
    LOGINPROPERTY(A.[name],'IsExpired') AS [IsExpired],
    LOGINPROPERTY(A.[name],'IsLocked') AS [IsLocked],
    LOGINPROPERTY(A.[name],'IsMustChange') AS [IsMustChange],
    LOGINPROPERTY(A.[name],'LockoutTime') AS [LockoutTime],
    LOGINPROPERTY(A.[name],'PasswordLastSetTime') AS [PasswordLastSetTime],
    LOGINPROPERTY(A.[name],'PasswordHashAlgorithm') AS [PasswordHashAlgorithm]
FROM 
    sys.sql_logins A
    JOIN sys.server_principals B ON A.[sid] = B.[sid]
WHERE
    A.is_disabled = 0
    AND B.is_fixed_role = 0
    AND LOGINPROPERTY( A.[name], 'IsMustChange' ) = 1

Resultado:

A diferencia de las otras 2 propiedades (CHECK_POLICY y CHECK_EXPIRATION), no es posible ver en la interfaz SSMS si la propiedad MUST_CHANGE está habilitada, solo usando T-SQL.

Al intentar iniciar sesión con este usuario en la base de datos con este usuario recién creado (prueba3), verá esta pantalla aquí:

Para forzar a un usuario existente a cambiar su contraseña, puede cambiar su contraseña a cualquiera y activar la opción MUST_CHANGE

USE [master]
GO

ALTER LOGIN [teste3] WITH PASSWORD = 'senhaqualquer123*' MUST_CHANGE

Importante: No es posible activar la opción MUST_CHANGE en un inicio de sesión existente sin cambiar la contraseña actual por cualquier otra en el mismo comando ALTER LOGIN.

Inicio de sesión bloqueado después de N intentos

Haga clic aquí para ver este contenido
Supongo que ustedes lo saben, pero SQL Server solo bloqueará un inicio de sesión SQL si la opción de política de contraseña está habilitada para ese inicio de sesión y la configuración de la política de contraseña de Windows/AD está configurada correctamente.

Si esta combinación no está configurada correctamente, puede intentar conectarse tantas veces como desee y SQL solo almacenará las fallas de conexión en el registro (si está configurado para hacerlo), pero no bloqueará el inicio de sesión.

Una forma de evitar que esto suceda es utilizar únicamente inicios de sesión con autenticación de Windows (la mejor opción) o establecer un límite en la cantidad de inicios de sesión fallidos para bloquear automáticamente.

Para hacer esto, puede abrir la pantalla "Política de seguridad local" escribiendo el comando "secpol.msc" en el menú Inicio

O en la pantalla Ejecutar:

Navegue al directorio "Políticas de cuenta" y luego al directorio "Política de bloqueo de cuenta"

La configuración predeterminada es la de la captura de pantalla, NO bloquea a los usuarios debido a intentos fallidos de conexión debido a un error de usuario/contraseña, lo cual me parece muy inseguro.

Incluso podemos confirmar que con un valor de 0 (cero), la contraseña NO será bloqueada por fallas de conexión.

Cambiaré esta configuración para bloquear después de 5 fallas de conexión seguidas

Y poco después de confirmar este cambio, aparece una nueva ventana de diálogo que le sugiere cambiar también la configuración de "Período de bloqueo de cuenta" y "Restablecer contador de bloqueo de cuenta después" a 30 minutos (este valor se puede cambiar más adelante). Esto significa que después de 30 minutos, la cuenta se desbloqueará automáticamente y se restablecerá el contador de fallos de conexión.

De ahora en adelante, después de 5 fallas (configuré este valor) durante el proceso de inicio de sesión, este inicio de sesión SQL se bloqueará automáticamente durante 30 minutos (este tiempo también es configurable).

Si continúa ingresando mal la contraseña, se mostrará el mensaje predeterminado de contraseña incorrecta, incluso si el usuario ya está bloqueado:

Error de inicio de sesión para el usuario "teste_politica_senha". Motivo: La contraseña no coincide con la del inicio de sesión proporcionado.

Si se ingresa la contraseña correcta y el usuario está bloqueado, se mostrará este mensaje de error:

Error de inicio de sesión para el usuario "teste_politica_senha". Motivo: la cuenta está actualmente bloqueada. El administrador del sistema puede desbloquearlo.

Para comprobar los inicios de sesión de SQL bloqueados, puede utilizar esta consulta:

SELECT
    A.[name],
    A.[type_desc],
    A.is_disabled,
    A.create_date,
    A.modify_date,
    A.is_policy_checked,
    A.is_expiration_checked,
    LOGINPROPERTY(A.[name],'BadPasswordCount') AS [BadPasswordCount],
    LOGINPROPERTY(A.[name],'BadPasswordTime') AS [BadPasswordTime],
    LOGINPROPERTY(A.[name],'DaysUntilExpiration') AS [DaysUntilExpiration],
    LOGINPROPERTY(A.[name],'HistoryLength') AS [HistoryLength],
    LOGINPROPERTY(A.[name],'IsExpired') AS [IsExpired],
    LOGINPROPERTY(A.[name],'IsLocked') AS [IsLocked],
    LOGINPROPERTY(A.[name],'IsMustChange') AS [IsMustChange],
    LOGINPROPERTY(A.[name],'LockoutTime') AS [LockoutTime],
    LOGINPROPERTY(A.[name],'PasswordLastSetTime') AS [PasswordLastSetTime],
    LOGINPROPERTY(A.[name],'PasswordHashAlgorithm') AS [PasswordHashAlgorithm]
FROM 
    sys.sql_logins A
    JOIN sys.server_principals B ON A.[sid] = B.[sid]
WHERE
    A.is_disabled = 0
    AND B.is_fixed_role = 0
    AND LOGINPROPERTY(A.[name],'IsLocked') = 1

Resultado:

También puede ver si esta opción está activa a través de la interfaz SSMS al abrir las propiedades de inicio de sesión SQL:

Vale recordar que existe un tiempo configurable donde el usuario se desbloqueará automáticamente después de N minutos.

Observación: Para obtener más información sobre estas configuraciones de "Política de bloqueo de cuenta", haga clic en este enlace aquí.

Política de seguridad local (sin AD) o administración de políticas de grupo (con AD)

Si la máquina está en un dominio (muy probable), estas configuraciones de complejidad de contraseña, bloqueo automático y otras deben cambiarse usando la utilidad “Group Policy Management” para que estos cambios se realicen en Windows Active Directory (Windows AD) y, poco después, se repliquen en todos los servidores:

Si solo cambia la configuración local de Windows del servidor, la próxima vez que se actualicen las políticas, la configuración del dominio sobrescribirá el cambio que realizó localmente.

Puede utilizar la “Política de seguridad local” (secpool.msc) para ver la política actual que se aplica en el servidor, ya que probablemente será la misma que la del dominio.

Para forzar una actualización de las políticas locales de Active Directory para la computadora local, puede ejecutar el comando "gpupdate /force" desde el símbolo del sistema DOS:

Si su máquina NO está en un dominio, simplemente cambie esta configuración en la utilidad "Política de seguridad local" (secpool.msc), como mostré en el artículo.

¡Y eso es todo, amigos!
¡Un abrazo grande y hasta la próxima!