Chiffrement du flux SQL*NET

Le trafic réseau entre un client Oracle et son serveur peut être chiffré pour répondre aux exigences de sécurité en complément du chiffrement des données dans les datafiles et colonnes (TDE).

Oracle propose depuis longtemps un chiffrement natif du flux SQL*NET.
Jusqu’en version 11gR2 inclus, il fallait posséder une Entrerprise Edition ET acheter l’option ASO = Advanced Security (qui comprend aussi le chiffrement TDE).
A partir de la 12c, cette option est incluse dans les éditions Enterprise  et SE2.

Le chiffrement natif fourni par Oracle se distingue du chiffrement TLS, notamment par sa simplicité de mise en œuvre au détriment d’un niveau de sécurité plus faible.

En effet , le chiffrement SQL*Net possède les avantages suivants :

  • Simple à configurer : deux paramètres à positionner dans le fichier sqlnet.ora
  • Pas de configuration obligatoire côté client, elle reste cependant possible
  • Ne nécessite pas de certificat
  • Perte de performance faible

Au niveau des inconvénients, le chiffrement natif Oracle n’est pris en charge que par les clients  OCI Thick.

Pour le cas d’une connexion avec un driver JDBC Thin, le fichier sqlnet.ora n’étant pas lu, il faudra ajouter des propriétés dans la configuration de la connexion.

Je ne vais pas détailler dans cet  article toutes les options de configuration possibles, mais plus focaliser sur la vérification du chiffrement effectif du flux après un rappel rapide de la configuration à réaliser. 

Pour plus de détails sur la configuration, bien d’autres articles ainsi que la documentation Oracle détaillent tous les paramètres.

 

Paramétrage SQLNET.ORA

Celui-ci peut s’effectuer sur le client et sur le serveur, il est toutefois plus simple dans la majorité des cas d’usage de configurer la partie serveur et laisser par défaut la configuration sur le client.

 L’activation du chiffrement côté serveur repose sur 2 paramètres :

SQLNET.ENCRYPTION_SERVER=required|requested|rejected|accepted
SQLNET.ENCRYPTION_TYPES_SERVER=(<encryption algorithm>,<encryption algorithm>,..)
SQLNET.ENCRYPTION_SERVER

Ce paramètre contrôle comment le serveur gère les demandes de connexion chiffrées.

Du côté client, le paramètre correspondant est SQLNET.ENCRYPTION_CLIENT et sa valeur par défaut est ACCEPTED comme pour le paramètre côté serveur.

Les autres valeurs possibles (client et serveur) sont :

  • accepted : indique que le client accepte le chiffrement s’il lui est demandé.
  • rejected : toute demande de connexion chiffrée est rejetée.
  • requested : le chiffrement est activé si l’autre côté le demande et qu’il est possible.
  • required : le chiffrement est exigé mais n’est possible que si l’autre côté le permet.

On voit donc que la combinaison par défaut client|serveur  = ACCEPTED n’active pas le chiffrement puisque qu’aucune des 2 parties ne le demande.

Le tableau ci-dessous nous donne les différentes combinaisons possibles et leur résultat sur l’activation ou non de la couche de chiffrement réseau (ON=Activé, OFF=Désactivé).

server setting

A noter que certaines combinaisons incompatibles (REQUIRED|REJECTED) provoquent une erreur «  »ORA-12660 Encryption or crypto-checksumming parameters incompatible ».

Il suffit donc de commenter la ligne SQLNET.ENCRYPTION_SERVER dans le fichier sqlnet.ora du serveur pour désactiver le chiffrement du flux (pour les nouvelles connexions).

 

SQLNET.ENCRYPTION_TYPES_SERVER

Ce paramètre permet de lister les types de chiffrement souhaités avec un l’ordre de préférence, l’algorithme sera choisi par le serveur et le client s’il est accepté des 2 côtés.

Sur mon serveur Redhat 6.6 Oracle 11.2.0.4,  la liste des algorithmes supportés par le client est indiquée lorsqu’on active une trace des connexions avec le chiffrement activé.

