0

I need to use base64 encoded credentials for an API call. From a web UI interface for the API I have gotten the credentials in base64 encoded form. For the sake of showing my issue I have saved the credentials to a file creds and the base64 encoded credentials from the web ui in a file creds64.

Then, running the script below:

#!/bin/bash

CREDS=$(cat creds)
BASE64_CREDS=$(cat creds64)

if [[ $CREDS == $(base64 -d creds64) ]]; then
  echo "the original credentials were the same as the decoded base64 credentials"
else
  echo "the original credentials were NOT the same as the decoded base64 credentials"
fi

if [[ $(base64 creds) == $BASE64_CREDS ]]; then
  echo "the encoded original credentials were the same as the base64 credentials"
else
  echo "the encoded original credentials were NOT the same as the base64 credentials"
fi

The resulting output is

the original credentials were the same as the decoded base64 credentials
the encoded original credentials were NOT the same as the base64 credentials

I don't understand how the base64 decoded credentials can equal the "plain" credentials, but comparing the result of base64 creds with the already encoded credentials can be not equal.

What am I misunderstanding here?

Agryphos
  • 23
  • 1
  • 5

1 Answers1

3

If the raw binary data contains null bytes, the shell basically cannot handle it. Compare

bash$ var=$'\0hello'
bash$ echo "$var"

bash$

The lack of quoting is also problematic; see When to wrap quotes around a shell variable

Keeping the data in a variable is unnecessary anyway. The following should work robustly regardless of any null characters.

if base64 -d creds64 | cmp - creds; then
    :

Finally, as Robby Cornelissen notes in a comment, the base64 command probably adds line breaks to keep the lines in the output below 80 characters. Your web client's result might not have line breaks at all, or could have them at a different line width.

You can fix this by trimming any newlines anywhere:

if [[ "$(base64 creds | tr -d '\n') = "$(tr -d '\n' <creds64)" ]]; then
    :

The use of [[ is a Bashism, so not portable to POSIX sh. You can switch to [ instead if you need portability. Somewhat similarly, cmp might not support specifying - to mean standard input on all platforms; Bash lets you use /dev/stdin instead, or /dev/fd/0 on some platforms.

tripleee
  • 175,061
  • 34
  • 275
  • 318
  • I avoided quoting the variables since my credentials contains characters that I don't want the shell to process further than seeing it as a pure string literal. I did try to add quotes (and didn't get the error I thought I would, so yay) but still got the same result – Agryphos Jan 27 '23 at 09:43
  • 2
    @Agryphos That's exactly why you _want_ to quote the text! – tripleee Jan 27 '23 at 09:43
  • The lack of quoting is actually only an issue in a single case in OP's code: when comparing to `$(base64 -d creds64)`. All other cases are special cases that do not require quotes. This one case *does* require quotes to disable wildcard matching (`[[ $FOO = abc* ]]` performs wildcard matching, `[[ $FOO = "abc*" ]]` does not). In particular, you do *not* need quotes around an expansion in the RHS of an assignment, nor around an expansion in the LHS of `[[`. – Konrad Rudolph Jan 27 '23 at 09:50
  • Trimming line breaks before encoding fixed it, thank you – Agryphos Jan 27 '23 at 14:15