CyberArk REST API Returns 401 Even with Valid Session Token (How to Fix)

You authenticate to CyberArk’s PVWA API, get a session token back with HTTP 200, then immediately call GET /PasswordVault/api/Accounts and get a 401 Unauthorized. The token looks valid. You confirmed it’s being passed in the request. The account has the right permissions. Here’s why it’s happening and how to fix it. The Problem: Wrong Authorization Header Format CyberArk’s PVWA API (v9.x and earlier) does not use the standard Bearer token format. Sending: ...

April 9, 2026 Â· 4 min Â· 802 words Â· Rob Washington

API Rate Limiting: Protecting Your Services Without Frustrating Users

Rate limiting is the bouncer at your API’s door. Too strict and legitimate users bounce. Too loose and bad actors overwhelm your service. Here’s how to get it right. Why Rate Limit? Without rate limiting: One misbehaving client can DOS your entire service Costs spiral when someone scrapes your API Bugs in client code create accidental amplification You have no defense against credential stuffing Rate limiting provides fairness, stability, and cost control. ...

March 13, 2026 Â· 8 min Â· 1618 words Â· Rob Washington

SSH Hardening: Secure Your Servers in 30 Minutes

SSH is the front door to your servers. A weak SSH config is an open invitation to attackers. Here’s how to lock it down properly without locking yourself out. The Bare Minimum 1 2 3 4 5 6 7 8 9 10 11 12 13 # /etc/ssh/sshd_config # Disable root login PermitRootLogin no # Disable password authentication PasswordAuthentication no # Enable key-based auth only PubkeyAuthentication yes # Disable empty passwords PermitEmptyPasswords no 1 2 # Apply changes sudo systemctl restart sshd These four settings stop 99% of automated attacks. ...

March 12, 2026 Â· 7 min Â· 1382 words Â· Rob Washington

Secrets Management: Stop Committing Your API Keys

We’ve all done it. Committed a database password. Pushed an API key. Then frantically force-pushed hoping nobody noticed. Here’s how to manage secrets properly so that never happens again. The Problem 1 2 3 4 5 6 7 8 9 # Bad: Secrets in code DATABASE_URL="postgres://admin:supersecret@db.example.com/prod" # Bad: Secrets in .env checked into git # .env API_KEY=sk-live-abc123 # Bad: Secrets in CI/CD logs echo "Deploying with $DATABASE_PASSWORD" Secrets in code get leaked. Always. It’s just a matter of when. ...

March 12, 2026 Â· 6 min Â· 1275 words Â· Rob Washington

Container Security Essentials: Beyond docker run

Containers aren’t inherently secure. They share a kernel with the host. A container escape is a host compromise. Here’s how to not be the cautionary tale. Image Security Use Minimal Base Images Every package is attack surface. Minimize it. 1 2 3 4 5 6 7 8 # Bad: Full OS with thousands of packages FROM ubuntu:22.04 # Better: Minimal OS FROM alpine:3.19 # Best: Distroless (no shell, no package manager) FROM gcr.io/distroless/static-debian12 Distroless images contain only your app and runtime dependencies. No shell means attackers can’t get a shell. ...

March 12, 2026 Â· 6 min Â· 1136 words Â· Rob Washington

SSL Certificates: Automation That Doesn't Expire

Certificate expiration is the outage you always see coming and somehow never prevent. Here’s how to automate SSL so it stops being a problem. The Problem SSL certificates expire. When they do: Users see scary browser warnings APIs reject connections Mobile apps fail silently Trust is broken And it’s always on a Friday night. Let’s Encrypt + Certbot Free, automated, trusted certificates. Basic Setup 1 2 3 4 5 6 7 8 # Install certbot apt install certbot python3-certbot-nginx # Get certificate (nginx plugin handles everything) certbot --nginx -d example.com -d www.example.com # Test renewal certbot renew --dry-run Certbot adds a cron job automatically for renewal. ...

March 11, 2026 Â· 3 min Â· 464 words Â· Rob Washington

Docker Best Practices for Production

Docker makes it easy to containerize applications. Docker makes it equally easy to create bloated, insecure, slow-to-build images. The difference is discipline. These practices come from running containers in production—where image size affects deployment speed, security vulnerabilities get exploited, and build times multiply across teams. Start With the Right Base Image Your base image choice cascades through everything else. The options: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # Full OS - 900MB+ FROM ubuntu:22.04 # Slim OS - 80MB FROM debian:bookworm-slim # Minimal - 5MB FROM alpine:3.19 # Language-specific slim - varies FROM python:3.12-slim FROM node:20-alpine # Distroless - minimal runtime only FROM gcr.io/distroless/python3 General guidance: ...

March 11, 2026 Â· 7 min Â· 1379 words Â· Rob Washington

Secrets Management Done Right

Every developer has done it. Committed an API key to git, pushed to GitHub, and watched in horror as the secret scanner flagged it within minutes. If you’re lucky, the service revokes the key automatically. If you’re not, someone’s crypto-mining on your AWS account. Secrets management isn’t glamorous, but getting it wrong is expensive. The Problem Space Secrets include: API keys and tokens Database credentials Encryption keys TLS certificates OAuth client secrets SSH keys Signing keys These all share properties: they’re sensitive, they need rotation, and they need to reach your application somehow without being exposed. ...

March 11, 2026 Â· 6 min Â· 1236 words Â· Rob Washington

Environment Variables Done Right: Configuration Without the Pain

Environment variables seem trivial. Set a value, read it in code. Done. Then you deploy to production and realize the staging database URL leaked into prod. Or someone commits a .env file with API keys. Or your Docker container starts with 47 environment variables and nobody knows which ones are actually required. Here’s how to do it properly. The Basics: Reading Environment Variables Every language has a way to read environment variables: ...

March 10, 2026 Â· 5 min Â· 924 words Â· Rob Washington

API Rate Limiting: Protecting Your Service Without Annoying Your Users

Rate limiting is the immune system of your API. Without it, a single misbehaving client can take down your service for everyone. With poorly designed limits, you’ll frustrate legitimate users while sophisticated attackers route around you. The goal isn’t just protection—it’s fairness. Every user gets a reasonable share of your capacity. The Basic Algorithms Fixed Window The simplest approach: count requests per time window, reject when over limit. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import time import redis def is_rate_limited(user_id: str, limit: int = 100, window: int = 60) -> bool: """Fixed window: 100 requests per minute.""" r = redis.Redis() # Window key based on current minute window_key = f"ratelimit:{user_id}:{int(time.time() // window)}" current = r.incr(window_key) if current == 1: r.expire(window_key, window) return current > limit Problem: Burst at window boundaries. A user can make 100 requests at 0:59 and 100 more at 1:00—200 requests in 2 seconds while technically staying under “100/minute.” ...

March 10, 2026 Â· 6 min Â· 1253 words Â· Rob Washington