[000001 22-JUN-2020 08:36:49:952] naeshow: These are the encryption algorithms that the client will accept:
[000001 22-JUN-2020 08:36:49:952] naeshow: Choice 1: 'AES256' (ID 17)
[000001 22-JUN-2020 08:36:49:952] naeshow: Choice 2: 'RC4_256' (ID 6)
[000001 22-JUN-2020 08:36:49:952] naeshow: Choice 3: 'AES192' (ID 16)
[000001 22-JUN-2020 08:36:49:952] naeshow: Choice 4: '3DES168' (ID 12)
[000001 22-JUN-2020 08:36:49:952] naeshow: Choice 5: 'AES128' (ID 15)
[000001 22-JUN-2020 08:36:49:952] naeshow: Choice 6: 'RC4_128' (ID 10)
[000001 22-JUN-2020 08:36:49:952] naeshow: Choice 7: '3DES112' (ID 11)
[000001 22-JUN-2020 08:36:49:952] naeshow: Choice 8: 'RC4_56' (ID 8)
[000001 22-JUN-2020 08:36:49:952] naeshow: Choice 9: 'DES' (ID 2)
[000001 22-JUN-2020 08:36:49:952] naeshow: Choice 10: 'RC4_40' (ID 1)
[000001 22-JUN-2020 08:36:49:952] naeshow: Choice 11: 'DES40' (ID 3)

Dans ma configuration, le paramètre SQLNET.ENCRYPTION_TYPES_SERVER est positionné de la manière suivante, qui indique que l’algorithme à utiliser doit être exclusivement AES256

SQLNET.ENCRYPTION_TYPES_SERVER=(AES256)

Deux autres paramètres sont à positionner dans sqlnet.ora pour configurer la vérification d’intégrité des données du flux SQLNET chiffré. Ceci n’est pas obligatoire mais fortement recommandé.

Ces paramètres fonctionnent de la même manière que les autres, avec leur pendant côté client.

SQLNET.CRYPTO_CHECKSUM_SERVER=required|requested|rejected|accepted
SQLNET.CRYPTO_CHECKSUM_TYPES_SERVER=((valid_crypto_checksum_algorithm,valid_crypto_checksum_algorithm,...))

Les algorithmes de checksum supportés dans ma configuration sont SHA1 et MD5.

Si les paramètres SQLNET.ENCRYPTION_TYPES_SERVER ou SQLNET.CRYPTO_CHECKSUM_TYPES_SERVER ne sont pas spécifiés, il seront testés un par un parmi ceux disponibles.

 

Configuration pour client JDBC Thin

En l’absence de fichier sqlnet.ora, on peut positionner les différents paramètres comme ci-dessous par exemple :

DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
Properties props = new Properties();
props.put("oracle.net.encryption_client", "accepted");
props.put("oracle.net.encryption_types_client", "AES256");
props.put("user", "XXX");
props.put("password", "YYY");
Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@myhost:1521:mySID", props);

 

Vérification du chiffrement

Supposons que notre configuration sqlnet.ora soit la suivante sur le serveur :

#  Config chiffrement SQLNET Serveur
SQLNET.ENCRYPTION_SERVER=REQUESTED
# Algorithme de chiffrement
SQLNET.ENCRYPTION_TYPES_SERVER=(AES256)
# Intégrité des données
SQLNET.CRYPTO_CHECKSUM_SERVER=REQUESTED
# Algorithme de vérification de l'intégrité des données
SQLNET.CRYPTO_CHECKSUM_TYPES_SERVER=(SHA1)

L’activation de la fonctionnalité ne nécessite aucun redémarrage d’instance. Si elle est correcte, la configuration est prise en compte immédiatement.

Comment le vérifier ? Il y a plusieurs moyens :

Vérification des options de protocole actives

En se connectant sysdba sous SQL*PLUS et en interrogeant la vue v$session_connect_info :

select NETWORK_SERVICE_BANNER 
from v$session_connect_info
where SID = sys_context('USERENV','SID');

NETWORK_SERVICE_BANNER
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Oracle Bequeath NT Protocol Adapter for Linux: Version 11.2.0.4.0 - Production
Oracle Advanced Security: authentication service for Linux: Version 11.2.0.4.0 - Production
Oracle Advanced Security: encryption service for Linux: Version 11.2.0.4.0 - Production
Oracle Advanced Security: AES256 encryption service adapter for Linux: Version 11.2.0.4.0 - Product
Oracle Advanced Security: crypto-checksumming service for Linux: Version 11.2.0.4.0 - Production
Oracle Advanced Security: SHA1 crypto-checksumming service adapter

On voit que notre configuration est bien prise en compte mais ça ne prouve pas pour autant que le flux est bien chiffré pour les sessions connectées.

