← All posts

By Speedy ·October 18, 2026· 14 min read

PCES-30-01 Study Guide: Every Objective, Mapped to Free Resources

pythonsecuritycertificationpcesstudy-guide
On this page
  1. The exam
  2. Core resources
  3. Block 1 · Security Essentials (10 items, 22%)
  4. Block 2 · IT Systems Security (12 items, 27%)
  5. Block 3 · Python for Security Operations (13 items, 29%, the largest block)
  6. Block 4 · Secure Development in Python (10 items, 22%)
  7. A 3-week plan (about 1 hour a day)

The Python Institute's PCES™ (Certified Entry-Level Security Specialist with Python) exam is new, and the official study-resources page still says "Coming soon." This guide goes through every objective in the PCES-30-01 syllabus. For each one it gives free resources and, for every Python objective, a short lab you can run yourself.

The exam

  • Code: PCES-30-01
  • Items: 45 (single-select, multiple-select, and typed-answer)
  • Time: 60 minutes
  • Pass mark: 75% cumulative (no minimum per block)
  • Assumed Python level: PCEP
  • Delivery: OpenEDG TestNow, online
Block Items Weight
1 · Security Essentials 10 22%
2 · IT Systems Security 12 27%
3 · Python for Security Operations 13 29%
4 · Secure Development & Implementation in Python 10 22%

Core resources

These six sources cover almost the whole syllabus, and nearly all of them are free:

The Python Institute said an official PCES practice test ($29) would come out in Q3/Q4 2026. Check the Edube store before you book your exam. It is the best way to learn the question style, especially the typed-answer items.


Block 1 · Security Essentials (10 items, 22%)

1.1.1 CIA triad, authenticity, accountability (1 item)

  • Messer: The CIA Triad, Non-repudiation
  • Know the trade-off between security and usability, for example requiring MFA on every action. Accountability means audit logs tied to individual accounts. Authenticity means signatures and verified origin.

1.2.1 Threats vs. risks, malware, phishing, DoS/DDoS, threat actors (2 items, shared with 1.2.2)

  • Messer: An Overview of Malware, plus the phishing, social engineering, Denial of Service, Threat Actors and Motivations of Threat Actors videos
  • Cloudflare Learning: What is a DDoS attack?
  • Worms spread on their own. Viruses need a host file and a user action. Trojans pretend to be useful software. Risk is likelihood × impact; a threat is the thing that might happen.

1.2.2 Basic protections and layered defense

  • Data at rest is protected with disk or file encryption. Data in transit is protected with TLS or a VPN.
  • Match each threat to its countermeasure: ransomware → offline backups, credential theft → MFA, malware → AV/EDR. Defense in depth means using several layers.

1.3.1–3 Consequences of data loss, theft and modification (3 items)

  • NetAcad Intro to Cybersecurity: the module on the cost and impact of breaches. The IBM Cost of a Data Breach report has real-world figures.
  • Expect scenario questions:
    • Theft hits intellectual property, PII and reputation.
    • Modification leads to bad decisions and safety risks, such as a wrong pharmacy dose or a falsified aviation maintenance record.
    • Loss means downtime plus recovery and legal costs.
  • Know the difference between intentional and accidental modification.

1.4.1 Losses from system unavailability (1 item)

  • Direct losses are sales missed during the outage. Indirect losses include abandoned carts, fewer repeat customers and brand damage.
  • Also know: emergency fix costs, customer compensation, delays to critical services, and regulations that require data to stay accessible.

1.5.1 GDPR, CCPA, HIPAA, breach notification, trade secrets, NDAs (1 item)

  • GDPR Art. 33 (notify the authority within 72 hours) · HHS: HIPAA Breach Notification Rule · California AG: CCPA
  • Scope:
    • GDPR covers personal data of people in the EU.
    • HIPAA covers US health data held by covered entities.
    • CCPA covers California consumers.
  • A trade secret is protected because it is kept secret, not because it is registered (unlike a patent).
  • An accidental disclosure, such as a misdirected email, still counts as a breach.

