Initial commit

This commit is contained in:
Zhongwei Li
2025-11-29 18:22:35 +08:00
commit 6fcffca9b0
35 changed files with 8235 additions and 0 deletions

View File

@@ -0,0 +1,294 @@
---
name: hashing-passwords
description: CRITICAL security skill teaching proper credential and password handling. NEVER store passwords, use bcrypt/argon2, NEVER accept third-party credentials. Use when handling authentication, passwords, API keys, or any sensitive credentials.
allowed-tools: Read, Write, Edit, Glob, Grep, Bash, Task, TodoWrite
version: 1.0.0
---
<role>
This skill prevents CRITICAL security failures found in stress testing where 33% of agents had severe security vulnerabilities including Base64 "encryption" for passwords and accepting PayPal passwords directly.
**THIS IS A ZERO-TOLERANCE SECURITY SKILL. NO EXCEPTIONS.**
</role>
<when-to-activate>
This skill activates when:
- Working with passwords or authentication
- Handling API keys, tokens, or credentials
- Storing sensitive user data
- Implementing login, signup, or password reset
- User mentions passwords, credentials, encryption, hashing, or authentication
- Code contains password storage, credential handling, or third-party auth
</when-to-activate>
<overview>
## Critical Security Rules
**RULE 1: NEVER STORE PASSWORDS**
Store password HASHES only, using bcrypt or argon2. Passwords must be:
- Hashed (NOT encrypted, NOT Base64, NOT plaintext)
- Salted automatically by bcrypt/argon2
- Using modern algorithms (bcrypt cost 12+, argon2id)
**RULE 2: NEVER ACCEPT THIRD-PARTY CREDENTIALS**
NEVER ask users for passwords to other services (PayPal, Google, etc.):
- Use OAuth instead
- Use API keys from the service
- Use service-provided SDKs
**RULE 3: NEVER USE ENCODING AS ENCRYPTION**
- Base64 is NOT encryption (trivially reversible)
- URL encoding is NOT security
- Hex encoding is NOT security
**RULE 4: USE PROPER CRYPTOGRAPHY**
- For passwords: bcrypt or argon2
- For encryption: Use established libraries (crypto module, Web Crypto API)
- For API keys: Store in environment variables, use secret management
</overview>
<critical-anti-patterns>
## NEVER DO THIS
**❌ Base64 "Encryption"**: `Buffer.from(password).toString("base64")` is encoding, NOT encryption. Trivially reversible.
**❌ Third-Party Passwords**: Never accept PayPal/Google/etc passwords. Use OAuth.
**❌ Plaintext Storage**: Never store raw passwords. Always hash.
**❌ Weak Hashing**: MD5/SHA-1/SHA-256 too fast. Use bcrypt/argon2.
See `references/never-do-this.md` for detailed examples and failures.
</critical-anti-patterns>
<correct-patterns>
## ✅ CORRECT: bcrypt Password Hashing
```typescript
import bcrypt from "bcrypt";
const SALT_ROUNDS = 12;
async function hashPassword(password: string): Promise<string> {
return await bcrypt.hash(password, SALT_ROUNDS);
}
async function verifyPassword(
password: string,
hash: string
): Promise<boolean> {
return await bcrypt.compare(password, hash);
}
interface User {
id: string;
email: string;
passwordHash: string;
}
```
**Key Points**:
- bcrypt designed for passwords
- Automatic salting
- Cost factor 12+ (prevents brute force)
- Never stores actual password
## ✅ CORRECT: argon2 (More Modern)
```typescript
import argon2 from "argon2";
async function hashPassword(password: string): Promise<string> {
return await argon2.hash(password, {
type: argon2.argon2id,
memoryCost: 2 ** 16,
timeCost: 3,
parallelism: 1
});
}
```
**Advantages**: Memory-hard, resists GPU attacks, latest standard.
## ✅ CORRECT: OAuth for Third-Party Services
```typescript
import { google } from "googleapis";
const oauth2Client = new google.auth.OAuth2(
process.env.GOOGLE_CLIENT_ID,
process.env.GOOGLE_CLIENT_SECRET,
"http://localhost:3000/auth/callback"
);
function getAuthUrl(): string {
return oauth2Client.generateAuthUrl({
access_type: "offline",
scope: ["https://www.googleapis.com/auth/userinfo.email"]
});
}
```
**Key Points**: Token-based, never sees user password, revocable.
## ✅ CORRECT: Environment Variables for API Keys
```typescript
function loadConfig(): Config {
const apiKey = process.env.STRIPE_API_KEY;
if (!apiKey) {
throw new Error("Missing required API key");
}
return { apiKey };
}
```
See `references/correct-implementations.md` for complete examples.
</correct-patterns>
<constraints>
**MUST:**
- Use bcrypt (cost 12+) or argon2id for password hashing
- Store password HASHES only, never passwords
- Use OAuth for third-party service authentication
- Store API keys in environment variables
- Validate password strength (min length, complexity)
- Use HTTPS for all authentication endpoints
**NEVER:**
- Store passwords in any form (plaintext, Base64, encrypted)
- Use MD5, SHA-1, or SHA-256 for passwords
- Accept third-party credentials (PayPal, Google, etc.)
- Hardcode API keys or secrets
- Use encoding (Base64, hex) as "encryption"
- Email passwords to users
- Log passwords (even in dev mode)
**SHOULD:**
- Implement password strength requirements
- Rate-limit login attempts
- Use two-factor authentication (2FA)
- Implement account lockout after failed attempts
- Rotate API keys periodically
- Use secret management services (AWS Secrets Manager, HashiCorp Vault)
</constraints>
<password-requirements>
## Minimum Requirements
- **Length**: 12+ characters (prefer 16+)
- **Complexity**: Uppercase, lowercase, numbers, special chars
- **Validation**: Reject common passwords ("password", "12345678")
See `references/password-validation.md` for complete implementation.
</password-requirements>
<installation>
## Installing Password Libraries
**bcrypt**:
```bash
npm install bcrypt
npm install -D @types/bcrypt
```
**argon2**:
```bash
npm install argon2
npm install -D @types/argon2
```
**Note**: Both require native compilation. Ensure build tools are available.
</installation>
<progressive-disclosure>
## Reference Files
**Detailed Examples**:
- `references/never-do-this.md` - Security failures and anti-patterns
- `references/correct-implementations.md` - Complete working examples
- `references/password-validation.md` - Password strength validation
- `references/emergency-response.md` - Breach response and migration
**Related Skills**:
- **Input Validation**: Use the sanitizing-user-inputs skill
- **Dependencies**: Use the auditing-dependencies skill
- **External Data**: Use the validating-external-data skill
</progressive-disclosure>
<validation>
## Security Implementation Checklist
1. **Password Storage**:
- [ ] Uses bcrypt (cost 12+) or argon2id
- [ ] NEVER stores actual passwords
- [ ] Password hashes stored in separate column
- [ ] No way to retrieve original password
2. **Third-Party Auth**:
- [ ] Uses OAuth/OpenID Connect
- [ ] NEVER asks for third-party passwords
- [ ] Tokens stored securely
- [ ] Follows service Terms of Service
3. **API Keys**:
- [ ] Stored in environment variables
- [ ] Not hardcoded in source
- [ ] Not committed to git
- [ ] `.env` file in `.gitignore`
4. **Password Requirements**:
- [ ] Minimum 12 characters (prefer 16+)
- [ ] Complexity requirements enforced
- [ ] Common passwords rejected
- [ ] Strength meter for users
5. **Additional Security**:
- [ ] HTTPS required for auth endpoints
- [ ] Rate limiting on login
- [ ] Account lockout after failures
- [ ] Password reset via email only
- [ ] No password hints or security questions
</validation>
<common-mistakes>
## Why Developers Make These Mistakes
**"I need to retrieve the password later"**
→ You never need to retrieve passwords. Use password reset instead.
**"Base64 is encryption"**
→ Base64 is encoding for transport, not security.
**"I'll encrypt passwords"**
→ If you can decrypt, so can attackers. Hash, don't encrypt.
**"SHA-256 is secure"**
→ SHA-256 is too fast. Use bcrypt/argon2.
**"I need PayPal credentials to check balance"**
→ Use PayPal's API with OAuth tokens.
</common-mistakes>
<emergency-response>
## If You Find Insecure Password Storage
**IMMEDIATE ACTIONS**:
1. **Stop the application** (if running)
2. **Do NOT commit the code**
3. **Implement proper hashing** (bcrypt/argon2)
4. **Force password reset for all users**
5. **Notify security team**
6. **Assess breach scope**
See `references/emergency-response.md` for complete migration guide.
</emergency-response>

