Insufficient Complexity
Let's pretend you have a secret password, "bad".
You calculate the SHA256 of this password and get "2f05d4b689d270cafb02285f35f44866f7dc8a2d368a3f9d1124373eeab31fb1".
If you ALSO tell me that it's the hash of a password that is less than 4 lowercase characters, I can obtain your password in seconds by brute forcing. I only need to try 26^4 combinations for a total of 456976 sha256 hashes to exhaust all possibilities.
Give it a try yourself: https://ideone.com/hSWQ8R
from hashlib import sha256
import string
secret_hash = "2f05d4b689d270cafb02285f35f44866f7dc8a2d368a3f9d1124373eeab31fb1"
def pass_generator(max_length, charset):
for i in range(len(charset)**max_length):
yield get_pass(i, charset)
def get_pass(num, charset):
password = ""
while num > len(charset) - 1:
num, index = divmod(num, len(charset))
password += charset[index]
password += charset[num]
return password
for password in pass_generator(4, string.ascii_lowercase):
if sha256(password.encode()).hexdigest() == secret_hash:
print(password)
Common Passwords
Let's suppose you instead have the secret password, "123456".
If I do a quick google search and get a list of the 10,000 most common passwords, I can search fewer, potentially more complex passwords and in this case yours is definitely in there.
Common Patterns on Dictionary Words
Ok, ok, maybe you use "Sm@11Fry!"
Well, crackers have tools to generate and test possible passwords based on patterns, with common letter/symbol/number substitutions and suffixes. It would definitely take longer than the last two, but it's still possible that a generator would come up with this based on manipulating dictionary words with masks.
Insufficient Complexity, Space-Time Tradeoff Version
Ok, so how about "a#g%6^NN!"
Suppose that over many, many years I've used brute force to hash many, many different password-sized strings, including enough symbols to cover what you have here. Now suppose I find a data structure that would let me compactly save the relationship between the hash and the password. So now if I can look up your hash in this data structure, I'm essentially getting several years of brute forcing almost for free (yes, someone had to do the hashing originally). This is called a rainbow table and it's a fascinating concept I encourage you to read up on.
To limit the effectiveness of rainbow tables, you can salt your secret: generate some random bytes, and append them to your password before hashing.
Now my existing rainbow tables are useless - I'd have to recalculate them from scratch for your exact salt (assuming I could obtain it). This is an even bigger deal when you recognize that salts are typically per-user in a real authentication scheme, so an attacker who obtains the whole table of hashed user creds and passwords and salts doesn't get any reusable value from brute forcing the password space for just one of the salts.
Your Case
Suppose I had a yaml like so, and I hash it. Later I use sops to encrypt the secret string "bad", and the hash and the sopsified yaml are available to the cracker:
apiVersion: v1
kind: Secret
metadata:
name: my-cool-secret
type: Opaque
stringData:
bad
I could brute force this just as easily as the password example above. I assume I would know the exact structure of the yaml, so the only variable part is the stringData, and if I expect it to be all lower case and very short, it's the same complexity of brute force.
Try it for yourself: https://ideone.com/P0FNg6
from hashlib import sha256
import string
template = """apiVersion: v1
kind: Secret
metadata:
name: my-cool-secret
type: Opaque
stringData:
{}"""
secret_hash = "2f97f7393a589fff32db98e5edf54a455937516f567b0d352556537fcb06aa53"
def pass_generator(max_length, charset):
for i in range(len(charset)**max_length):
yield get_pass(i, charset)
def get_pass(num, charset):
password = ""
while num > len(charset) - 1:
num, index = divmod(num, len(charset))
password += charset[index]
password += charset[num]
return password
for password in pass_generator(4, string.ascii_lowercase):
if sha256(template.format(password).encode()).hexdigest() == secret_hash:
print(password)
All of the same cracking principles apply as with the simpler examples above, except that multiple secrets extend the total complexity.