因此,在 IT 領域作業,我們幾乎一直都需要生成安全密碼,某些組織除了所需的字符類數量和長度要求之外,還增加了更嚴格的要求。我作業的一個這樣的組織也限制了可以連續出現的單個字符類(小寫、大寫、特殊字符、數字)的字符數。我已經構建了一個確實有助于實作這一點的函式,但是,它本質上只是暴力破解密碼,這非常糟糕。你會如何從計算機科學的角度來解決這個特定的問題,知道速度是關鍵,同時保持隨機性。
我覺得應該有某種我可以實作的 shuffle 技術,但是我想出的每個解決方案要么太慢,要么降低了字串的隨機性。
Function New-Password {
PARAM(
[Int]$PasswordLength = 64,
[Int]$MinUpperCase = 5,
[Int]$MinLowerCase = 5,
[Int]$MinSpecialCharacters = 5,
[Int]$MinNumbers = 5,
[Int]$ConsecutiveCharClass = 0,
[Int]$ConsecutiveCharCheckCount = 1000,
[String]$LowerCase = 'abcdefghiklmnoprstuvwxyz',
[String]$UpperCase = 'ABCDEFGHKLMNOPRSTUVWXYZ',
[String]$Numbers = '1234567890',
[String]$SpecialCharacters = '!"$%&/()=?}][{@#* ',
[String]$PasswordProfile = '',
#Advanced Options
[Bool]$EnhancedEntrophy = $True
)
If ([String]::IsNullOrEmpty($PasswordProfile) -eq $False) {
#You can define custom password profiles here for easy reference later on.
New-Variable -Force -Name:'PasswordProfiles' -Value:@{
'iDrac' = [PSCustomObject]@{PasswordLength=20;SpecialCharacters=" &?>-}|.!(',_[`"@#)*;$]/§%=<:{@";}
}
If ($PasswordProfile -in $PasswordProfiles.Keys) {
$PasswordProfiles[$PasswordProfile] |Get-Member -MemberType NoteProperty |ForEach-Object {
Set-Variable -Name $_.name -Value $PasswordProfiles[$PasswordProfile].($_.name)
}
}
}
New-Variable -Force -Name:'PassBldr' -Value @{}
New-Variable -Force -Name:'CharacterClass' -Value:([String]::Empty)
ForEach ($CharacterClass in @("UpperCase","LowerCase","SpecialCharacters","Numbers")) {
$Characters = (Get-Variable -Name:$CharacterClass -ValueOnly)
If ($Characters.Length -gt 0) {
$PassBldr[$CharacterClass] = [PSCustomObject]@{
Min = (Get-Variable -Name:"min$CharacterClass" -ValueOnly);
Characters = $Characters
Length = $Characters.length
}
}
}
#Sanity Check(s)
$MinimumChars = $MinUpperCase $MinLowerCase $MinSpecialCharacters $MinNumbers
If ($MinimumChars -gt $PasswordLength) {
Write-Error -Message:"Specified number of minimum characters ($MinimumChars) is greater than password length ($PasswordLength)."
Return
}
#New-Variable -Force -Name:'Random' -Value:(New-Object -TypeName:'System.Random')
New-Variable -Force -Name:'Randomizer' -Value:$Null
New-Variable -Force -Name:'Random' -Value:([ScriptBlock]::Create({
Param([Int]$Max=[Int32]::MaxValue,[Int32]$Min=1)
if ($Min -gt $Max) {
Write-Warning "[$($myinvocation.ScriptLineNumber)] Min ($Min) must be less than Max ($Max)."
return -1
}
if ($EnhancedEntrophy) {
if ($Randomizer -eq $Null) {
Set-Variable -Name:'Randomizer' -Value:(New-Object -TypeName:'System.Security.Cryptography.RNGCryptoServiceProvider') -Scope:1
}
#initialize everything
$Difference=$Max-$Min
[Byte[]] $bytes = 1..4 #4 byte array for int32/uint32
#generate the number
$Randomizer.getbytes($bytes)
$Number = [System.BitConverter]::ToUInt32(($bytes),0)
return ([Int32]($Number % $Difference $Min))
} Else {
if ($Randomizer -eq $Null) {
Set-Variable -Name:'Randomizer' -Value:(New-Object -TypeName:'System.Random') -Scope:1
}
return ([Int]$Randomizer.Next($Min,$Max))
}
}))
$GetString = [ScriptBlock]::Create({
Param([Int]$Length,[String]$Characters)
Return ([String]$Characters[(1..$Length |ForEach-Object {& $Random $Characters.length})] -replace " ","")
})
$CreatePassword = [scriptblock]::Create({
New-Variable -Name Password -Value ([System.Text.StringBuilder]::new()) -Force
#Meet the minimum requirements for each character class
ForEach ($CharacterClass in $PassBldr.Values) {
If ($CharacterClass.Min -gt 0) {
$Null = $Password.Append([string](Invoke-Command $GetString -ArgumentList $CharacterClass.Min,$CharacterClass.Characters))
}
}
#Now meet the minimum length requirements.
If ([Int]($PasswordLength-$Password.length) -gt 0) {
$Null = $Password.Append((Invoke-Command $GetString -ArgumentList ($PasswordLength-$Password.length),($PassBldr.Values.Characters -join "")))
}
return (([Char[]]$Password.ToString() | Get-Random -Count $Password.Length) -join "")
})
Switch ([Int]$ConsecutiveCharClass) {
'0' { New-Variable -Name NewPassword -Value (& $CreatePassword) -Force }
{$_ -gt 0} {
New-Variable -Name CheckPass -Value $False -Force
New-Variable -Name CheckCount -Value ([Int]0) -Force
For ($I=0; $I -le $ConsecutiveCharCheckCount -and $CheckPass -eq $False; $I ) {
New-Variable -Name NewPassword -Value (& $CreatePassword) -Force
$TestPassed = 0
ForEach ($CharClass in $PassBldr.Values) {
IF ([Regex]::IsMatch([Regex]::Escape($NewPassword),"[$([Regex]::Escape($CharClass.Characters))]{$ConsecutiveCharClass}") -eq $False) {
$TestPassed
}
}
if ($TestPassed -eq $CheckClasses.Count) {
$CheckPass = $True
}
}
}
Default {Write-Warning -Message "This shouldn't be possible, how did you get here?!"}
}
Return $NewPassword
}
uj5u.com熱心網友回復:
你會如何從計算機科學的角度來解決這個特定的問題,知道速度是關鍵,同時保持隨機性。
在進一步討論之前,我應該注意這些屬性(符合所描述的策略與保持隨機性/熵)是相互排斥的 - 您不能通過仔細“糾正”來自 PRNG 的輸出分布來“保持隨機性”。
我會將問題分成兩個獨立的功能:
Test-PasswordCharSequence- 快速驗證給定的密碼字串是否符合策略Shuffle-PasswordCharSequence- 隨機打亂任何給定密碼中的字符一次
原子化這些核心操作應該更容易調整/重構。
對于驗證函式,使用正則運算式可能很誘人——但我建議簡單地遍歷字串并跟蹤同一類的連續字符。
function Test-PasswordCharSequence {
param(
[string]
$String,
[System.Collections.IDictionary]
$CharacterMap,
[int]$Limit = 5
)
# Keep tracking the last seen character class and length of consecutive sequence
$currentClass = ""
$counter = 0
foreach($char in $String.ToCharArray())
{
if($CharacterMap.ContainsKey($char) -and $CharacterMap[$char] -eq $currentClass)
{
$counter
}
else
{
$counter = 1
$currentClass = $CharacterMap[$char]
}
# if we've seen the same class for too many consecutive characters, fail
if($counter -gt $Limit){
return $false
}
}
# No sequence over limit observed
return $true
}
然后我們需要一個函式來洗牌密碼。我所知道的最有效的真正隨機(同樣取決于所使用的 RNG)混洗演算法是就地 Fisher-Yates 混洗演算法,可以按如下方式實作:
function Shuffle-PasswordCharSequence
{
param(
[Parameter(Mandatory)]
[string]$String
)
$chars = $String.ToCharArray()
$max = $chars.Length
#Fisher-Yates Left to Right
for($i = 0; $i -lt $max - 1; $i )
{
$j = Get-Random -Minimum 0 -Maximum ($max - $i)
$chars[$j],$chars[$i $j] = $chars[$i $j],$chars[$j]
}
return [string]::new($chars)
}
要將這些與您現有的New-Password功能結合使用:
# define character classes to use
$CharacterClasses = @{
LowerCase = 'abcdefghiklmnoprstuvwxyz'
UpperCase = 'ABCDEFGHKLMNOPRSTUVWXYZ'
Numbers = '1234567890'
SpecialCharacters = '!"$%&/()=?}][{@#* '
}
# generate inverse character map for the validation function
# we use [Dictionary[char,string]] rather than [hashtable] to ensure case-sensitive handling of keys ('b' vs 'B')
$classMap = [System.Collections.Generic.Dictionary[char,string]]::new()
foreach($entry in $CharacterClasses.GetEnumerator())
{
foreach($char in $entry.Value.ToCharArray())
{
$classMap[$char] = $entry.Name
}
}
# generate initial password
$passwordCandidate = New-Password -PasswordLength 127 @CharacterClasses
# validate generated password, shuffle until successful
$shuffleCount = 0
while(!(Test-PasswordCharSequence $passwordCandidate -CharacterMap $classMap)){
$passwordCandidate = Shuffle-PasswordCharSequence $passwordCandidate
$shuffleCount
}
Write-Host "Generated valid password after ${shuffleCount} shuffles"
uj5u.com熱心網友回復:
我認為@vonPryz在他的評論中提到的“為什么不逐個字符地構建密碼? ”實際上是可能的,并且可能是最快的方法。
關鍵是您必須分兩個階段創建密碼,首先使用字符集(而不是最終字符)構建復雜性串列,然后在下一階段為該位置的字符集選擇相關字符。如果$MaxConsecutiveChar達到,則從該位置的字符集中選擇一個新字符:
Function New-Password {
Param(
[Int]$PasswordLength = 64,
[Int]$MinUpperCase = 5,
[Int]$MinLowerCase = 5,
[Int]$MinSpecialCharacters = 5,
[Int]$MinNumbers = 5,
[Int]$MaxConsecutiveChar = 3,
[String]$LowerCase = 'abcdefghiklmnoprstuvwxyz',
[String]$UpperCase = 'ABCDEFGHKLMNOPRSTUVWXYZ',
[String]$Numbers = '1234567890',
[String]$SpecialCharacters = '!"$%&/()=?}][{@#* '
)
enum CharSet {
LowerCase
UpperCase
SpecialCharacters
Numbers
}
$CharSets = [system.collections.generic.dictionary[CharSet, Char[]]]::new()
$MinSetChars = [system.collections.generic.dictionary[CharSet, Int]]::new()
$MinimumChars = 0
[CharSet].GetEnumNames().ForEach{
$CharSets[$_] = (Get-Variable -ValueOnly -Name $_).ToCharArray()
$MinChar = [Int](Get-Variable -ValueOnly -Name "Min$_")
$MinSetChars[$_] = $MinChar
$MinimumChars = $MinChar
}
If ($MinimumChars -gt $PasswordLength) {
Throw "Specified number of minimum characters ($MinimumChars) is greater than password length ($PasswordLength)."
}
# Build a list of characters sets
$SetList = for ($i = 0; $i -lt $PasswordLength; $i ) { $CharSets.Keys |Get-Random }
# Insert the Min* required characters for the specific sets
# Making sure that the position is not already taken by another Min* characterset
$Used = [System.Collections.Generic.HashSet[int]]::New()
$CharSets.Keys.ForEach{
for ($i = 0; $i -lt $MinSetChars[$_]) {
$At = Get-Random $PasswordLength
if (!$Used.Contains[$At]) {
$SetList[$At] = $_
$Null = $Used.Add($At)
$i
}
}
}
# Elect a character for each set
$LastChar = $Null
$ConsecutiveChars = 1
-Join $SetList.ForEach{
$Char = $CharSets[$_] |Get-Random
# Check the consecutive characters (choose another when required)
While ($ConsecutiveChars -ge $MaxConsecutiveChar -and $Char -eq $LastChar) { $Char = $CharSets[$_] |Get-Random }
$ConsecutiveChars = if ($Char -eq $LastChar) { $ConsecutiveChars 1 } else { 1 }
$LastChar = $Char
$Char
}
}
New-Password
X23@[X0C5%FL3Demyf5?5})f]5Kt#usC#m1 3?T(NOb4DmYsX8FA3pF46OUZeW3V
為了證明-MaxConsecutiveChar有效并且安靜快速:
$Params = @{
MaxConsecutiveChar = 1
LowerCase = 'ab'
UpperCase = 'ab'
Numbers = 'ab'
SpecialCharacters = 'ab'
}
New-Password @Params
babababababababababababababababababababababababababababababababa
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/402172.html
標籤:。网 电源外壳 powershell-3.0 powershell-4.0
上一篇:我如何自動設定[L]不是全部?
