Using secrets for code and database credentials

Hi there

I am currently working with colleagues on a small webapp that is based on PHP and a database.

We have set up a workflow where every push to the master branch copies the repository to our test webserver.

Until now we have always worked with ZIP files and email - now that is automated and simplified but now we fail because the secrets do not work as we want them to.

We don’t want to store the real database accesses, but rather a variable that is replaced by the real credential when it is delivered to the web server, so that the web server gets a running webapp while there is no critical data in the repository.

Is there a way to automate this or do we have to upload manually again?

Thank you very much in advance!

1 Like

That’s what GitHub secrets are, aren’t they?

Yes exactly. I use them always for the Action / Deploy process to store my FTP credentials in it. But now i have a repository cotaining a php file with credentials for my database and i dont want to expose them. I hoped that there is a way to hide them with with secrets.

idea; can I create a file via cat ${{ SECRET }} > file.php in the workflow? If so, I would have the file containing the access data created by the workflow…?

I haven’t tested it personally, but the documentation describes how to commit an encrypted file and decrypt it using a secret. Maybe that would help?

Having any sort of credentials hardcoded in a PHP file sounds like a bad idea. Why don’t you you let PHP read the credentials from environment variables for instance?

1 Like

Can you be more specific? I know there is such a thing, but I don’t think I’ve really worked with it.

Something along the following, with the database password stored as GitHub secret with name SCRIPT_CREDENTIALS:

steps:
  - name: Execute script
    env:
      PASSWORD: ${{ secrets.SCRIPT_CREDENTIALS }}
    run: php script.php

script.php:

<?php
$servername = "localhost";
$username = "username";
$password = getenv("PASSWORD");

$conn = new mysqli($servername, $username, $password);

if ($conn->connect_error) {
  die("Connection failed: " . $conn->connect_error);
}
echo "Connected successfully";
?>
2 Likes

Sorry, I was on the road for a few days and could not test nor answer anything.

Your solution looks good, would do that.

In the converted file on the server you will only find the variables but not the modified data (the content of the variables)

Run php config/db.php
  php config/db.php
  shell: /bin/bash -e {0}
  env:
    SERVER: ***
    USERNAME: ***
    PASSWORD: ***
PHP Warning:  mysqli::__construct(): (HY000/2002): No such file or directory in /home/runner/work/dcd-test/dcd-test/config/db.php on line 6
PHP Stack trace:
PHP   1. {main}() /home/runner/work/dcd-test/dcd-test/config/db.php:0
PHP   2. mysqli->__construct() /home/runner/work/dcd-test/dcd-test/config/db.php:6
Connection failed: No such file or directory

The folder is in the correct path on the server, it just seems like the php command doesn’t work. Does that need a special package?

Can you check what the working directory is with pwd?

Looks like it cannot find config/db.php relative to the current working directory. Although the last log entry says “Connection failed”, which seems a bit odd.

Thanks a lot for this!. I want to do the same using python and email credentials, instead. In python I’m using os.environ.get() to read the secrets, but with no success so far. Any ideas on how to do it with python?

To quote from your Stackoverflow post:

- name: execute py script # run sj-gobierno.py to get the latest data
  env: | 
    EMAIL_ADDRESS: ${{ secrets.EMAIL_ADDRESS }}
    EMAIL_PASSWORD: ${{ secrets.EMAIL_PASSWORD }}

The problem is that you’re turning setting a text block for env, instead of individual keys. Correct version:

- name: execute py script # run sj-gobierno.py to get the latest data
  env:
    EMAIL_ADDRESS: ${{ secrets.EMAIL_ADDRESS }}
    EMAIL_PASSWORD: ${{ secrets.EMAIL_PASSWORD }}

I also highly recommend moving the pip install to a separate, previous step so it doesn’t have access to the secrets. Principle of least privilege and all that.

1 Like

Thank you, it worked!. And thank you for the recommendation to move pip install to a previous step. For others interested in using secrets inside python, I can confirm that os.environ.get() does the job.

1 Like

.py file

import smtplib
import os
from email.message import EmailMessage
EMAIL_ADDRESS = os.environ[‘EMAIL_ADDRESS’]
EMAIL_PASSWORD = os.environ[‘EMAIL_PASSWORD’]
msg = EmailMessage()
msg[‘Subject’] = ‘Checking smtplib FROM GITHUB’
msg[‘From’] = EMAIL_ADDRESS
msg[‘To’] = ‘lxmnmrzn@gmail.com’
msg.set_content(‘This is a email check.’)
with smtplib.SMTP_SSL(‘smtp.gmail.com’, 465) as smtp:
smtp.login(EMAIL_ADDRESS, EMAIL_PASSWORD)
smtp.send_message(msg)

.yml file

  • name: execute py script
    env:
    EMAIL_ADDRESS: {{ secrets.EMAIL_ADDRESS }} EMAIL_PASSWORD: {{ secrets.EMAIL_PASSWORD }}

ERROR
smtp.login(EMAIL_ADDRESS, EMAIL_PASSWORD)
can only concatenate str (not “bytes”) to str

Please help me solve this problem

Are you using Python 2?
os.environ['...'].decode('utf8') might do the trick.

There was an issue with the GitHub secrets, was setting secrets in environment instead of repo.
@canovasjm helped me solve it.
Thank you @Simran-B @canovasjm