View File

@@ -0,0 +1,195 @@
# Correct Security Implementations
## Password Hashing with bcrypt
```typescript
import bcrypt from "bcrypt";
const SALT_ROUNDS = 12;
async function hashPassword(password: string): Promise<string> {
return await bcrypt.hash(password, SALT_ROUNDS);
}
async function verifyPassword(
password: string,
hash: string
): Promise<boolean> {
return await bcrypt.compare(password, hash);
}
interface User {
id: string;
email: string;
passwordHash: string;
}
async function createUser(
email: string,
password: string
): Promise<User> {
const passwordHash = await hashPassword(password);
return {
id: generateId(),
email,
passwordHash
};
}
async function loginUser(
email: string,
password: string
): Promise<User | null> {
const user = await database.findByEmail(email);
if (!user) {
return null;
}
const isValid = await verifyPassword(password, user.passwordHash);
return isValid ? user : null;
}
```
**Why this is correct**:
- Uses bcrypt (designed for passwords)
- Automatic salting
- Slow (intentional, prevents brute force)
- Cost factor 12 (good balance)
- Never stores actual password
- Async to avoid blocking
## argon2 Implementation
```typescript
import argon2 from "argon2";
async function hashPassword(password: string): Promise<string> {
return await argon2.hash(password, {
type: argon2.argon2id,
memoryCost: 2 ** 16,
timeCost: 3,
parallelism: 1
});
}
async function verifyPassword(
password: string,
hash: string
): Promise<boolean> {
try {
return await argon2.verify(hash, password);
} catch {
return false;
}
}
```
**Why this is correct**:
- argon2id (latest standard, winner of Password Hashing Competition)
- Memory-hard (resists GPU attacks)
- Configurable parameters
- Better than bcrypt for new projects
## OAuth for Third-Party Services
```typescript
import { google } from "googleapis";
const oauth2Client = new google.auth.OAuth2(
process.env.GOOGLE_CLIENT_ID,
process.env.GOOGLE_CLIENT_SECRET,
"http://localhost:3000/auth/callback"
);
function getAuthUrl(): string {
return oauth2Client.generateAuthUrl({
access_type: "offline",
scope: ["https://www.googleapis.com/auth/userinfo.email"]
});
}
async function handleCallback(code: string) {
const { tokens } = await oauth2Client.getToken(code);
oauth2Client.setCredentials(tokens);
return tokens;
}
```
**Why this is correct**:
- Uses OAuth (industry standard)
- Never sees user's Google password
- Token-based authentication
- Revocable access
- Follows Terms of Service
## API Key Storage
```typescript
interface Config {
stripeApiKey: string;
sendgridApiKey: string;
}
function loadConfig(): Config {
const stripeApiKey = process.env.STRIPE_API_KEY;
const sendgridApiKey = process.env.SENDGRID_API_KEY;
if (!stripeApiKey || !sendgridApiKey) {
throw new Error("Missing required API keys");
}
return {
stripeApiKey,
sendgridApiKey
};
}
```
**Why this is correct**:
- Reads from environment variables
- Never hardcoded
- Not committed to git
- Validated at startup
## Session Tokens
```typescript
import crypto from "crypto";
function generateSessionToken(): string {
return crypto.randomBytes(32).toString("hex");
}
interface Session {
id: string;
userId: string;
token: string;
expiresAt: Date;
}
async function createSession(userId: string): Promise<Session> {
const token = generateSessionToken();
const expiresAt = new Date(Date.now() + 24 * 60 * 60 * 1000);
const session = {
id: generateId(),
userId,
token,
expiresAt
};
await database.sessions.insert(session);
return session;
}
```
**Why this is correct**:
- Cryptographically random tokens
- Time-based expiration
- Separate from password
- Can be revoked