1.6.1–2 Communication, escalation, incident response team (2 items)

  • Messer: Incident Response and Incident Planning (which covers tabletop exercises and drills)
  • NIST SP 800-61 Rev. 3 (April 2025). Skim the roles and responsibilities section only.
  • Report early, even if you aren't sure it's an incident. Write differently for technical and non-technical readers.
  • An incident record covers who, what, when, where, impact and the actions taken. Follow the escalation chain instead of skipping levels.

Block 2 · IT Systems Security (12 items, 27%)

2.1.1 Security as a continuous process (4 items, shared across 2.1.x)

Threats keep changing, so monitoring and patching never end. Typical examples are an unpatched server that gets exploited, or a policy that was never updated after a move to remote work.

2.1.2 Firewall, IDS, IPS, VPN

  • Messer: Firewalls, Intrusion Prevention, Virtual Private Networks
  • An IDS detects and alerts. An IPS sits inline and blocks traffic. A common question gives a scenario and asks which of the two fits.

2.1.3 Organizational safeguards

Least privilege, approved devices only, limited trust (to reduce insider threat), anonymizing data before sending it to internet services, regular security training, and keeping production separate from test.

2.1.4 Secure development practices

  • OWASP Logging Cheat Sheet (what must never be logged)
  • Peer code review, security requirements written at the start of a project (shift left), and masking or anonymizing PII and secrets in logs.

2.2.1 System hardening process (1 item)

  • Messer: Hardening Techniques. For a real-world checklist, look at one of the CIS Benchmarks.
  • Order matters: remove unneeded services, then apply updates, then configure secure settings. Harden before deployment, after any change, and on a schedule.

2.3.1–2 Ports, protocols, segmentation (2 items)

Port Service Note
20/21 FTP Plaintext. Prefer SFTP (over SSH on port 22) or FTPS
22 SSH / SFTP Encrypted remote shell and file transfer
23 Telnet Plaintext. Disable it
25 SMTP Mail between servers (port 587 for client submission)
53 DNS UDP and TCP
80 / 443 HTTP / HTTPS 443 is HTTP over TLS
3389 RDP Never expose it to the internet

Every open port is attack surface. Segmentation (VLANs, a DMZ for public servers) limits how far an attacker can move inside the network.

2.4.1–3 Authentication vs. authorization, password policy, MFA (3 items)

  • Messer: Authentication, Authorization, and Accounting, Multifactor Authentication · CISA: MFA
  • Authentication answers "who are you?" (logging in). Authorization answers "what are you allowed to do?" (file permissions).
  • MFA factors are something you know, something you have, or something you are. SMS is the weakest method and hardware tokens are the strongest.
  • Exam vs. real world: the syllabus treats expiry, history and complexity as the standard password policy settings, so learn them. Current NIST SP 800-63B guidance actually discourages forced periodic expiry. If the exam asks what the policy settings are, give all three.

2.5.1–2 Cloud storage risks, shared responsibility, remote access (2 items)

  • AWS Shared Responsibility Model
  • The provider secures the cloud itself. The customer secures what they put in it: data, identity and access management, and bucket policies. A public S3 bucket is the classic misconfiguration.
  • For remote workers: VPN, full-disk encryption, patching, screen lock, and no local admin rights.

Block 3 · Python for Security Operations (13 items, 29%, the largest block)

Only scan systems you own or have written permission to test. The exam tests this rule directly (objective 3.1.1). For practice, use 127.0.0.1, your own VMs, or scanme.nmap.org, which the Nmap project explicitly allows people to scan.

3.1.1 Authorized port scanning with socket and python-nmap (3 items, shared across 3.1.x)

python-nmap needs the nmap binary installed.

