Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

257
Vistas
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 Respuestas
Responde la pregunta

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 Denunciar

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 Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda