Objective. Students identify insecure scripting patterns and rewrite vulnerable code snippets to eliminate command injection, hard-coded credentials, and overly broad file permissions.
This lesson, its worked examples and its practice questions has not yet been verified by an industry-certified professional or by a certification or approval body. It is a study draft and may contain errors. Do not use it as instructions for work on a job site or on live equipment. Always follow your instructor, your supervising professional, and current official references and regulations.
VoTechWorks program approval and accreditation are still in progress. Credentials are issued only by the certifying bodies named in each program, after their own exams.
Automation scripts are everywhere in cybersecurity work: they collect logs, call APIs, manage user accounts, and respond to alerts. Because scripts often run with elevated permissions and interact with sensitive data, a single insecure pattern can give an attacker a foothold in the entire environment. Three patterns cause the majority of scripting vulnerabilities in practice: unsanitized input passed to a shell, credentials stored in plaintext inside source code, and scripts that run with more privilege than the task requires.
When a Python script builds a shell command by concatenating user-supplied input and passes it to `subprocess.run()` or `os.system()` with `shell=True`, the interpreter hands the full string to the operating system shell. An attacker who controls any part of that string can append additional commands using characters such as `;`, `&&`, `|`, or backticks.
Vulnerable pattern:
```python
import subprocess
filename = input("Enter filename: ")
subprocess.run("cat " + filename, shell=True)
```
If a user enters `report.txt; rm -rf /tmp`, the shell executes both commands.
Secure pattern — list argument, no shell:
```python
import subprocess
filename = input("Enter filename: ")
subprocess.run(["cat", filename], shell=False)
```
When you pass a list, Python passes each element as a discrete argument directly to the program; the shell is never involved, so shell metacharacters have no effect. Always validate and allowlist input even when using the list form — reject values that contain path-traversal sequences such as `../`.
Storing passwords, API keys, or tokens directly in source code is one of the most common and most costly mistakes in scripting. Source files end up in version control, get shared across teams, or are accidentally exposed in logs. Anyone who reads the file — or the git history — has the credential.
Vulnerable pattern:
```python
API_KEY = "sk-abc123secret"
```
Secure alternatives:
1. Environment variables — Read the value at runtime with `os.environ.get("API_KEY")`. The variable is set in the shell session or a deployment config file that is never committed to source control.
2. Secrets file with restricted permissions — Store credentials in a file outside the project directory, set its permissions to `600` (owner read/write only) with `chmod 600 secrets.env`, and read it at runtime. Never commit this file; add it to `.gitignore`.
3. Secrets managers — Production environments often use dedicated tools (such as HashiCorp Vault or cloud provider secrets managers) that audit access and rotate credentials automatically.
After removing a hard-coded credential, rotate the credential immediately — assume it was already compromised.
The principle of least privilege states that a process should have only the permissions it needs to complete its task, and no more. A script that runs as root (UID 0 on Linux) can read, write, or delete any file, modify system configuration, and install software. If the script is exploited, the attacker inherits root.
Privilege-drop pattern in Python:
```python
import os
os.setgid(1000) # set group ID first
os.setuid(1000) # then set user ID
```
Set GID before UID because a root process can change both; once UID is dropped, group changes may be restricted. Use a dedicated service account with the minimum necessary file permissions rather than a regular user account when possible.
For scripts that legitimately need a brief moment of elevated privilege (for example, opening a low-numbered port), perform only that operation as root, drop privileges immediately afterward, and continue execution as a limited user.
Linux and macOS use octal permission modes. A secrets file should be readable only by its owner:
Set permissions from the command line: `chmod 600 secrets.env`. Scripts that create temporary files containing sensitive data should also set restrictive permissions immediately on creation and delete the files when finished.
Safe scripting is not only about code — it includes the physical workspace. Security Operations Center (SOC) analysts and scripting practitioners face real ergonomic and electrical hazards:
The OSHA General Duty Clause (Section 5(a)(1) of the OSH Act) requires employers to provide a workplace free from recognized hazards likely to cause serious harm. This applies to IT workplaces just as it does to factories.
Secure scripts avoid shell injection by using subprocess with a list argument and no `shell=True` on user-supplied input. They never store credentials in source code, relying instead on environment variables, restricted secrets files, or secrets managers. They run with the minimum privilege required, dropping elevated rights as soon as possible. Combined with physical safety awareness in the workspace, these practices form the foundation of responsible automation work in cybersecurity.
Answer each question, then check it. Nothing is recorded; this is your own practice.
A free tech program — complimentary, as our commitment to get America working. Help us build VoTechWorks. Tell us what works, what doesn’t, and what you want to learn.