import socket
for port in (21, 22, 80, 443):
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.settimeout(0.5)
        if s.connect_ex(("127.0.0.1", port)) == 0:   # 0 means the port is open
            print(port, "open")

import nmap
nm = nmap.PortScanner()
nm.scan("127.0.0.1", "20-443")
print(nm["127.0.0.1"]["tcp"].keys())

Know that connect_ex returns an error code while connect raises an exception, and know AF_INET and SOCK_STREAM.

3.1.2 Outdated versions and weak or default passwords (test environments only)

from packaging.version import Version
if Version(installed) < Version(minimum_safe):
    print("outdated")
weak = {"admin", "password", "123456", "root"}
flagged = [u for u, pw in test_accounts.items() if pw in weak]

3.1.3 WHOIS and banner grabbing

Uses python-whois.

import whois
w = whois.whois("example.com"); print(w.registrar, w.expiration_date)

with socket.create_connection(("127.0.0.1", 22), timeout=3) as s:
    print(s.recv(1024).decode(errors="replace"))   # e.g. SSH-2.0-OpenSSH_9.6

3.2.1 TLS certificate validity and expiry (4 items, shared across 3.2.x)

import ssl, socket, datetime as dt
host = "example.com"
ctx = ssl.create_default_context()          # verifies the certificate chain and hostname
with socket.create_connection((host, 443), timeout=5) as sock:
    with ctx.wrap_socket(sock, server_hostname=host) as tls:
        cert = tls.getpeercert()
expires = dt.datetime.fromtimestamp(ssl.cert_time_to_seconds(cert["notAfter"]), dt.timezone.utc)
days = (expires - dt.datetime.now(dt.timezone.utc)).days
if days < 30: print(f"ALERT: {host} cert expires in {days} days")

An invalid or expired certificate raises ssl.SSLCertVerificationError during the handshake.

3.2.2 Process monitoring with psutil

psutil docs: process_iter, cpu_percent, virtual_memory, net_connections.

import psutil
for p in psutil.process_iter(["pid", "name", "memory_percent"]):
    if (p.info["memory_percent"] or 0) > 10:
        print("high memory:", p.info)
# cpu_percent() returns 0.0 on its first call; pass interval=1 or call it twice.

3.2.3 Automated checks: firewall, pending updates, alerts, restarting services

Send alerts with smtplib and email.message.EmailMessage.

import subprocess
def active(svc):
    return subprocess.run(["systemctl", "is-active", svc],
                          capture_output=True, text=True).stdout.strip() == "active"
if not active("ufw"):
    subprocess.run(["systemctl", "restart", "ufw"], check=True)
    notify_admin("ufw was down and has been restarted")
# Windows: ["netsh", "advfirewall", "show", "allprofiles", "state"]
# Pending updates (Debian/Ubuntu): ["apt", "list", "--upgradable"]

3.2.4 Running OS commands with subprocess: AV scans, log archiving

r = subprocess.run(["clamscan", "-r", "/home"], capture_output=True, text=True)
print(r.returncode)            # clamscan: 0 = clean, 1 = infected file found
shutil.make_archive("/var/backups/logs-2026-10", "gztar", "/var/log/app")

Pass the command as a list, and never use shell=True with user input, because that allows command injection. Know what returncode, check=True and CalledProcessError do.

3.3.1 Correlating logs from several sources (1 item)

import re
from collections import Counter
fails = Counter()
for line in open("auth.log"):
    if m := re.search(r"Failed password .* from (\d+\.\d+\.\d+\.\d+)", line):
        fails[m[1]] += 1
fw_blocked = {line.split()[5] for line in open("firewall.log") if "DENY" in line}
for ip, n in fails.items():
    if n >= 5 and ip in fw_blocked:
        print("correlated alert:", ip, n)

3.4.1–2 Structured reports: CSV, JSON, PDF, charts, recommendations (2 items)

