Initial commit
This commit is contained in:
294
skills/hashing-passwords/SKILL.md
Normal file
294
skills/hashing-passwords/SKILL.md
Normal 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>
|
||||
195
skills/hashing-passwords/references/correct-implementations.md
Normal file
195
skills/hashing-passwords/references/correct-implementations.md
Normal 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
|
||||
52
skills/hashing-passwords/references/emergency-response.md
Normal file
52
skills/hashing-passwords/references/emergency-response.md
Normal 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
|
||||
87
skills/hashing-passwords/references/never-do-this.md
Normal file
87
skills/hashing-passwords/references/never-do-this.md
Normal 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)
|
||||
66
skills/hashing-passwords/references/password-validation.md
Normal file
66
skills/hashing-passwords/references/password-validation.md
Normal 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
|
||||
Reference in New Issue
Block a user