security-patterns
Implements authentication, authorization, encryption, secrets management, and security hardening patterns
Install / Use
npx skills add CloudAI-X/claude-workflow-v2 --skill security-patternsInstalls into whichever agent you are using.
SKILL.md
Installable skill definition
Quality Score
Category
SecuritySupported Platforms
Our assessment of security-patterns
security-patterns scores 90/100 on our quality scale, 456th of 915 Security skills we index (top 50%).
Its SKILL.md is 13 KB long, well organised into 31 sections with 19 code examples: a thorough specification that gives an agent plenty to work with.
With 1,416 GitHub stars, it is one of the more widely adopted skills in the catalogue.
Maintenance, license and trust
- The repository was last updated 36 days ago, so security-patterns is actively maintained.
- It is released under the MIT license, a permissive license that allows use, modification and commercial use with attribution.
- Its trust signals score 100/100, with no cautions. These come from repository metadata, not a code audit — read the skill file before letting an agent act on it.
security-patterns compared with similar skills
All 4 of these similar skills score higher than security-patterns; compare them before choosing.
| Skill | Score | Stars | Updated | Format |
|---|---|---|---|---|
| security-patterns (this skill)by CloudAI-X | 90 | 1.4k | 36d ago | SKILL.md |
| Agent-Reachby Panniantong | 100 | 86.6k | 15d ago | CLAUDE.md |
| headroomby headroomlabs-ai | 100 | 74.2k | today | CLAUDE.md |
| Scraplingby D4Vinci | 100 | 84.8k | today | MCP Server |
| crawl4aiby unclecode | 100 | 84.6k | 5d ago | MCP Server |
Frequently asked questions
- How do I install security-patterns?
- Run
npx skills add CloudAI-X/claude-workflow-v2 --skill security-patterns. The install tabs above show the steps for each supported agent. - Which AI agents does security-patterns work with?
- It is written for Universal, as a SKILL.md file. Other agents that read the same format can often use it too.
- Is security-patterns safe to use?
- It is MIT-licensed and scores 100/100 on trust signals. Skills are instructions an agent will follow, so read the file before installing it and do not approve commands you do not understand.
- Is security-patterns still maintained?
- The repository was last updated 36 days ago, so security-patterns is actively maintained.
Skill content
View source on GitHubname: security-patterns description: Implements authentication, authorization, encryption, secrets management, and security hardening patterns. Use when designing auth flows, managing secrets, configuring CORS, implementing rate limiting, or when asked about JWT, OAuth, password hashing, API keys, RBAC, or security best practices.
Security Patterns
When to Load
- Trigger: Auth flows, encryption, secrets management, CORS configuration, input validation, rate limiting
- Skip: No security surface involved in the current task
Security Implementation Workflow
Copy this checklist and track progress:
Security Implementation Progress:
- [ ] Step 1: Choose authentication strategy
- [ ] Step 2: Implement authorization model
- [ ] Step 3: Set up password hashing
- [ ] Step 4: Configure secrets management
- [ ] Step 5: Enable encryption (transit + rest)
- [ ] Step 6: Configure CORS
- [ ] Step 7: Add rate limiting
- [ ] Step 8: Validate against anti-patterns checklist
Authentication Patterns
JWT (JSON Web Tokens)
import jwt from "jsonwebtoken";
function generateTokens(user: User) {
const accessToken = jwt.sign(
{ sub: user.id, role: user.role },
process.env.JWT_SECRET!,
{ expiresIn: "15m", algorithm: "HS256" },
);
const refreshToken = jwt.sign(
{ sub: user.id, tokenVersion: user…[redacted] },
process.env.JWT_REFRESH_SECRET!,
{ expiresIn: "7d" },
);
return { accessToken, refreshToken };
}
// WRONG: localStorage (XSS vulnerable) | CORRECT: httpOnly cookie for refresh, memory for access
res.cookie("refreshToken", refreshToken, {
httpOnly: true,
secure: true,
sameSite: "strict",
maxAge: 7 * 24 * 60 * 60 * 1000,
path: "/api/auth/refresh",
});
JWT Verification Middleware
function authenticate(req: Request, res: Response, next: NextFunction) {
const header = req.headers.authorization;
if (!header?.startsWith("Bearer ")) {
return res.status(401).json({ error: "Missing token" });
}
try {
const token = header.slice(7);
const payload = jwt.verify(token, process.env.JWT_SECRET!) as JwtPayload;
req.user = { id: payload.sub, role: payload.role };
next();
} catch (err) {
if (err instanceof jwt.TokenExpiredError) {
return res.status(401).json({ error: "Token expired" });
}
return res.status(401).json({ error: "Invalid token" });
}
}
Session-Based Auth
import session from "express-session";
import RedisStore from "connect-redis";
app.use(
session({
store: new RedisStore({ client: redisClient }),
secret: process.env.SESSION_SECRET!,
resave: false,
saveUninitialized: false,
cookie: {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
sameSite: "strict",
maxAge: 24 * 60 * 60 * 1000, // 24 hours
},
}),
);
OAuth 2.0 / OIDC Flow Summary
Authorization Code Flow (web apps with backend):
1. Redirect to provider: /authorize?response_type=code&client_id=...&redirect_uri=...&scope=openid email
2. User authenticates, provider redirects back with ?code=AUTHORIZATION_CODE
3. Backend exchanges code for tokens (POST /token with client_secret)
4. Backend receives access_token + id_token, creates session/JWT
PKCE Flow (SPAs, mobile): Same but with code_verifier/code_challenge instead of client_secret
NEVER use Implicit Flow (deprecated, tokens exposed in URL)
API Key Authentication
async function authenticateApiKey(
req: Request,
res: Response,
next: NextFunction,
) {
const apiKey = req.headers["x-api-key"] as string;
if (!apiKey) return res.status(401).json({ error: "API key required" });
// WRONG: Direct comparison (timing attack) | CORRECT: Hash-based lookup
const hashedKey = crypto.createHash("sha256").update(apiKey).digest("hex");
const keyRecord = await db.apiKey.findUnique({ where: { hash: hashedKey } });
if (!keyRecord || keyRecord.revokedAt)
return res.status(401).json({ error: "Invalid API key" });
req.apiClient = { id: keyRecord.clientId, scopes: keyRecord.scopes };
next();
}
Authorization Models
RBAC (Role-Based Access Control)
const PERMISSIONS = {
admin: [
"users:read",
"users:write",
"users:delete",
"posts:read",
"posts:write",
"posts:delete",
],
editor: ["posts:read", "posts:write", "posts:delete", "users:read"],
viewer: ["posts:read", "users:read"],
} as const;
type Role = keyof typeof PERMISSIONS;
function authorize(...requiredPermissions: string[]) {
return (req: Request, res: Response, next: NextFunction) => {
const userPermissions = PERMISSIONS[req.user.role as Role] || [];
const hasPermission = requiredPermissions.every((p) =>
(userPermissions as readonly string[]).includes(p),
);
if (!hasPermission)
return res.status(403).json({ error: "Insufficient permissions" });
next();
};
}
// Usage: app.delete("/api/users/:id", authenticate, authorize("users:delete"), deleteUser);
Resource-Level Authorization
// WRONG: Only checking role, not ownership -- any editor can edit ANY post
// CORRECT: Check ownership or admin role
app.put(
"/api/posts/:id",
authenticate,
authorize("posts:write"),
async (req, res) => {
const post = await db.post.findUnique({ where: { id: req.params.id } });
if (!post) return res.status(404).json({ error: "Not found" });
if (post.authorId !== req.user.id && req.user.role !== "admin") {
return res
.status(403)
.json({ error: "Not authorized to edit this post" });
}
await db.post.update({ where: { id: req.params.id }, data: req.body });
},
);
Password Handling
import bcrypt from "bcrypt";
// WRONG: plaintext or MD5/SHA256 (too fast, brute-forceable)
// CORRECT: bcrypt with appropriate cost factor
const SALT_ROUNDS = 12; // ~250ms on modern hardware
async function hashPassword(password: string): Promise<string> {
return bcrypt.hash(password, SALT_ROUNDS);
}
async function verifyPassword(
password: string,
hash: string,
): Promise<boolean> {
return bcrypt.compare(password, hash); // constant-time comparison built-in
}
// Registration
await db.user.create({
data: { email, password: await hashPassword(req.body.password) },
});
// Login -- WRONG: "Invalid password" (reveals email exists) | CORRECT: generic message
const user = await db.user.findUnique({ where: { email } });
if (!user || !(await verifyPassword(req.body.password, user.password))) {
return res.status(401).json({ error: "Invalid email or password" });
}
Password Policies
function validatePassword(password: string): string[] {
const errors: string[] = [];
if (password.length < 12) errors.push("Minimum 12 characters");
if (password.length > 128) errors.push("Maximum 128 characters");
// Check against breached password lists (haveibeenpwned API or local)
// Do NOT enforce arbitrary complexity rules (uppercase + number + symbol)
// NIST 800-63B recommends length over complexity
return errors;
}
Secrets Management
# WRONG: Hardcoded values in source code
# API_KEY = "some-value-here"
# CORRECT: Environment variables loaded from .env
from dotenv import load_dotenv
import os
load_dotenv()
api_key = os.getenv("API_KEY")
db_url = os.getenv("DATABASE_URL")
# CORRECT: Secrets manager for production
# AWS: Secrets Manager, Parameter Store
# GCP: Secret Manager
# HashiCorp Vault for self-hosted
Secret Rotation
1. Generate new secret value
2. Deploy code that accepts BOTH old and new values
3. Update all consumers to use the new value
4. Verify old value is no longer in use
5. Revoke old value
Never: Rotate in-place without a transition period
Encryption Patterns
In Transit
// Redirect HTTP to HTTPS in production
app.use((req, res, next) => {
if (
req.headers["x-forwarded-proto"] !== "https" &&
process.env.NODE_ENV === "production"
) {
return res.redirect(301, `https://${req.hostname}${req.url}`);
}
next();
});
// HSTS header
app.use((req, res, next) => {
res.setHeader(
"Strict-Transport-Security",
"max-age=31536000; includeSubDomains",
);
next();
});
At Rest
import crypto from "crypto";
const ALGORITHM = "aes-256-gcm";
function encrypt(
plaintext: string,
key: Buffer,
): { ciphertext: string; iv: string; tag: string } {
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv(ALGORITHM, key, iv);
let ciphertext =
cipher.update(plaintext, "utf8", "hex") + cipher.final("hex");
return {
ciphertext,
iv: iv.toString("hex"),
tag: cipher.getAuthTag().toString("hex"),
};
}
function decrypt(
ciphertext: string,
key: Buffer,
iv: string,
tag: string,
): string {
const decipher = crypto.createDecipheriv(
ALGORITHM,
key,
Buffer.from(iv, "hex"),
);
decipher.setAuthTag(Buffer.from(tag, "hex"));
return decipher.update(ciphertext, "hex", "utf8") + decipher.final("utf8");
}
// Use for PII, sensitive data. Encryption key in secrets manager, NOT in code.
CORS Configuration
import cors from "cors";
// WRONG: Allow everything
app.use(cors()); // origin: *, credentials: false
// WRONG: Wildcard with credentials
app.use(cors({ origin: "*", credentials: true })); // browsers reject this
// CORRECT: Explicit allowed origins
const ALLOWED_ORIGINS = [
"https://myapp.com",
"https://admin.myapp.com",
...(process.env.NODE_ENV !== "production" ? ["http://localhost:3000"] : []),
];
app.use(
cors({
origin: (origin, callback) => {
if (!origin || ALLOWED_ORIGINS.includes(origin)) {
callback(null, true);
} else {
callback(new Error("Not allowed by CORS"));
}
},
credentials: true,
methods: ["GET", "POST", "PUT", "PATCH", "DELETE"],
allowedHeaders: ["Content-Type", "Authorization"],
maxAge: 86400, // cache preflight for 24 hours
}),
);
Rate Limiting
import rateLimit from "express-rate-limit";
import RedisStore from "rate-limit-redis";
// Global rate limit
app.use(
rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // 100 requests per window
standardHeaders: true, // RateLimit-* headers
legacyHeaders: false,
store: new RedisStore({
sendCommand: (...args) => redisClient.sendCommand(args),
}),
}),
);
// Strict limit on auth endpoints
app.use(
"/api/auth/login",
rateLimit({
windowMs: 15 * 60 * 1000,
max: 5, // 5 login attempts per 15 min
message: { error: "Too many login attempts. Try again later." },
}),
);
// Per-API-key rate limiting for developer APIs
app.use(
"/api/v1/",
rateLimit({
windowMs: 60 * 1000, // 1 minute
max: 60, // 60 requests per minute
keyGenerator: (req) => req.apiClient?.id || req.ip,
}),
);
Security Headers
import helmet from "helmet";
app.use(helmet()); // Sets many secure headers at once
// Key headers helmet sets:
// X-Content-Type-Options: nosniff
// X-Frame-Options: DENY
// Strict-Transport-Security: max-age=15552000; includeSubDomains
// Content-Security-Policy: default-src 'self'
// Customize CSP for your app
app.use(
helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "data:", "https://cdn.example.com"],
connectSrc: ["'self'", "https://api.example.com"],
},
}),
);
Input Validation
import { z } from "zod";
// WRONG: Trusting user input directly (SQL injection risk)
app.post("/api/users", (req, res) => {
db.query(`SELECT * FROM users WHERE email = '${req.body.email}'`);
});
// CORRECT: Validate with schema, use parameterized queries
const CreateUserSchema = z.object({
email: z.string(
Truncated for display — read the full file on GitHub.
Related Skills
Agent-Reach
86.6kGive your AI agent eyes to see the entire internet. Read & search Twitter, Reddit, YouTube, GitHub, Bilibili, XiaoHongShu — one CLI, zero API fees.
headroom
74.2kCompress tool outputs, logs, files, and RAG chunks before they reach the LLM. 20% fewer tokens for coding agents, 60-95% fewer tokens for JSON, same answers. Library, proxy, MCP server.
Scrapling
84.8k🕷️ An adaptive Web Scraping framework that handles everything from a single request to a full-scale crawl! Don't be shy, join here: https://discord.gg/EMgGbDceNQ and follow here for daily tips and tricks: https://x.com/Scrapling_dev
crawl4ai
84.6kOpen-source web crawler and scraper for LLMs and AI agents: any website into clean, LLM-ready Markdown. Run it yourself, or use Crawl4AI Cloud with one key.
Languages
Trust signals
From repository metadata: license, adoption, age and documentation. Not a code audit — see the Safety scan above for what the skill file itself contains.
