10

I am using Python 2.7.3 and successfully passing my login credentials to an SMTP server to send me a text or email when an event trigger takes place. However, now I would like to store the SMTP server login and password in a separate file so that multiple scripts could use that information or so that I can share my script without having to remove my credentials each time.

I am using the great tutorial from Alex Le on sending an SMS via Python. But now I want to take the following segment and put it into another file that can be called by multiple scripts. This could be either just the username/password pair or the whole section.

server = smtplib.SMTP( "smtp.gmail.com", 587 )
server.starttls()
server.login( '<gmail_address>', '<gmail_password>' )

I would consider myself a pretty basic Python programmer. I don't mind doing some research, but I think I need help on what terms I should be looking for.

dpeach
  • 111
  • 1
  • 1
  • 6

3 Answers3

14

Get all the critical variables from .yml file:

import yaml
conf = yaml.load(open('conf/application.yml'))
email = conf['user']['email']
pwd = conf['user']['password']

server = smtplib.SMTP( "smtp.gmail.com", 587 ) # add these 2 to .yml as well
server.starttls()
server.login(email, pwd)

The application.yml will look similar to this:

user:
    email: example@mail.com
    password: yourpassword

This way, you will never see the actual credentials in the script.

admix
  • 1,752
  • 3
  • 22
  • 27
  • 2
    This worked beautifully! It also gives me a great tutorial on how to do this for other types of information I want to insert into a script. – dpeach Dec 11 '15 at 19:43
  • Glad to hear that @dpeach :) – admix Dec 11 '15 at 19:44
  • 1
    This is one of the good solution. It hides the credential from the script which is how the question requested. It just doesn't protect the credential from plaintext. But this is another topic, which is big. I upvoted this answer. It doesn't deserve the downvote from ridiculous user. – Antony.H Jan 07 '18 at 17:51
  • 3
    Is there any difference in terms of security between this method and just use a credentials.py containing a dictionary with the credentials? – Javier Lopez Tomas Feb 22 '19 at 10:05
  • good answer, but I would use safe_load instead of load, since safe_load is safer after vulnerabilities issue of load: https://github.com/yaml/pyyaml/wiki/PyYAML-yaml.load(input)-Deprecation – Driss NEJJAR Feb 17 '22 at 19:09
12

In production environments what we usually do is make a seperate file and save it somewhere outside the project. Now do chmod 600, i.e. allow only root to access the file. Now in your code run read the file by running in the the superuser mode. Or you could also create a different user which can access the file and run the code using that user.

OR You could use environment variables in your system. You can set one by doing the following in bash shell

export KEY=some_value

And then in your Python code

os.environ.get('KEY')
utkbansal
  • 2,787
  • 2
  • 26
  • 47
  • This is the right answer. Store the email and password outside of the project and use the `import os` library to get it. You want to make sure that it never even gets a chance to be pushed into the public repository. DO NOT PUT IT IN `settings.py`. Oh god. Edit: if you're in Windows, you can still store it outside of the project and just import the file, followed by parsing it. The point is, keep it out of the project directory! – Mmm Donuts Dec 11 '15 at 19:37
  • @PadraicCunningham I didn't make a recommendation in that regard. I was targeting the issue of mistakenly committing it to a public repo - which Microsoft did just this past week. – Mmm Donuts Dec 11 '15 at 19:43
  • this is really the right answer if it is something the OP can do – Joran Beasley Dec 11 '15 at 19:46
  • Thanks @utkbansal. I will definitely take your advice and set the file permissions as 600. Since this is on an older Debian install for Raspberry Pi, and I am monitoring temperatures through the GPIO, I have to run the main script as root anyway. (The newest version of Raspbian (Debian for the Pi) allows GPIO control as a user.) – dpeach Dec 11 '15 at 19:47
  • @dpeach, adding what OS you are using would have been a good idea – Padraic Cunningham Dec 11 '15 at 19:48
  • @PadraicCunningham You can't import a file in Windows using python? Huh, weird. Alright well, cheers. – Mmm Donuts Dec 11 '15 at 19:48
  • @Kris, there is no root in windows, that is what I obviously meant – Padraic Cunningham Dec 11 '15 at 19:48
  • @dpeach great! If you are using linux, the environment variables way. It is much more secure than storing you credtentails on a filesystem – utkbansal Dec 11 '15 at 19:49
  • @PadraicCunningham we can use environment variables afaik on windows. – utkbansal Dec 11 '15 at 19:53
  • Yes, @PadraicCunningham, mentioning I was using Linux would have been helpful. Since I've never done anything in Python on Windows, I forget that is even an option. – dpeach Dec 11 '15 at 20:00
  • 1
    @Kris I think you're overdoing with the capitals and the "Oh god". For someone stating to be a basic programmer, using literal tutorial code to send e-mail and asking how to get the variables out of the script, putting it in a separate file is not wrong at all. Making it public or putting it a file with a .py extension are not the same thing. You're answering as if it was a question about best deployment practices. – Takis Dec 11 '15 at 20:30
  • @PadraicCunningham that's not very nice – Mmm Donuts Dec 11 '15 at 23:07
5

Use a separate configuration file settings.py containing:

EMAIL = 'gmail address'
PASSWORD = 'gmail password'

As the configuration file is a Python file, you can import it from your actual code:

from . import settings

server = smtplib.SMTP( "smtp.gmail.com", 587 )
server.starttls()
server.login(settings.EMAIL, settings.PASSWORD)

This is similar to what projects such as Django use, which you can see here.

You would need to keep the settings.py file secret, so you would not add it to your revision control software and wouldn't make it publicly readable.

Takis
  • 726
  • 5
  • 11