Plusieurs moyens sont possibles :

  •  Interroger la vue gv$session_connect_info

 La requête ci-dessous (merci à l’auteur) nous fournit des informations sur le type de chiffrement utilisé le cas échéant par les sessions connectées :

with sessions as
(
select /*+ MATERIALIZE */ inst_id, username, sid, serial#
from gv$session
where not ( type = 'BACKGROUND' or username is NULL )
),
session_connect_info as
(
select /*+ MATERIALIZE */ inst_id, sid, serial#,
regexp_replace( network_service_banner,'^Oracle Advanced Security: ([[:alnum:]]+) encryption service adapter.+$','\1') encryption_type
from gv$session_connect_info
where network_service_banner like 'Oracle Advanced Security:%encryption service adapter%'
)
select s.inst_id, s.sid, s.serial#, s.username, sci.encryption_type
from sessions s
join session_connect_info sci on ( sci.inst_id = s.inst_id and
sci.sid = s.sid and
sci.serial# = s.serial#
)
order by s.inst_id,s.username;

Ce qui donne sur ma base :

INST_ID    SID         SERIAL#   USERNAME        ENCRYPTION_TYPE
---------- ---------- ---------- --------------- --------------------
1 600 2607 SCOTT AES256
1 253 3151 TIGER AES256
1 498 3829 SYS AES256
2 602 9217 SCOTT AES256
2 103 809 SYS AES256
...
  • Activer une trace SQL*Net sur le serveur

Il faut au préalable activer les paramètres de trace dans le fichier sqlnet.ora :

# Traceserver
DIAG_ADR_ENABLED=OFF
TRACE_LEVEL_SERVER = 16
TRACE_DIRECTORY_SERVER = /home/oracle/network/log
TRACE_FILE_SERVER = SQLNetTrace
TRACE_TIMESTAMP_SERVER = ON
TRACE_FILELEN_SERVER = 2048
TRACE_FILENO_SERVER = 2

Le paramètre DIAG_ADR_ENABLED=OFF est important pour que la trace soit bien générée dans le répertoire  indiqué.

  • 1er test sans chiffrement (SQLNET.ENCRYPTION_SERVER commenté)

Se connecter à la base avec sqlplus ou sqldevelopper (OCI), lancer une simple requête :

SQL> select sysdate from dual;
SYSDATE
------------------
22-JUN-20

Sortir de la session et commenter le paramètre TRACE_LEVEL_SERVER pour éviter la production de nouvelles traces.

Dans le répertoire /home/oracle/network/log , des fichiers sont apparus :

-rw-r----- 1 oracle asmadmin 151411 Jun 2 18:16 sqlnettrace1_40774.trc
-rw-r----- 1 oracle asmadmin 0 Jun 22 18:16 sqlnettrace2_40774.trc

Dans le plus gros, on trouve que la session n’est pas chiffrée (le checksum l’est toujours je ne l’avais pas désactivé) :

[000001 22-JUN-2020 18:16:28:414] na_tns: authentication is not active
[000001 22-JUN-2020 18:16:28:414] na_tns: encryption is not active
[000001 22-JUN-2020 18:16:28:414] na_tns: crypto-checksumming is active, using SHA1
[000001 22-JUN-2020 18:16:28:414] na_tns: exit

Et plus loin dans la trace, on retrouve la requête en clair :

[000001 22-JUN-2020 18:17:28:414] nsprec: 00 00 00 00 00 00 00 00 |........|
[000001 22-JUN-2020 18:17:28:414] nsprecv: 00 00 00 00 00 18 73 65 |......se|
[000001 22-JUN-2020 18:17:28:414] nsprecv: 6C 65 63 74 20 73 79 73 |lect.sys|
[000001 22-JUN-2020 18:17:28:414] nsprecv: 64 61 74 65 20 66 72 6F |date.fro|
[000001 22-JUN-2020 18:17:28:414] nsprecv: 6D 20 64 75 61 6C 01 00 |m.dual..|
[000001 22-JUN-2020 18:17:28:414] nsprecv: 00 00 00 00 00 00 00 00 |........|
  • 2ème test avec chiffrement (SQLNET.ENCRYPTION_SERVER=REQUESTED)

Dans nos nouvelles session, en répétant les manipulations précédentes sur la trace et exécutant à nouveau la requête :

