What are the internals of Pythons str.join()? (Hiding passwords from output) -
i stumbled upon interesting(?) way hide passwords (and other personal data) general output screen logfiles.
in book how make mistakes in python mike pirnat suggests implement class sensitive strings , overload __str__- , __repr__-methods.
i experimented , got this:
class secret(str): def __init__(self, s): self.string = s def __repr__(self): return "'" + "r"*len(self.string) + "'" def __str__(self): return "s" * len(self.string) def __add__(self, other): return str.__add__(self.__str__(), other) def __radd__(self, other): return str.__add__(other, self.__str__()) def __getslice__(self, i, j): return ("x"*len(self.string))[i:j] (i'm aware using len provides information content hide. it's testing.)
it works fine in cases:
pwd = secret("nothidden") print("the passwort " + pwd) # passwort sssssssss print(pwd + " passwort.") # sssssssss password. print("the passwort {}.".format(pwd)) # password sssssssss. print(["the", "passwort", "is", pwd]) # ['the', 'password', 'is', 'rrrrrrrrr'] print(pwd[:]) # xxxxxxxxx however not work:
print(" ".join(["the", "password", "is", pwd])) # password nothidden so, how str.join() work internally? method have overload obscure string?
the issue inheriting str, implements __new__ means though avoided calling parent constructor in class, underlying c object still initialized it.
now join checking if has str subclass and, being implemented in c, access directly underlying c structure, or uses other str-related function bypasses __str__ , __repr__ (think it: if value string or string subclass, why code call __str__ or __repr__ obtain value? accesses underlying character array in way!)
to fix this: not inherit str! unfortunately means not able use object string in situations, that's pretty inevitable.
an alternative may work implement __new__ , feed different value str's __new__ method:
class secret(str): def __new__(cls, initializer): return super(secret, cls).__new__(cls, 'x'*len(initializer)) def __init__(self, initializer): self.text = initializer def __repr__(self): return "'{}'".format("r"*len(self)) def __str__(self): return "s"*len(self) def __add__(self, other): return str(self) + other def __radd__(self, other): return other + str(self) which results in:
in [19]: pwd = secret('nothidden') in [20]: print("the passwort " + pwd) # passwort sssssssss ...: print(pwd + " passwort.") # sssssssss password. ...: ...: print("the passwort {}.".format(pwd)) # password sssssssss. ...: print(["the", "passwort", "is", pwd]) # ['the', 'password', 'is', 'rrrrrrrrr'] ...: print(pwd[:]) passwort sssssssss sssssssss passwort. passwort sssssssss. ['the', 'passwort', 'is', 'rrrrrrrrr'] xxxxxxxxx in [21]: print(" ".join(["the", "password", "is", pwd])) password xxxxxxxxx however fail see how useful. mean: purpose of class avoid programming errors end display sensitive information? having exception being triggered better can identify bugs! it's best raise notimplementederror inside __str__ , __repr__ instead of silently provided useless value... sure don't leak secret finding bugs becomes hard.
Comments
Post a Comment