TP PowerShell – Génération de mot de passe pour le livre de A. PETITJEAN

TP autour des aléas en PowerShell, suite à un besoin de génération de mot de passe : on veut générer un mot de passe aléatoire pour la création de compte d’un utilisateur. Pour ça déjà on va se demander comment on génère un aléatoire en PowerShell, bien comme d’hab., il y a une cmd-let pour ça :    

PS C:\> Get-Random     
1493366162     
PS C:\> Get-Random -Minimum 0 -Maximum 26     
7     
PS C:\> Get-Random -Minimum 0 -Maximum 26     
4
PS C:\> Get-Random -Minimum 0 -Maximum 26     
17   
PS C:\> Get-Random -Minimum 0 -Maximum 26     
22     
PS C:\> > Get-Random -Minimum 0 -Maximum 26     
8

À moins que vous n’ayez oublié combien il y a de lettre dans l’alphabet, je pense que vous avez saisi l’idée…

Bon à partir de là, on peut convertir nos nombres en `char`,
D’abord, on doit compter jusqu’au nombre de caractères voulus dans notre chaîne, voir le bouquin d’Arnaud pour plus de détail, on va utiliser cette abréviation :

PS C:\> 1..12     
1     
2     
3         
4     
5     
6     
7     
8     
9     
10     
11     
12

Ensuite, je vous invite à vous munir d’une table ASCII pour comprendre la suite    :

PS C:\> 1..12 | ForEach {$passwd += [char](Get-Random -Minimum 33 -Maximum 127)}     
PS C:\> $passwd     
GgD*!~$-Ja~s

Alors là comme ça, ça pique un peu, mais on a bien un mot de passe complexe de 12 caractères en sortie, le détail :

[char](Get-Random -Minimum 33 -Maximum 127)}

=> Renvoi un caractère dont la valeur ASCII est entre 33 et 127 (voir table ASCII), ce qui correspond à une plage de caractères ‘Human-readable’

$passwd + [char](Get-Random -Minimum 33 -Maximum 127)}

=> Ici on concatène le char renvoyé dans une chaîne stockée dans la variable $passwd

1..12 | ForEach

=> Pour chaque élément du tableau « 1..12   » effectue l’opération suivante, c’est  une façon raccourcie (un peu « quick and dirty » mais très efficace) de dire :

for($i=0    ; $i –lt 12    ; $i= $i +1)

Et ça marche…


 

Sauf que, pour un mot de passe, il y a des règles de complexités à respecter (spéciaux, majuscules, minuscules), et là on ne respecte pas ces règles systématiquement…

* C’est le problème avec l’aléatoire, c’est qu’il est aléatoire…
« Merci captain Obvious »…

Bref, on va chercher à faire une vraie fonction de génération de mot de passe, et comme je suis en mode flemmard là :

je vous renvoie, encore, vers le bouquin d’Arnaud p. 503 (v3), partie 5.2.4 : Réalisation Générateur de mots de passe.


 

Mais bon, finalement, il n’est pas si bien que ça le générateur à Arnaud, on ne peut pas lui dire combien de caractères de chaque type on veut, mais juste s’il y en a ou pas de telle ou telle classe.
Du coup, j’en ai refait un… mieux

