, , Sécuriser le SQL embarqué

SQL prend une place de plus en plus importante dans les développements.

Il faut donc prendre des mesures de protection spécifiques sur le SQL embarqué qu’on ne prenait pas forcément en RPGLE.

Par exemple si vous utilisez du cryptage avec les fonctions ENCRYPT et DECRYPT il est important de ne pas afficher la clé de cryptage.

1) Le debug

Le premier risque c’est le debug : vous allez voir la clé de décryptage, même si vous utilisez une variable host c’est facile à voir.

La solution, c’est quand vous compilez votre programme le paramètre DBGENCKEY(‘Votre_clé’)

Vous devrez désormais indiquer votre clé pour débuguer.

Attention bien à ne pas perdre cette clé qui vous sera demandée à chaque de bug !

2) Informations sur les instructions SQL embarquées

Vous avez des vues qui vous permettent de voir le code sql embarqué dans vos programmes RPGLE

exemple :

la vue QSYS2.SYSPROGRAMSTMTSTAT

Pour vous prémunir vous devrez utiliser une variable host et ainsi vous n’aurez pas la valeur de décryptage dans l’instruction …

3) Le cache sql

Vous avez l’instruction exécutée que vous pouvez consulter dans le cache SQL, par exemple par ACS

Comment faire pour que la valeur ne soit pas affichée et pas affichable dans la requête avec les variables ?

Vous avez une procédure SYSPROC.SET_COLUMN_ATTRIBUTE

CALL SYSPROC.SET_COLUMN_ATTRIBUTE(‘GENVOI’, ‘GPARAM’, ‘PWORD’, ‘SECURE YES’);

plus de detail ici

Vous avez :h dans le cache et quand vous faites <gestion instruction SQL et variables>
la zone apparait en sécure

Vous devrez faire cette opérations sur toutes les zones !

https://www.ibm.com/docs/en/i/7.4?topic=services-set-column-attribute-procedure

4) Les SQL packages

Le SQL package est un objet qui stocke des informations pour en tirer partie au cours d’une future utilisation, depuis l’arrivée du moteur SQE, ils sont utilisés en second par rapport au cache SQL

Il faut différencier 2 types de SQL PACKAGE :

  • Les sql packages qui font partie de votre programme pour les SQL statiques, vous pouvez les consulter par la commande PRTSQLINF de votre programme sqlrpgle qui produira un spool comme celui ci :

On voit que également en ayant utilisé une variable hôte que l’information n’apparait pas en claire

  • Il existe une deuxième catégorie de packages SQL qui sont dus à l’utilisation du Extended Dynamic SQL, ce sont des objets qui sont créés essentiellement pour des accès ODBC et dynamiques, ce sont des objets de type *SQLPKG, ils sont en principe créés dans la bibliothèque qui contient la base de données. SQL utilisera les informations qui sont stockées à l’intérieur quand il en aura besoin. Il est difficile de voir le contenu des ces objets , cependant vous pouvez faire un dump de cet objet, par la commande DMPOBJ comme ci-dessous , mais encore une fois pas de contenu des variables hôtes.

Ces objets peuvent être supprimés, le système les recréera automatiquement quand, par exemple, vous changez de version d’IBM i ou quand il occupe trop de place. Attention cependant, il en existe certains qui font partie de SQL comme

QSQLPKG2 de QSYS
QSQXDPKG de QSYS

En règle générale, il ne faut pas toucher à ceux dont le nom commence par Q, ni aux objets qui sont dans QSYS.

Ci dessous un lien pour en savoir plus ces objets

https://www.ibm.com/support/pages/sql-package-questions-and-answers

Complément sur la procédure SET_COLUMN_ATTRIBUTE

Merci Christian

la procédure SET_COLUMN_ATTRIBUTE cache aussi :

  • la valeur des marqueurs qui sont dans les lignes QQRID=3010 des moniteurs de performance et images instantanées de cache de plan.
  • la valeur des marqueurs qui sont dans les attributs d’un graphe Visual Explain (voir image).
  • la valeur des marqueurs dans le pseudo SQL des nœuds de VE (voir image).