[000001 22-JUN-2020 18:22:38:712 naeecom: The server chose the 'AES256' encryption algorithm
[000001 22-JUN-2020 18:22:38:712] naeecom: exit
[000001 22-JUN-2020 18:22:38:712] naeccom: entry
[000001 22-JUN-2020 18:22:38:712] naeccom: The server chose the 'SHA1' crypto-checksumming algorithm
[000001 22-JUN-2020 18:22:38:712] naeccom: exit
[000001 22-JUN-2020 18:22:38:712] na_tns: entry
[000001 22-JUN-2020 18:22:38:712] na_tns: Secure Network Services is available.
...
[000001 22-JUN-2020 18:22:38:712] na_tns:      authentication is not active
[000001 22-JUN-2020 18:22:38:712] na_tns:      encryption is active, using AES256
[000001 22-JUN-2020 18:22:38:712] na_tns:      crypto-checksumming is active, using SHA1

Cette fois  plus de requête en clair, le texte est visiblement chiffré :

[000001 22-JUN-2020 18:22:38:712] nspsend: packet dump
[000001 22-JUN-2020 18:22:38:712] nspsend: 00 CC 00 00 06 00 00 00 |........|
[000001 22-JUN-2020 18:22:38:712] nspsend: 00 00 FE 5D A9 22 74 3C |...]."t<|
[000001 22-JUN-2020 18:22:38:712] nspsend: B3 12 AB 6B 96 E0 C5 75 |...k...u|
[000001 22-JUN-2020 18:22:38:712] nspsend: 29 95 6D AA D5 40 D8 8F |).m..@..|
[000001 22-JUN-2020 18:22:38:712] nspsend: 96 BB B2 B9 E4 4C 4C 27 |.....LL'|
[000001 22-JUN-2020 18:22:38:712] nspsend: DB E8 F0 AB C8 38 79 B7 |.....8y.|
[000001 22-JUN-2020 18:22:38:712] nspsend: 37 A2 7E EB 3C B1 AE D5 |7.~.<...|
[000001 22-JUN-2020 18:22:38:712] nspsend: 4A 5E 1C CA 48 3F B8 84 |J^..H?..|

Ne pas oublier que la sécurité de ce chiffrement repose aussi sur la protection du fichier sqlnet.ora qui ne doit être accessible en modification par tout un chacun, et il faut protéger également la variable TNS_ADMIN qui, si elle peut être modifiée dans l’environnement, peut permettre simplement de contourner cette configuration de chiffrement du flux.

Idéalement, dans un environnement cluster, on positionnera la valeur de celle-ci au niveau de la ressource CRS grâce à SRVCTL au lieu de la mettre dans le .profile du user oracle.

srvctl modify database -d MYDB -T "TNS_ADMIN=/home/oracle/network/admin"

Enfin, n’oubliez pas de désactiver les traces sous peine saturer rapidement le filesystem …

 

2 réflexions sur “Chiffrement du flux SQL*NET”

  1. merci pour cette documentation, j’ai adapté la requête sur les types de chiffrement (valable sur une 11g), à ma base en 19c sous Windows. Elle devrait fonctionner à partir de la 12c
    col username for a30
    col encryption_type for a15
    col checksum_type for a15

    with sessions as
    (
    select /*+ MATERIALIZE */ inst_id, username, sid, serial#
    from gv$session
    where not ( type = ‘BACKGROUND’ or username is NULL )
    ),
    session_connect_encrypt_info as
    (
    select /*+ MATERIALIZE */ inst_id, sid, serial#,
    regexp_replace( network_service_banner,’ Encryption service adapter .*’, ») encryption_type
    from gv$session_connect_info
    where network_service_banner like ‘% Encryption service adapter%’
    ),
    session_connect_checksum_info as
    (
    select /*+ MATERIALIZE */ inst_id, sid, serial#,
    regexp_replace( network_service_banner,’ Crypto-checksumming service adapter .*’, ») checksum_type
    from gv$session_connect_info
    where network_service_banner like ‘% Crypto-checksumming service adapter%’
    )
    select s.inst_id, s.sid, s.serial#, s.username, scei.encryption_type, scci.checksum_type
    from sessions s
    left outer join session_connect_encrypt_info scei on ( scei.inst_id = s.inst_id and
    scei.sid = s.sid and
    scei.serial# = s.serial#
    )
    left outer join session_connect_checksum_info scci on ( scci.inst_id = s.inst_id and
    scci.sid = s.sid and
    scci.serial# = s.serial#
    )
    order by s.inst_id,s.username;

    1. Laurent GALLET

      Merci Pierre pour cette contribution, cette version va m’être utile en 19C également.

Les commentaires sont fermés.