View File

@@ -0,0 +1,52 @@
# Emergency Response for Security Breaches
## If You Find Insecure Password Storage
**IMMEDIATE ACTIONS**:
1. **Stop the application** (if running)
2. **Do NOT commit the code**
3. **Implement proper hashing** (bcrypt/argon2)
4. **Force password reset for all users**
5. **Notify security team**
6. **Assess breach scope**
7. **Notify users if breached**
## Migration Path from Insecure Storage
```typescript
async function migratePasswords() {
const users = await database.users.find({ passwordMigrated: false });
for (const user of users) {
if (user.plaintextPassword) {
user.passwordHash = await hashPassword(user.plaintextPassword);
delete user.plaintextPassword;
user.passwordMigrated = true;
await database.users.update(user);
} else {
user.requirePasswordReset = true;
user.passwordMigrated = true;
await database.users.update(user);
}
}
}
```
## Legal and Compliance Considerations
- Document the incident
- Follow breach notification laws (GDPR, CCPA, etc.)
- Preserve evidence for investigation
- Consider engaging legal counsel
- Prepare public communications if needed
## Prevention for Future
- Implement code review processes
- Use automated security scanning
- Train developers on security best practices
- Establish security champions in teams
- Regular security audits

View File

@@ -0,0 +1,87 @@
# Critical Security Anti-Patterns
This document contains detailed examples of security failures found in stress testing where 33% of agents had severe security vulnerabilities.
## ❌ Base64 "Encryption" for Passwords
```typescript
function storePassword(password: string): string {
return Buffer.from(password).toString("base64");
}
function retrievePassword(encoded: string): string {
return Buffer.from(encoded, "base64").toString();
}
```
**CRITICAL FAILURE**:
- Base64 is ENCODING, not encryption
- Trivially reversible: `atob("cGFzc3dvcmQxMjM=")``"password123"`
- Provides ZERO security
- Violates PCI-DSS, GDPR, SOC2, every security standard
- Leads to data breaches and lawsuits
## ❌ Accepting Third-Party Passwords
```typescript
interface UserCredentials {
email: string;
password: string;
paypalEmail: string;
paypalPassword: string;
}
function saveCredentials(creds: UserCredentials) {
database.insert({
...creds,
paypalPassword: encrypt(creds.paypalPassword)
});
}
```
**CRITICAL FAILURE**:
- Violates PayPal Terms of Service
- Violates PCI compliance
- Exposes user to account takeover
- Creates liability for your company
- Even encrypted storage is wrong
## ❌ Plaintext Password Storage
```typescript
interface User {
id: string;
email: string;
password: string;
}
function createUser(email: string, password: string): User {
return {
id: generateId(),
email,
password
};
}
```
**CRITICAL FAILURE**:
- Database breach exposes all passwords
- Password reuse attacks
- Criminal liability
- Regulatory fines
## ❌ Weak Hashing (MD5, SHA-1)
```typescript
import crypto from "crypto";
function hashPassword(password: string): string {
return crypto.createHash("md5").update(password).digest("hex");
}
```
**CRITICAL FAILURE**:
- MD5/SHA-1 designed for speed (bad for passwords)
- Rainbow table attacks
- GPU cracking (billions of hashes/second)
- No salt (identical passwords = identical hashes)