Conclusion :

SQL change la donne sur certains points, vous devrez adapter votre sécurité en fonction.

Dans tous les cas, préférez une variable host à un hardcoding qui apparait en clair.

Il y a certainement d’autres choses à mettre en œuvre, mais ces quelques astuces vous donnent une idée de ce que vous devrez mettre en œuvre

, , Liste des fonctions usage

Vous avez installé un nouveau système et il vous manque des fonctions usage dans navigator for i , attention il y a des fonctions qui ne sont pas administrables par cette interface mais uniquement en 5250 par la commande =>WRKFCNUSG, cela dépendra de la catégorie :

Pour gérer plus simplement les utilisateurs pour les fonctions non administrées dans navigator for i , (par exemple QIBM_DB_ZDA qui sert pour autoriser les accès ODBC) nous proposons un produit téléchargeable à cette adresse https://github.com/Plberthoin/PLB/blob/master/WRKUSRUSG/

Vous pouvez gérer le comportement globale de la fonction <F10> et les users en exception <F6>

Maintenant ,voici comment ajouter celles qui sont administrables et que vous ne voyez pas

Leur affichage dans Navigator for i peut dépendre des produits installés sur votre partition

Ouvrir l’onglet  

Ajouter des fonctions dans fonctions usage

Puis choisir dans les actions

sélectionnez l’enregistrement des fonctions

Quand vous revenez dans liste des fonctions, vous en avez maintenant beaucoup plus

On peut analyser les violations des fonctions usage, c’est les postes de type GR dans le journal d’audit.

Soit par un DSPJRN

Soit par une requête SQL

Liste des violations de Fonction Usage sur la journée précédente !

SELECT *

  FROM TABLE (

      SYSTOOLS.AUDIT_JOURNAL_GR (STARTING_TIMESTAMP => CURRENT DATE – 1 DAYS))

    WHERE FUNCTION_REGISTRATION_OPERATION = ‘USAGE FAILURE’

Conclusion :

L’utilisation de certaines de ces fonctions devient primordiale, et il faudra s’habituer à les utiliser .

Vous pouvez ajouter ou supprimer des fonctions liées à des applications installées sur votre partition dans navigator for i mais toutes ne sont pas gérées dans l’interface !

, Navigator for Visualisation des Audits

Navigator for i évolue, petit rappel au passage l’ancien interface ne sera plus utilisable en 2023, ils vous faut donc passé au nouveau, en réalité pas de panique, il n’y a rien à faire l’application des PTFs fait l’installation automatiquement.

Je vais vous parler ici d’une fonction passée un peu inaperçue mais qui peut intéresser certain d’entre vous en effet elle permet de visualiser les postes d’audit sous forme de graphique !

Vous devez choisir l’option

Vous devez ensuite choisir les informations que vous voulez voir sur votre graphique

Vous pouvez choisir une vue détail

ou une vue graphique

Remarque :

C’est le deuxième outil qui se base sur les journaux d’audit, l’autre c’est IDS il faut être un expert en réseau pour en tirer partie

Celui la est très simple et il vous permet d’avoir rapidement affichage intéressant des informations de sécurités que vous voulez tracer

C’est un menu que vous pouvez lancer à tout moment par la combinaison de touche <flèche> + <ATTN> + <Enter>

Si vous avez du mal au clavier vous pouvez dans ACS faire un clique droit !

Ce menu vous propose des actions voici un source qui vous permet de voir ces options

Le programme en CLP