Function Get-SecurityRandomString {

Ici la documentation, au format reconnu en natif par PowerShell si vous faite un `get-help Get-SecurityRandomString`. La doc donc :

< #     
.SYNOPSIS     
Génère un texte aléatoire, par exemple, comme générateur de mot de passe.     

.DESCRIPTION     
Renvoie une chaîne de caractères associée au tableau d'octets généré aléatoirement    

.INPUTS     
Length : la longueur de la chaîne souhaitée
Special : Nombre minimum de caractères spéciaux dans la chaine retournée.
lower : Nombre minimum de caractères en minuscule dans la chaine retournée.
Upper : Nombre minimum de caractères en majuscule dans la chaine retournée.
Digit : Nombre minimum de chiffre dans la chaine retournée.

.OUTPUTS      
La chaîne de caractères aléatoire de la longueur demandée respectant les critères minimum en entrée.      

.EXAMPLE      
PS C:\> Get-SecurityRandomString 26
}d+E?4/4ZEt?a2`4R'=TV$Q+G3

C:\PS > Get-SecurityRandomString -length 12 -Special 2 -lower 1 -Upper 3 -Digit 1
D+*-g\Gx6JmL      

.AUTHORS      
Moa      

.LASTUPDATE     
06/06/666     
#>

On signale également que les paramètres « standards » ( verbose, debug, etc…) sont supportés, et on déclare les paramètres de la fonction .

[ CmdletBinding () ]   
Param (     
[Parameter (Mandatory=$true, Position=1, ValueFromPipeline=$true)] [int] $length=12,   
[Parameter (Mandatory=$false, Position=2, ValueFromPipeline=$false)] [int] $Special=1,   
[Parameter (Mandatory=$false, Position=3, ValueFromPipeline=$false)] [int] $lower=1,   
[Parameter (Mandatory=$false, Position=4, ValueFromPipeline=$false)] [int] $upper=1,   
[Parameter (Mandatory=$false, Position=5, ValueFromPipeline=$false)] [int] $Digit=1   
)

La fonction est divisée en 3 blocs : begin, process et end.
Voir commentaires dans le code à partir de là :

Begin {     
    #exécuté une seul fois, quelle que soit le nombre d'éléments de l'entrée   

    #Initialisation des différentes variables        
    #Un fournisseur d'aléatoires du .NET.   
    $rnd = New-Object System.Security.Cryptography.RNGCryptoServiceProvider   
    #Nos ensembles de classes de caractères   
    $CharsPoolSpecial += ' & & (-)=[]{}+-*/$,;:?.\!_@#%'   
    $CharsPoolLower += 'abcdefghijklmnopqrstuvwxyz'   
    $CharsPoolUpper += 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'   
    $CharsPoolDigit += '0123456789'   
    
    #La classe de cractères par défaut : les minuscules.   
    $CharsPoolGlobal = $CharsPoolLower   
    #On ajoute ensuite si besoin les classes demandées en entrée   
    if ( $Special -gt 0 ){     
      $CharsPoolGlobal += $CharsPoolSpecial   
    }     
    if ( $Upper -gt 0 ){     
      $CharsPoolGlobal += $CharsPoolUpper   
    }     
    if ( $Digit -gt 0 ){     
      $CharsPoolGlobal += $CharsPoolDigit   
    }     
}     

Process{     
    #Pour chaque entrée du pipeline   
    #Initialisation   
    $result = ''   
    
    #taille minimum de la sortie:   
    $length = [Math]:: Max(($Special+$lower+$upper+$Digit),$length)     
    
    #Tableau d'octets   
    $bytes = New-Object System.Byte[] $length   
    $rnd . GetBytes( $bytes )     
    #Pour chaque classe de caractères, on pioche au hasard dans nos ensembles aléatoirement et on concatène à $Result   
    
    for ( $j = 0 ; $j -lt $Special ; $j ++ ){     
        #réduction à certains caractères : (cf. table ASCII)   
        $result += $CharsPoolSpecial [($bytes[$j]%$CharsPoolSpecial.length)]   
    }     
    for ( $j = 0 ; $j -lt $Lower ; $j++ ){     
        $result += $CharsPoolLower [($bytes[$j]%$CharsPoolLower.length)]   
    }     
    for ($j=0 ; $j -lt $Upper ; $j++ ){     
        $result += $CharsPoolUpper [($bytes[$j]%$CharsPoolUpper.length)]   
    }     
    for ( $j = 0 ; $j -lt $Digit ; $j++ ){     
        $result += $CharsPoolDigit [($bytes[$j]%$CharsPoolDigit.length)]   
    }     
    for ( $j = 0 ; $j -lt ($length-($Special+$Lower+$Upper+$Digit));$j++){     
        $result += $CharsPoolGlobal [ ( $bytes [ $j ] % $CharsPoolGlobal.length)]   
    }     

    #On mélange pour finir pour éviter l'effet spéciauxminusculesmajusculeschiffres   
    for ( $i = 0 ; $i -lt $length ; $i++ ){     
        $newpos = (( $i + (Get-Random -Maximum $length -Minimum 0 ))%$length)     
        $tmp = $result [$i]   
        $result = ($result.Remove($i,1)).Insert($i,$result[$newpos])     
        $result = ($result.Remove($newpos,1)).Insert($newpos,$tmp)     
    }     
  
    #Et on sort le résultat   
    $result ;     
}     


End{     
    #Une fois à la fin : Nettoyage   
    Clear-Variable 'result' , 'bytes' , 'rnd' , 'length' , 'Special' , 'lower' , 'upper' , 'Digit'   
 }     
}

Et donc là, pour ceux qui ont suivi, comment générer 24 mots de passe de longueur itératives d’un seul coup ?

PS C:\> 1..24 | Get-SecurityRandomString -Special 2 -lower 1 -Upper 3 -Digit 1     
zZ.3NZ)     
1N-hHU*     
0 & & uAU(W     
K3!nRN$     
]Q\YL9l     
HVUv1_-     
mRM_P6_     
?R+2RIiH     
}IMi#4+VW     
%MY,G9pNPo     
(+CV4k,kTcK     
yuM)Y?8Ct;rR     
6)GX#Zgf-5t*0     
;T(kYpPx)X)#3J     
I4cVu@U6(WJ5j#m     
JgQG4A$iNB7mYn+E     
*,C]*dDI?#-1Pn\FU     
j,3J9AYGTO-BIJHC/p     
YlJ9!o,qjU-iI=QBGRJ     
HcG3[x[-XRx2X#qVYYy8     
K:jbqw62QJ:BkNKuSHf-9     
t71@eU,\0K9khtjkTIS..:     
3WKF[{lQ5d!YlVeTEt[[dDt     
KL4#Q$DZPtN],\DyP$Yq438\

Et pour ceux qui se demandent à quoi ça sert de faire ça, c’est très simple :

PARCE QUE JE PEUX LE FAIRE !!!

Voilà, Arnaud n’aura plus qu’à modifier son livre avec ce chef-d’œuvre de code que je viens de lui d’écrire…

TP Bonus : modifier la fonction pour quel supporte un nombre min et max de type de caractère, classe par classe.
Exemple : je veux 6 spéciaux au moins mais pas plus de 8 spéciaux au total.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.