import csv, json, datetime as dt
rows = [{"timestamp": dt.datetime.now().isoformat(), "ip": "10.0.0.5",
         "threat": "brute-force", "action": "Block IP, enforce MFA"}]
with open("report.csv", "w", newline="") as f:
    w = csv.DictWriter(f, fieldnames=rows[0].keys()); w.writeheader(); w.writerows(rows)
json.dump(rows, open("report.json", "w"), indent=2)

For PDF output use fpdf2 or ReportLab, and use matplotlib for the visual summary. Every finding should include a timestamp, the IP or host, the threat type, and an actionable recommendation.

3.5.1 Scheduling with cron, Task Scheduler and APScheduler (3 items, shared across 3.5.x)

crontab.guru explains cron expressions. The APScheduler 3.x docs cover the API that most tutorials, and the exam, use.

# cron, every day at 02:00:   0 2 * * * /usr/bin/python3 /opt/sec/scan.py
# Windows:  schtasks /create /tn NightlyScan /tr "python C:\sec\scan.py" /sc daily /st 02:00
from apscheduler.schedulers.blocking import BlockingScheduler
sched = BlockingScheduler()
sched.add_job(run_scan, "interval", minutes=30)
sched.add_job(run_backup, "cron", hour=2)
sched.start()

Know the five cron fields in order: minute, hour, day of month, month, day of week.

3.5.2 Backups and integrity checks

import hashlib, hmac
def sha256(path):
    with open(path, "rb") as f:
        return hashlib.file_digest(f, "sha256").hexdigest()   # Python 3.11+
ok = hmac.compare_digest(sha256("db.sqlite"), sha256("/backup/db.sqlite"))

3.5.3 Chaining tasks

def pipeline():
    findings = run_checks()
    if not run_backup(): findings.append("backup failed")
    cleanup_old_logs()
    write_report(findings)        # each step runs only after the previous one finishes

Block 4 · Secure Development in Python (10 items, 22%)

4.1.1 Static analysis with pylint and flake8 (5 items, shared across 4.1.x)

  • pylint · flake8 · also useful: bandit, a linter focused on security
  • pylint gives a score out of 10 and message codes in five categories (C, R, W, E, F). flake8 combines pyflakes, pycodestyle (PEP 8) and mccabe. Run pylint file.py and flake8 file.py once each so you recognize the output.

4.1.2 SQL injection and XSS

OWASP Query Parameterization · OWASP XSS Prevention

# BAD:  cur.execute(f"SELECT * FROM users WHERE name = '{name}'")   # input  ' OR '1'='1
cur.execute("SELECT * FROM users WHERE name = ?", (name,))        # sqlite3 uses ? placeholders
import html
safe = html.escape(user_comment)     # < > & " ' are converted to entities

4.1.3 Output encoding for HTML, XML and JSON

html.escape(s)                                   # HTML
from xml.sax.saxutils import escape; escape(s)   # XML: & < >
json.dumps({"msg": s})                           # JSON escapes quotes, backslashes and control characters

4.1.4 Path traversal and safe error handling

OWASP: Path Traversal · OWASP Error Handling

from pathlib import Path
BASE = Path("/srv/files").resolve()
target = (BASE / requested_name).resolve()     # "../../etc/passwd" resolves to a path outside BASE
if not target.is_relative_to(BASE):            # Python 3.9+
    raise PermissionError("invalid path")
try:
    ...
except Exception:
    log.exception("download failed")           # the full traceback goes to the server log
    return "Something went wrong.", 500        # the user only sees a generic message

4.1.5 Secrets in environment variables, never in git

OWASP Secrets Management · python-dotenv

import os
api_key = os.environ["API_KEY"]        # raises KeyError if it's missing
db_pw = os.getenv("DB_PASSWORD")       # returns None if it's missing
# add .env to .gitignore

4.2.1 Encryption with Fernet (3 items, shared across 4.2.x)

cryptography: Fernet

