Claude-code-plugins-plus-skills hubspot-security-basics
install
source · Clone the upstream repo
git clone https://github.com/jeremylongshore/claude-code-plugins-plus-skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/jeremylongshore/claude-code-plugins-plus-skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/plugins/saas-packs/hubspot-pack/skills/hubspot-security-basics" ~/.claude/skills/jeremylongshore-claude-code-plugins-plus-skills-hubspot-security-basics && rm -rf "$T"
manifest:
plugins/saas-packs/hubspot-pack/skills/hubspot-security-basics/SKILL.mdsource content
HubSpot Security Basics
Overview
Security best practices for HubSpot private app tokens, OAuth scopes, webhook signature verification, and secret management.
Prerequisites
- HubSpot private app or OAuth app configured
- Understanding of environment variables and secret management
Instructions
Step 1: Least-Privilege Scopes
Only request the scopes your integration actually uses:
| Use Case | Required Scopes |
|---|---|
| Read contacts | |
| Write contacts | , |
| Read/write deals | , |
| Marketing emails | |
| Forms | |
| Contact lists | , |
| Properties | |
| Custom objects | , , |
| Webhooks | |
Never use: Do not grant
all scopes. If you regenerate a private app token, the old token is immediately revoked.
Step 2: Token Storage
# .env (NEVER commit) HUBSPOT_ACCESS_TOKEN=pat-na1-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx HUBSPOT_WEBHOOK_SECRET=your-webhook-secret # .gitignore .env .env.local .env.*.local
// Validate token is present at startup function validateConfig(): void { if (!process.env.HUBSPOT_ACCESS_TOKEN) { throw new Error('HUBSPOT_ACCESS_TOKEN is required. See .env.example'); } // Never log the token console.log('HubSpot: Token configured', { prefix: process.env.HUBSPOT_ACCESS_TOKEN.substring(0, 8) + '...', }); }
Step 3: Webhook Signature Verification (v3)
HubSpot sends webhooks with signature verification headers:
import crypto from 'crypto'; import express from 'express'; // HubSpot v3 signature verification // Header: X-HubSpot-Signature-v3 function verifyHubSpotSignatureV3( requestBody: string, signature: string, timestamp: string, clientSecret: string, requestUri: string, method: string = 'POST' ): boolean { // Reject if timestamp is older than 5 minutes (replay protection) const now = Math.floor(Date.now() / 1000); if (Math.abs(now - parseInt(timestamp)) > 300) { console.warn('HubSpot webhook timestamp too old'); return false; } // v3: HMAC SHA-256 of method + URI + body + timestamp const sourceString = `${method}${requestUri}${requestBody}${timestamp}`; const expectedSignature = crypto .createHmac('sha256', clientSecret) .update(sourceString) .digest('base64'); return crypto.timingSafeEqual( Buffer.from(signature), Buffer.from(expectedSignature) ); } // Express middleware const webhookRouter = express.Router(); webhookRouter.post('/hubspot', express.raw({ type: 'application/json' }), (req, res) => { const signature = req.headers['x-hubspot-signature-v3'] as string; const timestamp = req.headers['x-hubspot-request-timestamp'] as string; const requestUri = `https://${req.headers.host}${req.originalUrl}`; if (!verifyHubSpotSignatureV3( req.body.toString(), signature, timestamp, process.env.HUBSPOT_WEBHOOK_SECRET!, requestUri )) { return res.status(401).json({ error: 'Invalid signature' }); } const events = JSON.parse(req.body.toString()); // Process events... res.status(200).json({ received: true }); } );
Step 4: Token Rotation Procedure
# 1. Generate new token in HubSpot # Settings > Integrations > Private Apps > [Your App] > Auth tab # Click "Rotate token" (old token revoked immediately) # 2. Update in your secret manager # AWS Secrets Manager aws secretsmanager update-secret --secret-id hubspot/production \ --secret-string '{"access_token":"pat-na1-NEW_TOKEN"}' # GCP Secret Manager echo -n "pat-na1-NEW_TOKEN" | gcloud secrets versions add hubspot-token --data-file=- # 3. Restart/redeploy your application to pick up new token # 4. Verify new token works curl -s https://api.hubapi.com/crm/v3/objects/contacts?limit=1 \ -H "Authorization: Bearer $HUBSPOT_ACCESS_TOKEN" | jq .status
Step 5: Git Secret Scanning
# .github/workflows/secret-scan.yml name: Secret Scan on: [push, pull_request] jobs: scan: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Check for HubSpot tokens run: | if grep -rE "pat-[a-z]{2}[0-9]-[a-f0-9-]{36}" --include="*.ts" --include="*.js" --include="*.json" .; then echo "ERROR: HubSpot access token found in source code" exit 1 fi
Output
- Minimal scopes configured per use case
- Tokens stored in environment variables, never in code
- Webhook signatures verified with replay protection
- Token rotation procedure documented
- CI scanning for leaked tokens
Error Handling
| Security Issue | Detection | Mitigation |
|---|---|---|
| Token in git history | | Rotate token immediately |
| Excessive scopes | Audit in Settings > Private Apps | Remove unneeded scopes |
| Unverified webhooks | Security audit | Add signature verification |
| Token never rotated | Track creation date | Schedule quarterly rotation |
Resources
Next Steps
For production deployment, see
hubspot-prod-checklist.