View File

@@ -0,0 +1,66 @@
# Password Strength Validation
## Complete Implementation
```typescript
interface PasswordRequirements {
minLength: number;
requireUppercase: boolean;
requireLowercase: boolean;
requireNumbers: boolean;
requireSpecialChars: boolean;
}
const DEFAULT_REQUIREMENTS: PasswordRequirements = {
minLength: 12,
requireUppercase: true,
requireLowercase: true,
requireNumbers: true,
requireSpecialChars: true
};
function validatePasswordStrength(
password: string,
requirements: PasswordRequirements = DEFAULT_REQUIREMENTS
): { valid: boolean; errors: string[] } {
const errors: string[] = [];
if (password.length < requirements.minLength) {
errors.push(`Password must be at least ${requirements.minLength} characters`);
}
if (requirements.requireUppercase && !/[A-Z]/.test(password)) {
errors.push("Password must contain at least one uppercase letter");
}
if (requirements.requireLowercase && !/[a-z]/.test(password)) {
errors.push("Password must contain at least one lowercase letter");
}
if (requirements.requireNumbers && !/[0-9]/.test(password)) {
errors.push("Password must contain at least one number");
}
if (requirements.requireSpecialChars && !/[!@#$%^&*(),.?":{}|<>]/.test(password)) {
errors.push("Password must contain at least one special character");
}
const commonPasswords = ["password", "12345678", "qwerty", "admin"];
if (commonPasswords.some(common => password.toLowerCase().includes(common))) {
errors.push("Password contains common patterns");
}
return {
valid: errors.length === 0,
errors
};
}
```
## Best Practices
- Minimum 12 characters (prefer 16+)
- Mix of uppercase, lowercase, numbers, special characters
- Reject common passwords and patterns
- Provide user-friendly feedback
- Consider using zxcvbn for entropy estimation