pgm
dclf wrkattn
dcl &msg *char 100
dcl &txt *char 50
             RTVMSG     MSGID(CPX2313) MSGF(QCPFMSG) MSG(&MSG)
             DOUNTIL    COND(&IN03) /* do */
             RTVOBJD    OBJ(%SST(&MSG 1 10)) OBJTYPE(*CMD) TEXT(&txt)
             chgvar &cmd001  (%sst(&msg  1 10) *cat &txt)
             RTVOBJD    OBJ(%SST(&MSG 12 10)) OBJTYPE(*CMD) TEXT(&txt)
             chgvar &cmd002  (%sst(&msg 12 10)  *cat &txt)
             RTVOBJD    OBJ(%SST(&MSG 23 10)) OBJTYPE(*CMD) TEXT(&txt)
             chgvar &cmd003  (%sst(&msg 23 10) *cat &txt)
             RTVOBJD    OBJ(%SST(&MSG 34 10)) OBJTYPE(*CMD) TEXT(&txt)
             chgvar &cmd004  (%sst(&msg 34 10) *cat &txt)
             RTVOBJD    OBJ(%SST(&MSG 45 10)) OBJTYPE(*CMD) TEXT(&txt)
             chgvar &cmd005  (%sst(&msg 45 10) *cat &txt)
             RTVOBJD    OBJ(%SST(&MSG 56 10)) OBJTYPE(*CMD) TEXT(&txt)
             chgvar &cmd006  (%sst(&msg 56 10) *cat &txt)
             RTVOBJD    OBJ(%SST(&MSG 67 10)) OBJTYPE(*CMD) TEXT(&txt)
             chgvar &cmd007  (%sst(&msg 67 10) *cat &txt)
             RTVOBJD    OBJ(%SST(&MSG 78 10)) OBJTYPE(*CMD) TEXT(&txt)
             chgvar &cmd008  (%sst(&msg 78 10) *cat &txt)
             RTVOBJD    OBJ(%SST(&MSG 89 10)) OBJTYPE(*CMD) TEXT(&txt)
             monmsg cpf9801
             chgvar &cmd009  %sst(&msg 89 10)
             SNDRCVF    RCDFMT(FMT01)
             IF         COND(&IN03) THEN(LEAVE)
             enddo


endpgm

L’écran

     A*
     A                                      DSPSIZ(24 80 *DS3)
     A                                      CA03(03)
     A          R FMT01
     A*%%TS  SD  20221129  091353  QSECOFR     REL-V7R4M0  5770-WDS
     A                                  1 20'Liste des commandes ATTN'
     A                                      DSPATR(HI)
     A                                  3  2' 1'
     A            CMD001        61A  O  3  6
     A                                  4  2' 2'
     A            CMD002        61A  O  4  6
     A                                  5  2' 3'
     A            CMD003        61A  O  5  6
     A                                  6  2' 4'
     A            CMD004        61A  O  6  6
     A                                  7  2' 5'
     A            CMD005        61A  O  7  6
     A                                  8  2' 6'
     A            CMD006        61A  O  8  6
     A                                  9  2' 7'
     A            CMD007        61A  O  9  6
     A                                 10  2'80'
     A            CMD008        61A  O 10  6
     A                                 11  2'90'
     A            CMD009        61A  O 11  6
     A                                 22  4'F3=Exit'

Vous l’avez compris l’information se trouve dans le message CPX2313 .

Donc pour customiser votre menu ATTN, il vous suffit de faire un changement sur ce message, attention c’est pour tous les utilisateurs de votre système …

On rencontre parfois ces 2 customisations que je vous ai mis dans un programme CLP

pgm
/*-------------------------------------*/
/* Customisation du menu ATTN          */
/* 2 on remplace DSPJOB par WRKJOB     */
/* 4 on remplace SNDMSG par SNDSMTPEMM */
/*-------------------------------------*/
dcl &msg *char 100
             RTVMSG     MSGID(CPX2313) MSGF(QCPFMSG) MSG(&MSG)
             chgvar         %SST(&MSG 12 10)  'WRKJOB'
             chgvar         %sst(&msg 34 10)  'SNDSMTPEMM'
             CHGMSGD    MSGID(CPX2313) MSGF(QCPFMSG) MSG(&MSG)
endpgm

Vous pourrez désormais gérer vos travaux et envoyer un mail (si tout est paramétré chez vous)

, La valeur de sécurité QSECURITY

La valeur de sécurité QSECURITY

La valeur QSECURITY permet de connaître le niveau de sécurité appliqué au système.

Il y a 5 niveaux de sécurité, 10 – 20 – 30 – 40 – 50.

10 étant la sécurité la plus faible et 50 la plus élevé.