from cryptography.fernet import Fernet
key = Fernet.generate_key()            # keep the key safe; without it the data can't be decrypted
f = Fernet(key)
token = f.encrypt(b"secret")           # bytes in, bytes out
f.decrypt(token)                       # a wrong key or altered token raises InvalidToken
open("x.enc", "wb").write(f.encrypt(open("x.txt", "rb").read()))

Fernet uses AES-128-CBC with an HMAC-SHA256 tag, so it provides both confidentiality and integrity.

4.2.2 SSH and SFTP with paramiko

paramiko docs

import paramiko
c = paramiko.SSHClient()
c.load_system_host_keys()
c.set_missing_host_key_policy(paramiko.RejectPolicy())   # AutoAddPolicy accepts unknown hosts and allows man-in-the-middle attacks
c.connect("host", username="ops", key_filename="~/.ssh/id_ed25519")
_, out, _ = c.exec_command("uptime"); print(out.read().decode())
sftp = c.open_sftp(); sftp.put("report.pdf", "/srv/reports/report.pdf"); sftp.close()
c.close()

4.2.3 Handling PDF, DOCX and XLSX files safely

  • Automate the Boring Stuff: the PDF/Word chapter and the Excel chapter
  • pypdf docs. The syllabus names PyPDF2, which has been merged back into pypdf. The API is the same (PdfReader, PdfWriter, encrypt, extract_text).
  • python-docx · openpyxl · oletools (its olevba tool detects macros)
from PyPDF2 import PdfReader, PdfWriter
r = PdfReader("in.pdf"); w = PdfWriter()
for page in r.pages: w.add_page(page)
w.encrypt("long-unique-passphrase")
with open("out.pdf", "wb") as f: w.write(f)
text = r.pages[0].extract_text()

import docx
d = docx.Document("memo.docx")
d.core_properties.author = ""; d.core_properties.comments = ""; d.save("clean.docx")

import openpyxl
wb = openpyxl.load_workbook("data.xlsx")
for ws in wb.worksheets:
    if ws.sheet_state != "visible": print("hidden sheet:", ws.title)
    for row in ws.iter_rows():
        for c in row:
            if c.data_type == "f": print("formula:", c.coordinate, c.value)

Macros live in .xlsm and .docm files, not .xlsx or .docx. When you share a protected file, send its password through a different channel than the file itself.

4.3.1–2 Hashing and checksum validation (2 items)

hashlib · RFC 6151 (MD5 security considerations) · RFC 6150 (MD4 retired)

published = "9f86d081884c7d65..."      # copied from the vendor's HTTPS download page
if not hmac.compare_digest(sha256("installer.exe"), published.lower()):
    raise SystemExit("checksum mismatch, do not run this file")

MD4, MD5 and SHA-1 are broken because collisions can be found. Use SHA-256 or stronger (SHA-384, SHA-512, SHA3-256). A hash detects that a file changed; it does not encrypt anything.


A 3-week plan (about 1 hour a day)

Days Focus Why
1–4 Block 1: Messer videos plus NetAcad's threat and impact modules Mostly vocabulary and scenarios, so it goes quickly
5–8 Block 2: Messer videos, the ports table, AWS shared responsibility 27% of the exam
9–14 Block 3: type out and run every lab against localhost 29%, the largest block, and typed-answer items need real recall
15–18 Block 4: the labs plus the OWASP cheat sheets Library APIs (Fernet, paramiko, PyPDF2) are easy points once you've used them
19–21 Take the official practice test, then review weak objectives You need 75% overall; there is no per-block minimum

Install everything the labs use:

pip install psutil python-nmap python-whois cryptography paramiko PyPDF2 python-docx openpyxl "apscheduler<4" fpdf2 matplotlib oletools pylint flake8 bandit

The objectives and item counts are taken from the official PCES-30-01 syllabus. Good luck on the exam.

// about the author

Speedy