Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

253
Views
Shuffle Password String para limitar los caracteres consecutivos de la misma clase

Entonces, trabajando en TI, tenemos el requisito de generar contraseñas seguras casi todo el tiempo, bueno, ciertas organizaciones agregan requisitos más estrictos más allá de la cantidad de clases de caracteres requeridas y los requisitos de longitud. Una de esas organizaciones para las que trabajo también limita la cantidad de caracteres de una sola clase de caracteres (minúsculas, mayúsculas, caracteres especiales, números) que pueden aparecer consecutivamente. He creado una función que facilita esto, sin embargo, esencialmente solo fuerza bruta la contraseña, lo cual es bastante terrible. ¿Cómo abordaría este problema en particular desde una perspectiva informática sabiendo que la velocidad es crítica mientras se mantiene la aleatoriedad?

Siento que debería haber algún tipo de técnica de reproducción aleatoria que pueda implementar, pero cada solución que se me ocurre es demasiado lenta o reduce la aleatoriedad de la cadena.

 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 }
over 4 years ago · Santiago Trujillo
2 answers
Answer question

0

¿Cómo abordaría este problema en particular desde una perspectiva informática sabiendo que la velocidad es crítica mientras se mantiene la aleatoriedad?

Antes de continuar, debo señalar que estas propiedades (cumplimiento de la política descrita versus mantenimiento de la aleatoriedad/entropía) son mutuamente excluyentes: no se puede "mantener la aleatoriedad" al "corregir" cuidadosamente la distribución de la salida de un PRNG.

Yo dividiría el problema en dos funciones separadas:

  • Test-PasswordCharSequence : para validar rápidamente si una cadena de contraseña dada cumple con la política
  • Shuffle-PasswordCharSequence : para mezclar aleatoriamente los caracteres en cualquier contraseña una vez

La atomización de estas operaciones centrales debería facilitar el ajuste/refactorización.

Para la función de validación, podría ser tentador usar una expresión regular, pero sugeriría simplemente iterar sobre la cadena y realizar un seguimiento de los caracteres consecutivos de la misma clase.

 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 }

Entonces necesitamos una función para mezclar la contraseña. El algoritmo de barajado verdaderamente aleatorio (nuevamente, dependiendo del RNG utilizado) más eficiente que conozco es el algoritmo de barajado in situ de Fisher-Yates , que se puede implementar de la siguiente manera:

 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) }

Para usarlos junto con su función New-Password existente:

 # 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"
over 4 years ago · Santiago Trujillo Report

0

Creo que lo que @vonPryz mencionó en su comentario " ¿Por qué no construir la contraseña carácter por carácter? " es realmente posible y probablemente la forma más rápida de hacerlo.
El punto es que debe crear la contraseña en 2 etapas, primero cree una lista de complejidad con los conjuntos de caracteres (en lugar de los caracteres finales) y luego, en la siguiente etapa, seleccione el carácter en cuestión para el conjunto de caracteres en esa posición. Si se alcanza el $MaxConsecutiveChar , elija un nuevo carácter del conjunto de caracteres en esa posición:

 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

Para probar que -MaxConsecutiveChar funciona y es rápido y silencioso:

 $Params = @{ MaxConsecutiveChar = 1 LowerCase = 'ab' UpperCase = 'ab' Numbers = 'ab' SpecialCharacters = 'ab' } New-Password @Params babababababababababababababababababababababababababababababababa
over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!