About secure-development
A secure development skill for software teams, based on the Hack23 ISMS Secure Development Policy. It covers practices from input validation and access control to testing, dependencies, and code review.
Key features
- 01Defines input validation and output encoding
- 02Covers authentication and access control
- 03Sets cryptography and configuration rules
- 04Specifies error handling and security testing
- 05Addresses dependencies and code review
Use cases
- 01Set secure development practices for web apps and APIs
- 02Review authentication, authorization, and input handling
- 03Plan security tests before commits and releases
- 04Manage dependency updates and sensitive configuration
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ hack23/ homepage --skill secure-developmentAfter installing, type/secure-developmentin Claude Code to confirm it loaded.
Details
From the project READMESecure Development Skill
Purpose
This skill ensures all code development follows security-by-design principles as defined in the Hack23 ISMS Secure Development Policy. It applies to all software development activities including web applications, APIs, infrastructure code, and scripts.
Rules
Input Validation
MUST:
- Validate ALL user input at the point of entry (server-side validation is mandatory)
- Use allowlist validation (define what IS allowed, not what ISN'T)
- Validate data type, length, format, and range
- Encode output before rendering to prevent XSS
- Sanitize input before use in queries, commands, or file operations
- Reject invalid input with clear error messages (without exposing system details)
MUST NOT:
- Rely solely on client-side validation
- Trust data from external sources (APIs, databases, files) without validation
- Use blocklist validation as the primary defense
- Echo raw user input back to the browser
- Expose stack traces or system information in error messages
Authentication and Authorization
MUST:
- Enforce authentication for all non-public resources
- Implement multi-factor authentication (MFA) for administrative access
- Use secure session management (HTTPOnly, Secure, SameSite cookies)
- Enforce strong password policies (minimum 12 characters, complexity requirements)
- Implement account lockout after failed login attempts (5 attempts)
- Log all authentication events (success and failure)
- Use the principle of least privilege for all access controls
- Verify authorization at each access point, not just at entry
MUST NOT:
- Store passwords in plaintext or reversible encryption
- Use weak hashing algorithms (MD5, SHA-1) for passwords
- Implement custom authentication schemes without security review
- Share authentication credentials between users or systems
- Grant broad access by default
Cryptography
MUST:
- Use AES-256 for symmetric encryption
- Use RSA-2048 or higher for asymmetric encryption
- Use SHA-256 or higher for hashing
- Enforce TLS 1.2 or higher for all network communications
- Store cryptographic keys securely (use key management systems, not code)
- Use cryptographically secure random number generators (not Math.random())
- Rotate encryption keys according to policy
MUST NOT:
- Hard-code encryption keys, passwords, or secrets in source code
- Use deprecated algorithms (DES, 3DES, MD5, SHA-1, SSL, TLS 1.0/1.1)
- Implement custom cryptographic algorithms
- Store secrets in version control (use secret management tools)
- Use weak or predictable initialization vectors (IVs)
Secure Error Handling
MUST:
- Implement centralized error handling
- Log all security-relevant errors with sufficient detail for investigation
- Return generic error messages to users
- Fail securely (deny access on error, don't fall back to insecure state)
- Clean up resources in finally blocks or using equivalent patterns
- Monitor and alert on unusual error patterns
MUST NOT:
- Expose stack traces to users
- Include sensitive data in error messages (passwords, keys, tokens, PII)
- Log sensitive data (passwords, credit card numbers, SSNs)
- Continue processing after critical security errors
- Suppress security exceptions without logging
Secure Configuration
MUST:
- Use environment variables or secret management for sensitive configuration
- Implement secure defaults (disable debug mode, enable security headers)
- Document all security-relevant configuration options
- Review configuration changes for security implications
- Use principle of least privilege for service accounts and API keys
- Disable unnecessary features, services, and endpoints
MUST NOT:
- Commit configuration files with secrets to version control
- Use default credentials in production
- Enable debug/verbose logging in production
- Expose administrative interfaces to public networks
- Use overly permissive CORS policies
Security Testing
MUST:
- Perform static application security testing (SAST) on every commit
- Perform dynamic application security testing (DAST) before production deployment
- Include security test cases in test suites
- Perform dependency vulnerability scanning
- Test authentication and authorization controls
- Test input validation for injection vulnerabilities
- Document security test results
MUST NOT:
- Deploy code without passing security scans
- Ignore or suppress security findings without proper justification and approval
- Test security controls in production environments
- Use production data in testing environments
Dependency Management
MUST:
- Use dependency scanning tools (e.g., Dependabot, Snyk)
- Keep dependencies up to date with security patches
- Review dependencies for known vulnerabilities before use
- Use package lock files to ensure reproducible builds
- Document approved dependencies and their versions
- Remove unused dependencies
MUST NOT:
- Use dependencies with known critical vulnerabilities
- Use abandoned or unmaintained dependencies
- Download dependencies from untrusted sources
- Bypass dependency version constraints without security review
Code Review
MUST:
- Require peer review for all code changes
- Include security review in code review process
- Use automated security scanning in PR pipelines
- Document security decisions in PR comments
- Verify security requirements are met before approval
MUST NOT:
- Merge code without required approvals
- Bypass security checks in CI/CD pipeline
- Approve your own security-critical changes
Examples
Example 1: Secure Input Validation (JavaScript)
// GOOD: Allowlist validation with type checking
function validateUsername(username) {
// Define what IS allowed
const usernameRegex = /^[a-zA-Z0-9_-]{3,20}$/;
// Validate type
if (typeof username !== 'string') {
throw new Error('Invalid input type');
}
// Validate format and length
if (!usernameRegex.test(username)) {
throw new Error('Username must be 3-20 characters and contain only letters, numbers, hyphens, and underscores');
}
return username;
}
// BAD: No validation
function validateUsernameBad(username) {
return username; // Accepts anything!
}
// BAD: Blocklist validation
function validateUsernameBad2(username) {
if (username.includes('<') || username.includes('>')) {
throw new Error('Invalid characters');
}
return username; // Still vulnerable to many attacks
}
Example 2: Secure Password Hashing (Node.js)
const bcrypt = require('bcrypt');
// GOOD: Use strong hashing with salt
async function hashPassword(password) {
// MUST use at least 12 rounds (2^12 iterations)
const saltRounds = 12;
const hashedPassword = await bcrypt.hash(password, saltRounds);
return hashedPassword;
}
async function verifyPassword(password, hashedPassword) {
return await bcrypt.compare(password, hashedPassword);
}
// BAD: Using weak hashing
const crypto = require('crypto');
function hashPasswordBad(password) {
// NEVER use MD5 or SHA-1 for passwords!
return crypto.createHash('md5').update(password).digest('hex');
}
// BAD: Storing plaintext
function storePasswordBad(password) {
return password; // NEVER store passwords in plaintext!
}
Example 3: Secure Session Management (Express.js)
const session = require('express-session');
const RedisStore = require('connect-redis')(session);
// GOOD: Secure session configuration
app.use(session({
store: new RedisStore({ client: redisClient }),
secret: process.env.SESSION_SECRET, // From environment, not hardcoded
name: 'sessionId', // Don't use default 'connect.sid'
resave: false,
saveUninitialized: false,
cookie: {
secure: true, // MUST use HTTPS
httpOnly: true, // MUST prevent JavaScript access
maxAge: 1800000, // 30 minutes
sameSite: 'strict' // CSRF protection
}
}));
// BAD: Insecure session configuration
app.use(session({
secret: 'mysecret123', // Hardcoded secret!
cookie: {
secure: false, // Allows HTTP
httpOnly: false // Vulnerable to XSS
}
}));
Example 4: Preventing SQL Injection (Node.js)
const mysql = require('mysql2/promise');
// GOOD: Parameterized queries
async function getUserByEmail(email) {
const [rows] = await connection.execute(
'SELECT * FROM users WHERE email = ?',
[email]
);
return rows[0];
}
// BAD: String concatenation
async function getUserByEmailBad(email) {
// NEVER concatenate user input into SQL!
const query = `SELECT * FROM users WHERE email = '${email}'`;
const [rows] = await connection.execute(query);
return rows[0];
}
Example 5: Secure Error Handling (Python)
import logging
# GOOD: Generic error messages to users, detailed logging
def process_payment(user_id, amount):
try:
# Process payment logic
charge_credit_card(user_id, amount)
return {"success": True, "message": "Payment processed"}
except CreditCardError as e:
# Log detailed error securely
logging.error(f"Payment failed for user {user_id}: {str(e)}",
extra={"user_id": user_id, "amount": amount})
# Return generic message to user
return {"success": False, "message": "Payment processing failed. Please try again."}
except Exception as e:
# Log unexpected errors
logging.exception(f"Unexpected error processing payment for user {user_id}")
# Return generic message
return {"success": False, "message": "An error occurred. Please contact support."}
# BAD: Exposing detailed errors
def process_payment_bad(user_id, amount):
try:
charge_credit_card(user_id, amount)
return {"success": True}
except Exception as e:
# NEVER expose stack traces or system details to users!
return {"success": False, "error": str(e), "trace": traceback.format_exc()}
Example 6: Secure Configuration (Environment Variables)
// GOOD: Use environment variables for secrets
require('dotenv').config();
const config = {
database: {
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD, // Never hardcode!
database: process.env.DB_NAME
},
api: {
key: process.env.API_KEY,
secret: process.env.API_SECRET
},
// Secure defaults
security: {
httpsOnly: true,
hstsEnabled: true,
debugMode: false
}
};
// Validate required environment variables
const required = ['DB_HOST', 'DB_USER', 'DB_PASSWORD', 'API_KEY'];
required.forEach(key => {
if (!process.env[key]) {
throw new Error(`Missing required environment variable: ${key}`);
}
});
// BAD: Hardcoded secrets
const configBad = {
database: {
host: 'localhost',
user: 'admin',
password: 'P@ssw0rd123', // NEVER hardcode passwords!
database: 'production'
},
api: {
key: 'sk_live_abcdef123456' // NEVER commit API keys!
}
};
Example 7: XSS Prevention (HTML Output Encoding)
// GOOD: Encode output before rendering
function escapeHtml(unsafe) {
return unsafe
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
function displayUserComment(comment) {
const safeComment = escapeHtml(comment);
document.getElementById('comment').innerHTML = safeComment;
}
// Or use a trusted library
const DOMPurify = require('dompurify');
function displayUserContent(content) {
const clean = DOMPurify.sanitize(content);
document.getElementById('content').innerHTML = clean;
}
// BAD: Direct HTML insertion
function displayUserCommentBad(comment) {
// NEVER insert unsanitized user input into HTML!
document.getElementById('comment').innerHTML = comment;
}
Related ISMS Policies
This skill implements requirements from:
- Secure Development Policy - Primary policy for secure coding practices
- Access Control Policy - Authentication and authorization requirements
- Cryptographic Controls Policy - Encryption and hashing requirements
- Information Security Policy - Overall security framework
- Data Classification Policy - Data handling requirements
Related Documentation
- SECURITY_ARCHITECTURE.md - Security controls and architecture
- THREAT_MODEL.md - Threat analysis and mitigation
- SECURITY.md - Security vulnerability reporting
Compliance Mapping
ISO 27001:2022
- A.8.24 Use of cryptography
- A.8.25 Secure development life cycle
- A.8.26 Application security requirements
- A.8.28 Secure coding
NIST Cybersecurity Framework
- PR.DS-2: Data-in-transit is protected
- PR.DS-6: Integrity checking mechanisms verify software integrity
- PR.IP-2: A System Development Life Cycle to manage systems is implemented
CIS Controls
- Control 16: Application Software Security
- 16.1 Establish and Maintain a Secure Application Development Process
- 16.2 Establish and Maintain a Process to Accept and Address Software Vulnerabilities
- 16.10 Apply Secure Design Principles in Application Architectures
Enforcement
Violations of secure development rules:
- Critical violations (hardcoded secrets, use of banned algorithms): Block deployment, require immediate remediation
- High severity violations (missing input validation, weak authentication): Require remediation before merge
- Medium severity violations: Require remediation within sprint
- Low severity violations: Create technical debt tickets
All violations must be tracked and reported in security metrics.