Overview
When you read a credential in a Syntasa notebook using synutils.credentials.read(...), the value comes back wrapped in a protected object — SecretString for a single value, SecretDict for a key/value pair such as {'Key': ..., 'Value': ...}.
By default, these objects display as [REDACTED] or ********** wherever they would normally be printed — in cell output, in print() statements, and in logs — so a credential is not accidentally exposed just by looking at it or letting it flow into an error message.
Redaction is a safeguard against a credential accidentally ending up somewhere it shouldn't — a stray print(), a debug log line, an error message. It is not a security boundary against a user who deliberately tries to extract the underlying value. Any user with legitimate access to read a credential in a notebook can recover its real value if they choose to, through normal, documented Python operations.
This is not a gap specific to Syntasa. Databricks documents the identical limitation for its own secrets feature: "Although Databricks attempts to redact secret values in notebook outputs, it is not possible to fully prevent these users from viewing secret contents."
The recommended protection, on both platforms, is the same: control who has permission to read the credential in the first place, rather than attempting to make the value un-extractable once someone already has access to it.
What redaction does cover
- Displaying the credential object directly (
x,print(x), an f-string likef"{x}") always shows the redacted placeholder, never the real value. - Logs and error messages that include the credential object show the redacted form.
What redaction does not cover
These protected objects are built on top of Python's own string and dictionary types. That makes them a drop-in replacement anywhere a plain string or dictionary is expected — for example, passing a credential straight into a boto3 or JDBC connection call without extra code. The trade-off is that any standard string or dictionary operation performed on the value returns the real, unredacted data.
This is expected, documented behavior, not a bug.
secret = synutils.credentials.read("my_credential")
secret # [REDACTED] — safe
print(secret) # [REDACTED] — safe
secret["Value"].split() # returns the real value split into words
secret["Value"][:4] # slicing returns real characters
secret["Value"] + "" # concatenation returns the real value
secret["Value"].encode() # encoding returns the real bytes
dict(secret) # converting to a plain dict reveals both fields
{**secret} # unpacking into a new dict reveals both fields
json.dumps(secret) # serializing to JSON reveals both fields
None of the above requires bypassing any protection — it is simply how Python string and dictionary subclassing works, and it is what makes these objects usable directly with SDKs and drivers without every caller needing special-case code.
Intentional, sanctioned access
If your code genuinely needs the raw value — for example, to hand it to an SDK method that doesn't accept the wrapped object directly — use the documented accessor methods rather than a workaround:
| Type | Accessors |
|---|---|
SecretString |
.get() or .unseal()
|
SecretDict |
.getAll() or .unseal()
|
These exist precisely so that legitimate programmatic use doesn't need to rely on the unintentional-reveal paths above.
Recommendation
Treat access to a credential-reading cell the same way you'd treat access to the credential itself. If a user should not be able to see a given credential's value, they should not have permission to read that credential.