Pour connaître la valeur du niveau de sécurité de votre système, vous devez vous rendre dans les valeurs système en utilisant la commande WRKSYSVAL.

Au vu du nombre de valeurs de notre système, on va trier par sous-ensemble.

On va rentrer *SEC pour aller voir les valeurs système de sécurité.

On affiche ensuite la valeur système QSECURITY.

Sur la ligne rouge on peut voir le niveau de sécurité de notre système.

Dans le carré bleu, on peut voir les 5 niveaux de sécurité avec une courte description.

Depuis la version V7R5, le niveau de sécurité minimal est de 30.

Tableau des valeurs de QSECURITY depuis la V7R5

IBM recommande le niveau de sécurité 40 en raison des vulnérabilités trouvées au niveau 30.

La plupart des entreprises possédant un IBM i travaillent avec un niveau de sécurité 40.

Seuls les services financiers et quelques autres entreprises utilisent le niveau de sécurité 50 pour se conformer aux normes de défense américaine.

Voyons maintenant en détails, les 5 niveaux de sécurité :

Niveau 10 Pas de mot de passe requis, des profils sont créés à chaque fois qu’un utilisateur essaie de se connecter. Les utilisateurs créés ont accès à tout car l’autorité *ALLOBJ leur est attribuée automatiquement. Ce niveau n’est plus entretenu par IBM.

Niveau 20 → À ce niveau l’autorité *ALLOBJ est toujours attribuée à chaque utilisateur. En plus du niveau 10, un identifiant et un mot de passe sont nécessaires pour se connecter. Seul un profil *SECADM peut créer des nouveaux profils utilisateur. Ce niveau n’est plus admis depuis la V7R5.

Niveau 30 → En plus du niveau 20, ce niveau peut gérer les autorisations des utilisateurs au cas par cas. Les profils ayant l’autorité *ALLOBJ sont forcément créés avec la classe de sécurité *SECOFR, les autres n’ont pas cette autorité.

Niveau 40 → Protection de l’intégrité du système d’exploitation. Signature et sécurité des ressources. Le système empêche les tentatives d’appeler directement des programmes système non reconnus.

Niveau 50 → Protection renforcée de l’intégrité du système d’exploitation. Signature et sécurité des ressources. Avant de passer à ce niveau, la mise en place du journal d’audit est obligatoire. Ce niveau a été créé pour répondre à la norme C2 (Norme de département de défense Américain). Une meilleure protection des blocs de contrôle interne est appliquée.

, , Droits et génération SQL script

Vous devez moderniser votre base de données, pour cela vous pouvez commencer par extraire le source de votre PF,
par exemple en passant par Générations d’instructions SQL dans ACS , ou en utilisant la procédure SQL de QSYS2 GENERATE_SQL Ou GENERATE_SQL_OBJECT ,


La plus part du temps on obtient un scripte SQL qui vous permettra de générer votre nouvelle table , ici un exemple ou on a enlevé les commentaires.

Que ce passe t’il au niveau des droits ?

Avant par DSPOBJAUT

par DROITS dans ACS

La liste d’autorisations

Premier effet vous pouvez avoir des différences sur les droits publics

exemple ici

Après

Vous vous retrouvez avec un droit USER DEF au lieu de *CHANGE et vous avez perdu le droit exécute, on est d’accord ca ne change rien sur une table, c’est juste un peu moins lisible quand on analyse au niveau du système

le plus gênant c’est la liste d’autorisation que vous perdez
et la cela peux changer complètement puisque vous perdez 1 voir 2 niveaux de recherches

Dans notre cas FORM06 se retrouve avec des droits *PUBLIC

Conclusion :


Après avoir modernisé vos tables, vous devez réappliquer vos droits le plus simple est de généré un objet de référence

une autre solution est de vous affranchir des listes d’autorisation qui ne sont pas générées dans SQL

, , , Tuez les jobs SSH dans QINTER

Si vous commencez à faire du SSH (en tant que client), par exemple pour faire du GitHub, vous avez des jobs qui vont rester et que vous devrez tuer par un kill ou un ENDJOB.

