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.