C’est des jobs BCI qui restent après avoir arrêté vos JOBs interactifs.

Voici un petit script CLP qui permet de faire ça, il vous suffit de le planifier tous les soirs à 23 heures par exemples.

Ici on a utiliser la fonction table QSYS2.ACTIVE_JOB_INFO et la fonction QSYS2.QCMDEXC et on packagé le tout dans un CLP …

PGM
/* Suppression des jobs SSH dans QINTER */
dcl &NBRCURRCD *dec 10
/*  Exécution de la requete SQL */
             RUNSQL     SQL('Create table qtemp/sortie as(SELECT +
                          QSYS2.QCMDEXC(''ENDJOB '' CONCAT +
                          JOB_NAME)  as resultat FROM +
                          TABLE(QSYS2.ACTIVE_JOB_INFO(SUBSYSTEM_LIST_+
                          FILTER => ''QINTER'')) X  where job_type +
                          = ''BCI'') with data') COMMIT(*NONE)
             MONMSG     MSGID(SQL0000) exec(do)
             SNDUSRMSG  MSG('Erreur dans l''exécution de la requête +
                          SQL d''épuration des jobs SSH') +
                          MSGTYPE(*INFO)
             Return
             enddo
  /* Envoi d'un message de fin */
             RTVMBRD    FILE(QTEMP/SORTIE) NBRCURRCD(&NBRCURRCD)
             if cond(&NBRCURRCD > 0) then(do)
             SNDUSRMSG  MSG(%CHAR(&NBRCURRCD) *TCAT ', job(s) SSH +
                          arrêté(s)') MSGTYPE(*INFO)
             enddo
             else do
             SNDUSRMSG  MSG('Pas de Job SSH à Supprimer ') +
                          MSGTYPE(*INFO)
             enddo
ENDPGM

Sur les job de ssh-agent vous pouvez demander le kill directement comme ceci, merci à Julien …

La commande QSH lancer par votre CL qui vous permet de soumettre se présente donc comme ça :

eval "$(ssh-agent -s)" ; // Démarrage de l'agent 
SSH ssh-add /home/jl/.ssh/github ; // Ajout de la clé à l'agent 
ssh -T git@github.com ; // Test de connexion à GitHub 
ssh-agent -k // Arrêter l'agent
, , Droits SQL sur les Zones

On m’a récemment demandé comment savoir si un fichier était couvert par des droits SQL sur les zones

J’ai d’abord pensé que la fonction table QSYS2.OBJECT_PRIVILEGES allait me rendre ce service !

donc j’ai lancé cette requête pour analyser mon fichier

Exemple :

SELECT *
FROM TABLE(QSYS2.OBJECT_PRIVILEGES(‘MABASESQL’, ‘CLIENTS’, ‘*FILE’));

et je n’ai pas trouvé l’information dans les zones renvoyées

puis j’ai essayé avec les commandes historiques en sortie de fichier ici DSPOBJAUT

Exemple :

DSPOBJAUT OBJ(MABASESQL/CLIENTS)
OBJTYPE(FILE) OUTPUT(OUTFILE)
OUTFILE(MABASESQL/LSTAUT)

Select * from MABASESQL.lstaut

et la on a le résultat qui l’indique
ZONE OAOBJA à ‘USER DEF’ et une ZONE des DATA ici OAUPD à ‘/’

En résumé

On ne sait pas par SQL, mais on sait faire par une sortie fichier historique


Oui je sais , on peut trouver l’information par des vues spécifiques dans QSYS2 et SYSIBM

Vous avez d’abord la vue SYSIBM.SQLCOLPRIVILEGES mais attention, vous avez toutes les autorisations sur la zone y compris celles qui correspondent à un *CHANGE sur le fichier par exemple.


exemple :

Select * from SYSIBM.SQLCOLPRIVILEGES
where TABLE_SCHEM = ‘MABASESQL’ and TABLE_NAME = ‘CLIENTS’

order by column_name, grantee


Ou mieux la vue QSYS2.COLUMN_PRIVILEGES qui ne contient que les zones avec des autorisations spécifiques

exemple

Select * from QSYS2.COLUMN_PRIVILEGES
where TABLE_SCHEMA = ‘MABASESQL’ and TABLE_NAME = ‘CLIENTS’

j’ai donc fait une RFE pour avoir l’information dans la fonction table QSYS2.OBJECT_PRIVILEGES

Si vous êtes intéressés votez pour moi c’est ici https://2e4ccba981d63ef83a875dad7396c9a0.ideas.aha.io/ideas/RIRP-I-1260

Je vous indiquerais la réponse d’IBM sur le sujet

, EXIT PGM sur PWRDWNSYS

Dans les programmes d’exit, il en existe un qui s’exécutera avant un PWRDWNSYS


Pour 2 raisons


-1 Pour faire un contrôle par rapport à une condition particulière de lancement
-2 Pour fermer des applications et valider des informations en cours

Voici un exemple succinct qu’il vous faudra améliorer

Ici on demande juste de valider par un Y que l’IPL est bien voulu

La commande pour ajouter le point d’exit est au début du programme

PGM                                                                    
/* association pour associer le programme */                           
/*ADDEXITPGM EXITPNT(QIBM_QWC_PWRDWNSYS)  */                           
/* FORMAT(PWRD0100)                       */                           
/* PGMNBR(1)                              */                           
/* PGM(GDATA/PPWRDWN)                     */                           
DCL VAR(&RPY) TYPE(*CHAR) LEN(1)                                       
dcl VAR(&USR) TYPE(*CHAR) LEN(10)                                      
Rtvjoba USER(&USR)                                                     
             SNDUSRMSG  MSGID(CPF9897) MSGF(QCPFMSG) +                 
                          MSGDTA('PWRDWNSYS demandé par, ' *BCAT +     
                          &USER *BCAT 'Tapez pour valider.') +         
                          VALUES('Y' 'N') DFT('N') TOUSR(QSYSOPR) +    
                          MSGRPY(&RPY)                               
/* Si non on arrête le travail */                                      
if (&RPY *NE 'Y') then(do) 
             SNDUSRMSG  MSGID(CPF9897) MSGF(QCPFMSG) MSGDTA('****  +   
                          PWRDWNSYS annulé ****') MSGTYPE(*INFO) +     
                          TOUSR(QSYSOPR)                               
              ENDJOB     JOB(*) OPTION(*IMMED)      
ENDDO                                              
 /* Si oui faire les traitements de fermeture    */    
 /*  pour les applications sensibles par exemple */
 /* libérer des unités etc ...                   */  
 endpgm  

Remarque

Vous pouvez tester même le type d’ipl ou la zone de redémarrage en utilisant les paramètres que vous pouvez recevoir dans la zone au format PWRD0100 ou
PWRD0100.
Attention après sa mise en œuvre, il sera appelé à chaque lancement de PWRDWNSYS pour l’enlever

RMVXITPGM EXITPNT(QIBM_QWC_PWRDWNSYS) FORMAT(PWRD0100) PGMNBR(1)

Plus d’informations ici
https://www.ibm.com/docs/en/i/7.4?topic=ssw_ibm_i_74/apis/xpwrdwn.htm

C’est le nombre de tentatives de connexions pour un profil


Vous pouvez désormais depuis la version 7.5 l’indiquer au niveau du profil et non plus de manière global par la valeurs système QMAXSIGN pas de *NOMAX 25 max

Pour connaitre le nombre de tentatives par profil sur votre ibmi

SELECT AUTHORIZATION_NAME, (CASE MAXIMUM_SIGN_ON_ATTEMPTS
WHEN ‘*SYSVAL’ THEN (select CURRENT_CHARACTER_VALUE
from QSYS2.SYSTEM_VALUE_INFO
where SYSTEM_VALUE_NAME = ‘QMAXSIGN’)
ELSE MAXIMUM_SIGN_ON_ATTEMPTS
END) AS MAXIMUM_SIGN_ON_ATTEMPTS
FROM qsys2.user_info

.

Conseil

Continuez à gérer le plus grand nombre par QMAXSIGN et les exceptions au niveau des profils spécifiques