@wazir-dev/cli 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +111 -0
- package/CHANGELOG.md +14 -0
- package/CONTRIBUTING.md +101 -0
- package/LICENSE +21 -0
- package/README.md +314 -0
- package/assets/composition-engine.mmd +34 -0
- package/assets/demo-script.sh +17 -0
- package/assets/logo-dark.svg +14 -0
- package/assets/logo.svg +14 -0
- package/assets/pipeline.mmd +39 -0
- package/assets/record-demo.sh +51 -0
- package/docs/README.md +51 -0
- package/docs/adapters/context-mode.md +60 -0
- package/docs/concepts/architecture.md +87 -0
- package/docs/concepts/artifact-model.md +60 -0
- package/docs/concepts/composition-engine.md +36 -0
- package/docs/concepts/indexing-and-recall.md +160 -0
- package/docs/concepts/observability.md +41 -0
- package/docs/concepts/roles-and-workflows.md +59 -0
- package/docs/concepts/terminology-policy.md +27 -0
- package/docs/getting-started/01-installation.md +78 -0
- package/docs/getting-started/02-first-run.md +102 -0
- package/docs/getting-started/03-adding-to-project.md +15 -0
- package/docs/getting-started/04-host-setup.md +15 -0
- package/docs/guides/ci-integration.md +15 -0
- package/docs/guides/creating-skills.md +15 -0
- package/docs/guides/expertise-module-authoring.md +15 -0
- package/docs/guides/hook-development.md +15 -0
- package/docs/guides/memory-and-learnings.md +34 -0
- package/docs/guides/multi-host-export.md +15 -0
- package/docs/guides/troubleshooting.md +101 -0
- package/docs/guides/writing-custom-roles.md +15 -0
- package/docs/plans/2026-03-15-cli-pipeline-integration-design.md +592 -0
- package/docs/plans/2026-03-15-cli-pipeline-integration-plan.md +598 -0
- package/docs/plans/2026-03-15-docs-enforcement-plan.md +238 -0
- package/docs/readmes/INDEX.md +99 -0
- package/docs/readmes/features/expertise/README.md +171 -0
- package/docs/readmes/features/exports/README.md +222 -0
- package/docs/readmes/features/hooks/README.md +103 -0
- package/docs/readmes/features/hooks/loop-cap-guard.md +133 -0
- package/docs/readmes/features/hooks/post-tool-capture.md +121 -0
- package/docs/readmes/features/hooks/post-tool-lint.md +130 -0
- package/docs/readmes/features/hooks/pre-compact-summary.md +122 -0
- package/docs/readmes/features/hooks/pre-tool-capture-route.md +100 -0
- package/docs/readmes/features/hooks/protected-path-write-guard.md +128 -0
- package/docs/readmes/features/hooks/session-start.md +119 -0
- package/docs/readmes/features/hooks/stop-handoff-harvest.md +125 -0
- package/docs/readmes/features/roles/README.md +157 -0
- package/docs/readmes/features/roles/clarifier.md +152 -0
- package/docs/readmes/features/roles/content-author.md +190 -0
- package/docs/readmes/features/roles/designer.md +193 -0
- package/docs/readmes/features/roles/executor.md +184 -0
- package/docs/readmes/features/roles/learner.md +210 -0
- package/docs/readmes/features/roles/planner.md +182 -0
- package/docs/readmes/features/roles/researcher.md +164 -0
- package/docs/readmes/features/roles/reviewer.md +184 -0
- package/docs/readmes/features/roles/specifier.md +162 -0
- package/docs/readmes/features/roles/verifier.md +215 -0
- package/docs/readmes/features/schemas/README.md +178 -0
- package/docs/readmes/features/skills/README.md +63 -0
- package/docs/readmes/features/skills/brainstorming.md +96 -0
- package/docs/readmes/features/skills/debugging.md +148 -0
- package/docs/readmes/features/skills/design.md +120 -0
- package/docs/readmes/features/skills/prepare-next.md +109 -0
- package/docs/readmes/features/skills/run-audit.md +159 -0
- package/docs/readmes/features/skills/scan-project.md +109 -0
- package/docs/readmes/features/skills/self-audit.md +176 -0
- package/docs/readmes/features/skills/tdd.md +137 -0
- package/docs/readmes/features/skills/using-skills.md +92 -0
- package/docs/readmes/features/skills/verification.md +120 -0
- package/docs/readmes/features/skills/writing-plans.md +104 -0
- package/docs/readmes/features/tooling/README.md +320 -0
- package/docs/readmes/features/workflows/README.md +186 -0
- package/docs/readmes/features/workflows/author.md +181 -0
- package/docs/readmes/features/workflows/clarify.md +154 -0
- package/docs/readmes/features/workflows/design-review.md +171 -0
- package/docs/readmes/features/workflows/design.md +169 -0
- package/docs/readmes/features/workflows/discover.md +162 -0
- package/docs/readmes/features/workflows/execute.md +173 -0
- package/docs/readmes/features/workflows/learn.md +167 -0
- package/docs/readmes/features/workflows/plan-review.md +165 -0
- package/docs/readmes/features/workflows/plan.md +170 -0
- package/docs/readmes/features/workflows/prepare-next.md +167 -0
- package/docs/readmes/features/workflows/review.md +169 -0
- package/docs/readmes/features/workflows/run-audit.md +191 -0
- package/docs/readmes/features/workflows/spec-challenge.md +159 -0
- package/docs/readmes/features/workflows/specify.md +160 -0
- package/docs/readmes/features/workflows/verify.md +177 -0
- package/docs/readmes/packages/README.md +50 -0
- package/docs/readmes/packages/ajv.md +117 -0
- package/docs/readmes/packages/context-mode.md +118 -0
- package/docs/readmes/packages/gray-matter.md +116 -0
- package/docs/readmes/packages/node-test.md +137 -0
- package/docs/readmes/packages/yaml.md +112 -0
- package/docs/reference/configuration-reference.md +159 -0
- package/docs/reference/expertise-index.md +52 -0
- package/docs/reference/git-flow.md +43 -0
- package/docs/reference/hooks.md +87 -0
- package/docs/reference/host-exports.md +50 -0
- package/docs/reference/launch-checklist.md +172 -0
- package/docs/reference/marketplace-listings.md +76 -0
- package/docs/reference/release-process.md +34 -0
- package/docs/reference/roles-reference.md +77 -0
- package/docs/reference/skills.md +33 -0
- package/docs/reference/templates.md +29 -0
- package/docs/reference/tooling-cli.md +94 -0
- package/docs/truth-claims.yaml +222 -0
- package/expertise/PROGRESS.md +63 -0
- package/expertise/README.md +18 -0
- package/expertise/antipatterns/PROGRESS.md +56 -0
- package/expertise/antipatterns/backend/api-design-antipatterns.md +1271 -0
- package/expertise/antipatterns/backend/auth-antipatterns.md +1195 -0
- package/expertise/antipatterns/backend/caching-antipatterns.md +622 -0
- package/expertise/antipatterns/backend/database-antipatterns.md +1038 -0
- package/expertise/antipatterns/backend/index.md +24 -0
- package/expertise/antipatterns/backend/microservices-antipatterns.md +850 -0
- package/expertise/antipatterns/code/architecture-antipatterns.md +919 -0
- package/expertise/antipatterns/code/async-antipatterns.md +622 -0
- package/expertise/antipatterns/code/code-smells.md +1186 -0
- package/expertise/antipatterns/code/dependency-antipatterns.md +1209 -0
- package/expertise/antipatterns/code/error-handling-antipatterns.md +1360 -0
- package/expertise/antipatterns/code/index.md +27 -0
- package/expertise/antipatterns/code/naming-and-abstraction.md +1118 -0
- package/expertise/antipatterns/code/state-management-antipatterns.md +1076 -0
- package/expertise/antipatterns/code/testing-antipatterns.md +1053 -0
- package/expertise/antipatterns/design/accessibility-antipatterns.md +1136 -0
- package/expertise/antipatterns/design/dark-patterns.md +1121 -0
- package/expertise/antipatterns/design/index.md +22 -0
- package/expertise/antipatterns/design/ui-antipatterns.md +1202 -0
- package/expertise/antipatterns/design/ux-antipatterns.md +680 -0
- package/expertise/antipatterns/frontend/css-layout-antipatterns.md +691 -0
- package/expertise/antipatterns/frontend/flutter-antipatterns.md +1827 -0
- package/expertise/antipatterns/frontend/index.md +23 -0
- package/expertise/antipatterns/frontend/mobile-antipatterns.md +573 -0
- package/expertise/antipatterns/frontend/react-antipatterns.md +1128 -0
- package/expertise/antipatterns/frontend/spa-antipatterns.md +1235 -0
- package/expertise/antipatterns/index.md +31 -0
- package/expertise/antipatterns/performance/index.md +20 -0
- package/expertise/antipatterns/performance/performance-antipatterns.md +1013 -0
- package/expertise/antipatterns/performance/premature-optimization.md +623 -0
- package/expertise/antipatterns/performance/scaling-antipatterns.md +785 -0
- package/expertise/antipatterns/process/ai-coding-antipatterns.md +853 -0
- package/expertise/antipatterns/process/code-review-antipatterns.md +656 -0
- package/expertise/antipatterns/process/deployment-antipatterns.md +920 -0
- package/expertise/antipatterns/process/index.md +23 -0
- package/expertise/antipatterns/process/technical-debt-antipatterns.md +647 -0
- package/expertise/antipatterns/security/index.md +20 -0
- package/expertise/antipatterns/security/secrets-antipatterns.md +849 -0
- package/expertise/antipatterns/security/security-theater.md +843 -0
- package/expertise/antipatterns/security/vulnerability-patterns.md +801 -0
- package/expertise/architecture/PROGRESS.md +70 -0
- package/expertise/architecture/data/caching-architecture.md +671 -0
- package/expertise/architecture/data/data-consistency.md +574 -0
- package/expertise/architecture/data/data-modeling.md +536 -0
- package/expertise/architecture/data/event-streams-and-queues.md +634 -0
- package/expertise/architecture/data/index.md +25 -0
- package/expertise/architecture/data/search-architecture.md +663 -0
- package/expertise/architecture/data/sql-vs-nosql.md +708 -0
- package/expertise/architecture/decisions/architecture-decision-records.md +640 -0
- package/expertise/architecture/decisions/build-vs-buy.md +616 -0
- package/expertise/architecture/decisions/index.md +23 -0
- package/expertise/architecture/decisions/monolith-to-microservices.md +790 -0
- package/expertise/architecture/decisions/technology-selection.md +616 -0
- package/expertise/architecture/distributed/cap-theorem-and-tradeoffs.md +800 -0
- package/expertise/architecture/distributed/circuit-breaker-bulkhead.md +741 -0
- package/expertise/architecture/distributed/consensus-and-coordination.md +796 -0
- package/expertise/architecture/distributed/distributed-systems-fundamentals.md +564 -0
- package/expertise/architecture/distributed/idempotency-and-retry.md +796 -0
- package/expertise/architecture/distributed/index.md +25 -0
- package/expertise/architecture/distributed/saga-pattern.md +797 -0
- package/expertise/architecture/foundations/architectural-thinking.md +460 -0
- package/expertise/architecture/foundations/coupling-and-cohesion.md +770 -0
- package/expertise/architecture/foundations/design-principles-solid.md +649 -0
- package/expertise/architecture/foundations/domain-driven-design.md +719 -0
- package/expertise/architecture/foundations/index.md +25 -0
- package/expertise/architecture/foundations/separation-of-concerns.md +472 -0
- package/expertise/architecture/foundations/twelve-factor-app.md +797 -0
- package/expertise/architecture/index.md +34 -0
- package/expertise/architecture/integration/api-design-graphql.md +638 -0
- package/expertise/architecture/integration/api-design-grpc.md +804 -0
- package/expertise/architecture/integration/api-design-rest.md +892 -0
- package/expertise/architecture/integration/index.md +25 -0
- package/expertise/architecture/integration/third-party-integration.md +795 -0
- package/expertise/architecture/integration/webhooks-and-callbacks.md +1152 -0
- package/expertise/architecture/integration/websockets-realtime.md +791 -0
- package/expertise/architecture/mobile-architecture/index.md +22 -0
- package/expertise/architecture/mobile-architecture/mobile-app-architecture.md +780 -0
- package/expertise/architecture/mobile-architecture/mobile-backend-for-frontend.md +670 -0
- package/expertise/architecture/mobile-architecture/offline-first.md +719 -0
- package/expertise/architecture/mobile-architecture/push-and-sync.md +782 -0
- package/expertise/architecture/patterns/cqrs-event-sourcing.md +717 -0
- package/expertise/architecture/patterns/event-driven.md +797 -0
- package/expertise/architecture/patterns/hexagonal-clean-architecture.md +870 -0
- package/expertise/architecture/patterns/index.md +27 -0
- package/expertise/architecture/patterns/layered-architecture.md +736 -0
- package/expertise/architecture/patterns/microservices.md +753 -0
- package/expertise/architecture/patterns/modular-monolith.md +692 -0
- package/expertise/architecture/patterns/monolith.md +626 -0
- package/expertise/architecture/patterns/plugin-architecture.md +735 -0
- package/expertise/architecture/patterns/serverless.md +780 -0
- package/expertise/architecture/scaling/database-scaling.md +615 -0
- package/expertise/architecture/scaling/feature-flags-and-rollouts.md +757 -0
- package/expertise/architecture/scaling/horizontal-vs-vertical.md +606 -0
- package/expertise/architecture/scaling/index.md +24 -0
- package/expertise/architecture/scaling/multi-tenancy.md +800 -0
- package/expertise/architecture/scaling/stateless-design.md +787 -0
- package/expertise/backend/embedded-firmware.md +625 -0
- package/expertise/backend/go.md +853 -0
- package/expertise/backend/index.md +24 -0
- package/expertise/backend/java-spring.md +448 -0
- package/expertise/backend/node-typescript.md +625 -0
- package/expertise/backend/python-fastapi.md +724 -0
- package/expertise/backend/rust.md +458 -0
- package/expertise/backend/solidity.md +711 -0
- package/expertise/composition-map.yaml +443 -0
- package/expertise/content/foundations/content-modeling.md +395 -0
- package/expertise/content/foundations/editorial-standards.md +449 -0
- package/expertise/content/foundations/index.md +24 -0
- package/expertise/content/foundations/microcopy.md +455 -0
- package/expertise/content/foundations/terminology-governance.md +509 -0
- package/expertise/content/index.md +34 -0
- package/expertise/content/patterns/accessibility-copy.md +518 -0
- package/expertise/content/patterns/index.md +24 -0
- package/expertise/content/patterns/notification-content.md +433 -0
- package/expertise/content/patterns/sample-content.md +486 -0
- package/expertise/content/patterns/state-copy.md +439 -0
- package/expertise/design/PROGRESS.md +58 -0
- package/expertise/design/disciplines/dark-mode-theming.md +577 -0
- package/expertise/design/disciplines/design-systems.md +595 -0
- package/expertise/design/disciplines/index.md +25 -0
- package/expertise/design/disciplines/information-architecture.md +800 -0
- package/expertise/design/disciplines/interaction-design.md +788 -0
- package/expertise/design/disciplines/responsive-design.md +552 -0
- package/expertise/design/disciplines/usability-testing.md +516 -0
- package/expertise/design/disciplines/user-research.md +792 -0
- package/expertise/design/foundations/accessibility-design.md +796 -0
- package/expertise/design/foundations/color-theory.md +797 -0
- package/expertise/design/foundations/iconography.md +795 -0
- package/expertise/design/foundations/index.md +26 -0
- package/expertise/design/foundations/motion-and-animation.md +653 -0
- package/expertise/design/foundations/rtl-design.md +585 -0
- package/expertise/design/foundations/spacing-and-layout.md +607 -0
- package/expertise/design/foundations/typography.md +800 -0
- package/expertise/design/foundations/visual-hierarchy.md +761 -0
- package/expertise/design/index.md +32 -0
- package/expertise/design/patterns/authentication-flows.md +474 -0
- package/expertise/design/patterns/content-consumption.md +789 -0
- package/expertise/design/patterns/data-display.md +618 -0
- package/expertise/design/patterns/e-commerce.md +1494 -0
- package/expertise/design/patterns/feedback-and-states.md +642 -0
- package/expertise/design/patterns/forms-and-input.md +819 -0
- package/expertise/design/patterns/gamification.md +801 -0
- package/expertise/design/patterns/index.md +31 -0
- package/expertise/design/patterns/microinteractions.md +449 -0
- package/expertise/design/patterns/navigation.md +800 -0
- package/expertise/design/patterns/notifications.md +705 -0
- package/expertise/design/patterns/onboarding.md +700 -0
- package/expertise/design/patterns/search-and-filter.md +601 -0
- package/expertise/design/patterns/settings-and-preferences.md +768 -0
- package/expertise/design/patterns/social-and-community.md +748 -0
- package/expertise/design/platforms/desktop-native.md +612 -0
- package/expertise/design/platforms/index.md +25 -0
- package/expertise/design/platforms/mobile-android.md +825 -0
- package/expertise/design/platforms/mobile-cross-platform.md +983 -0
- package/expertise/design/platforms/mobile-ios.md +699 -0
- package/expertise/design/platforms/tablet.md +794 -0
- package/expertise/design/platforms/web-dashboard.md +790 -0
- package/expertise/design/platforms/web-responsive.md +550 -0
- package/expertise/design/psychology/behavioral-nudges.md +449 -0
- package/expertise/design/psychology/cognitive-load.md +1191 -0
- package/expertise/design/psychology/error-psychology.md +778 -0
- package/expertise/design/psychology/index.md +22 -0
- package/expertise/design/psychology/persuasive-design.md +736 -0
- package/expertise/design/psychology/user-mental-models.md +623 -0
- package/expertise/design/tooling/open-pencil.md +266 -0
- package/expertise/frontend/angular.md +1073 -0
- package/expertise/frontend/desktop-electron.md +546 -0
- package/expertise/frontend/flutter.md +782 -0
- package/expertise/frontend/index.md +27 -0
- package/expertise/frontend/native-android.md +409 -0
- package/expertise/frontend/native-ios.md +490 -0
- package/expertise/frontend/react-native.md +1160 -0
- package/expertise/frontend/react.md +808 -0
- package/expertise/frontend/vue.md +1089 -0
- package/expertise/humanize/domain-rules-code.md +79 -0
- package/expertise/humanize/domain-rules-content.md +67 -0
- package/expertise/humanize/domain-rules-technical-docs.md +56 -0
- package/expertise/humanize/index.md +35 -0
- package/expertise/humanize/self-audit-checklist.md +87 -0
- package/expertise/humanize/sentence-patterns.md +218 -0
- package/expertise/humanize/vocabulary-blacklist.md +105 -0
- package/expertise/i18n/PROGRESS.md +65 -0
- package/expertise/i18n/advanced/accessibility-and-i18n.md +28 -0
- package/expertise/i18n/advanced/bidirectional-text-algorithm.md +38 -0
- package/expertise/i18n/advanced/complex-scripts.md +30 -0
- package/expertise/i18n/advanced/performance-and-i18n.md +27 -0
- package/expertise/i18n/advanced/testing-i18n.md +28 -0
- package/expertise/i18n/content/content-adaptation.md +23 -0
- package/expertise/i18n/content/locale-specific-formatting.md +23 -0
- package/expertise/i18n/content/machine-translation-integration.md +28 -0
- package/expertise/i18n/content/translation-management.md +29 -0
- package/expertise/i18n/foundations/date-time-calendars.md +67 -0
- package/expertise/i18n/foundations/i18n-architecture.md +272 -0
- package/expertise/i18n/foundations/locale-and-language-tags.md +79 -0
- package/expertise/i18n/foundations/numbers-currency-units.md +61 -0
- package/expertise/i18n/foundations/pluralization-and-gender.md +109 -0
- package/expertise/i18n/foundations/string-externalization.md +236 -0
- package/expertise/i18n/foundations/text-direction-bidi.md +241 -0
- package/expertise/i18n/foundations/unicode-and-encoding.md +86 -0
- package/expertise/i18n/index.md +38 -0
- package/expertise/i18n/platform/backend-i18n.md +31 -0
- package/expertise/i18n/platform/flutter-i18n.md +148 -0
- package/expertise/i18n/platform/native-android-i18n.md +36 -0
- package/expertise/i18n/platform/native-ios-i18n.md +36 -0
- package/expertise/i18n/platform/react-i18n.md +103 -0
- package/expertise/i18n/platform/web-css-i18n.md +81 -0
- package/expertise/i18n/rtl/arabic-specific.md +175 -0
- package/expertise/i18n/rtl/hebrew-specific.md +149 -0
- package/expertise/i18n/rtl/rtl-animations-and-transitions.md +111 -0
- package/expertise/i18n/rtl/rtl-forms-and-input.md +161 -0
- package/expertise/i18n/rtl/rtl-fundamentals.md +211 -0
- package/expertise/i18n/rtl/rtl-icons-and-images.md +181 -0
- package/expertise/i18n/rtl/rtl-layout-mirroring.md +252 -0
- package/expertise/i18n/rtl/rtl-navigation-and-gestures.md +107 -0
- package/expertise/i18n/rtl/rtl-testing-and-qa.md +147 -0
- package/expertise/i18n/rtl/rtl-typography.md +160 -0
- package/expertise/index.md +113 -0
- package/expertise/index.yaml +216 -0
- package/expertise/infrastructure/cloud-aws.md +597 -0
- package/expertise/infrastructure/cloud-gcp.md +599 -0
- package/expertise/infrastructure/cybersecurity.md +816 -0
- package/expertise/infrastructure/database-mongodb.md +447 -0
- package/expertise/infrastructure/database-postgres.md +400 -0
- package/expertise/infrastructure/devops-cicd.md +787 -0
- package/expertise/infrastructure/index.md +27 -0
- package/expertise/performance/PROGRESS.md +50 -0
- package/expertise/performance/backend/api-latency.md +1204 -0
- package/expertise/performance/backend/background-jobs.md +506 -0
- package/expertise/performance/backend/connection-pooling.md +1209 -0
- package/expertise/performance/backend/database-query-optimization.md +515 -0
- package/expertise/performance/backend/index.md +23 -0
- package/expertise/performance/backend/rate-limiting-and-throttling.md +971 -0
- package/expertise/performance/foundations/algorithmic-complexity.md +954 -0
- package/expertise/performance/foundations/caching-strategies.md +489 -0
- package/expertise/performance/foundations/concurrency-and-parallelism.md +847 -0
- package/expertise/performance/foundations/index.md +24 -0
- package/expertise/performance/foundations/measuring-and-profiling.md +440 -0
- package/expertise/performance/foundations/memory-management.md +964 -0
- package/expertise/performance/foundations/performance-budgets.md +1314 -0
- package/expertise/performance/index.md +31 -0
- package/expertise/performance/infrastructure/auto-scaling.md +1059 -0
- package/expertise/performance/infrastructure/cdn-and-edge.md +1081 -0
- package/expertise/performance/infrastructure/index.md +22 -0
- package/expertise/performance/infrastructure/load-balancing.md +1081 -0
- package/expertise/performance/infrastructure/observability.md +1079 -0
- package/expertise/performance/mobile/index.md +23 -0
- package/expertise/performance/mobile/mobile-animations.md +544 -0
- package/expertise/performance/mobile/mobile-memory-battery.md +416 -0
- package/expertise/performance/mobile/mobile-network.md +452 -0
- package/expertise/performance/mobile/mobile-rendering.md +599 -0
- package/expertise/performance/mobile/mobile-startup-time.md +505 -0
- package/expertise/performance/platform-specific/flutter-performance.md +647 -0
- package/expertise/performance/platform-specific/index.md +22 -0
- package/expertise/performance/platform-specific/node-performance.md +1307 -0
- package/expertise/performance/platform-specific/postgres-performance.md +1366 -0
- package/expertise/performance/platform-specific/react-performance.md +1403 -0
- package/expertise/performance/web/bundle-optimization.md +1239 -0
- package/expertise/performance/web/image-and-media.md +636 -0
- package/expertise/performance/web/index.md +24 -0
- package/expertise/performance/web/network-optimization.md +1133 -0
- package/expertise/performance/web/rendering-performance.md +1098 -0
- package/expertise/performance/web/ssr-and-hydration.md +918 -0
- package/expertise/performance/web/web-vitals.md +1374 -0
- package/expertise/quality/accessibility.md +985 -0
- package/expertise/quality/evidence-based-verification.md +499 -0
- package/expertise/quality/index.md +24 -0
- package/expertise/quality/ml-model-audit.md +614 -0
- package/expertise/quality/performance.md +600 -0
- package/expertise/quality/testing-api.md +891 -0
- package/expertise/quality/testing-mobile.md +496 -0
- package/expertise/quality/testing-web.md +849 -0
- package/expertise/security/PROGRESS.md +54 -0
- package/expertise/security/agentic-identity.md +540 -0
- package/expertise/security/compliance-frameworks.md +601 -0
- package/expertise/security/data/data-encryption.md +364 -0
- package/expertise/security/data/data-privacy-gdpr.md +692 -0
- package/expertise/security/data/database-security.md +1171 -0
- package/expertise/security/data/index.md +22 -0
- package/expertise/security/data/pii-handling.md +531 -0
- package/expertise/security/foundations/authentication.md +1041 -0
- package/expertise/security/foundations/authorization.md +603 -0
- package/expertise/security/foundations/cryptography.md +1001 -0
- package/expertise/security/foundations/index.md +25 -0
- package/expertise/security/foundations/owasp-top-10.md +1354 -0
- package/expertise/security/foundations/secrets-management.md +1217 -0
- package/expertise/security/foundations/secure-sdlc.md +700 -0
- package/expertise/security/foundations/supply-chain-security.md +698 -0
- package/expertise/security/index.md +31 -0
- package/expertise/security/infrastructure/cloud-security-aws.md +1296 -0
- package/expertise/security/infrastructure/cloud-security-gcp.md +1376 -0
- package/expertise/security/infrastructure/container-security.md +721 -0
- package/expertise/security/infrastructure/incident-response.md +1295 -0
- package/expertise/security/infrastructure/index.md +24 -0
- package/expertise/security/infrastructure/logging-and-monitoring.md +1618 -0
- package/expertise/security/infrastructure/network-security.md +1337 -0
- package/expertise/security/mobile/index.md +23 -0
- package/expertise/security/mobile/mobile-android-security.md +1218 -0
- package/expertise/security/mobile/mobile-binary-protection.md +1229 -0
- package/expertise/security/mobile/mobile-data-storage.md +1265 -0
- package/expertise/security/mobile/mobile-ios-security.md +1401 -0
- package/expertise/security/mobile/mobile-network-security.md +1520 -0
- package/expertise/security/smart-contract-security.md +594 -0
- package/expertise/security/testing/index.md +22 -0
- package/expertise/security/testing/penetration-testing.md +1258 -0
- package/expertise/security/testing/security-code-review.md +1765 -0
- package/expertise/security/testing/threat-modeling.md +1074 -0
- package/expertise/security/testing/vulnerability-scanning.md +1062 -0
- package/expertise/security/web/api-security.md +586 -0
- package/expertise/security/web/cors-and-headers.md +433 -0
- package/expertise/security/web/csrf.md +562 -0
- package/expertise/security/web/file-upload.md +1477 -0
- package/expertise/security/web/index.md +25 -0
- package/expertise/security/web/injection.md +1375 -0
- package/expertise/security/web/session-management.md +1101 -0
- package/expertise/security/web/xss.md +1158 -0
- package/exports/README.md +17 -0
- package/exports/hosts/claude/.claude/agents/clarifier.md +42 -0
- package/exports/hosts/claude/.claude/agents/content-author.md +63 -0
- package/exports/hosts/claude/.claude/agents/designer.md +55 -0
- package/exports/hosts/claude/.claude/agents/executor.md +55 -0
- package/exports/hosts/claude/.claude/agents/learner.md +51 -0
- package/exports/hosts/claude/.claude/agents/planner.md +53 -0
- package/exports/hosts/claude/.claude/agents/researcher.md +43 -0
- package/exports/hosts/claude/.claude/agents/reviewer.md +54 -0
- package/exports/hosts/claude/.claude/agents/specifier.md +47 -0
- package/exports/hosts/claude/.claude/agents/verifier.md +71 -0
- package/exports/hosts/claude/.claude/commands/author.md +42 -0
- package/exports/hosts/claude/.claude/commands/clarify.md +38 -0
- package/exports/hosts/claude/.claude/commands/design-review.md +46 -0
- package/exports/hosts/claude/.claude/commands/design.md +44 -0
- package/exports/hosts/claude/.claude/commands/discover.md +37 -0
- package/exports/hosts/claude/.claude/commands/execute.md +48 -0
- package/exports/hosts/claude/.claude/commands/learn.md +38 -0
- package/exports/hosts/claude/.claude/commands/plan-review.md +42 -0
- package/exports/hosts/claude/.claude/commands/plan.md +39 -0
- package/exports/hosts/claude/.claude/commands/prepare-next.md +37 -0
- package/exports/hosts/claude/.claude/commands/review.md +40 -0
- package/exports/hosts/claude/.claude/commands/run-audit.md +41 -0
- package/exports/hosts/claude/.claude/commands/spec-challenge.md +41 -0
- package/exports/hosts/claude/.claude/commands/specify.md +38 -0
- package/exports/hosts/claude/.claude/commands/verify.md +37 -0
- package/exports/hosts/claude/.claude/settings.json +34 -0
- package/exports/hosts/claude/CLAUDE.md +19 -0
- package/exports/hosts/claude/export.manifest.json +38 -0
- package/exports/hosts/claude/host-package.json +67 -0
- package/exports/hosts/codex/AGENTS.md +19 -0
- package/exports/hosts/codex/export.manifest.json +38 -0
- package/exports/hosts/codex/host-package.json +41 -0
- package/exports/hosts/cursor/.cursor/hooks.json +16 -0
- package/exports/hosts/cursor/.cursor/rules/wazir-core.mdc +19 -0
- package/exports/hosts/cursor/export.manifest.json +38 -0
- package/exports/hosts/cursor/host-package.json +42 -0
- package/exports/hosts/gemini/GEMINI.md +19 -0
- package/exports/hosts/gemini/export.manifest.json +38 -0
- package/exports/hosts/gemini/host-package.json +41 -0
- package/hooks/README.md +18 -0
- package/hooks/definitions/loop_cap_guard.yaml +21 -0
- package/hooks/definitions/post_tool_capture.yaml +24 -0
- package/hooks/definitions/pre_compact_summary.yaml +19 -0
- package/hooks/definitions/pre_tool_capture_route.yaml +19 -0
- package/hooks/definitions/protected_path_write_guard.yaml +19 -0
- package/hooks/definitions/session_start.yaml +19 -0
- package/hooks/definitions/stop_handoff_harvest.yaml +20 -0
- package/hooks/loop-cap-guard +17 -0
- package/hooks/post-tool-lint +36 -0
- package/hooks/protected-path-write-guard +17 -0
- package/hooks/session-start +41 -0
- package/llms-full.txt +2355 -0
- package/llms.txt +43 -0
- package/package.json +79 -0
- package/roles/README.md +20 -0
- package/roles/clarifier.md +42 -0
- package/roles/content-author.md +63 -0
- package/roles/designer.md +55 -0
- package/roles/executor.md +55 -0
- package/roles/learner.md +51 -0
- package/roles/planner.md +53 -0
- package/roles/researcher.md +43 -0
- package/roles/reviewer.md +54 -0
- package/roles/specifier.md +47 -0
- package/roles/verifier.md +71 -0
- package/schemas/README.md +24 -0
- package/schemas/accepted-learning.schema.json +20 -0
- package/schemas/author-artifact.schema.json +156 -0
- package/schemas/clarification.schema.json +19 -0
- package/schemas/design-artifact.schema.json +80 -0
- package/schemas/docs-claim.schema.json +18 -0
- package/schemas/export-manifest.schema.json +20 -0
- package/schemas/hook.schema.json +67 -0
- package/schemas/host-export-package.schema.json +18 -0
- package/schemas/implementation-plan.schema.json +19 -0
- package/schemas/proposed-learning.schema.json +19 -0
- package/schemas/research.schema.json +18 -0
- package/schemas/review.schema.json +29 -0
- package/schemas/run-manifest.schema.json +18 -0
- package/schemas/spec-challenge.schema.json +18 -0
- package/schemas/spec.schema.json +20 -0
- package/schemas/usage.schema.json +102 -0
- package/schemas/verification-proof.schema.json +29 -0
- package/schemas/wazir-manifest.schema.json +173 -0
- package/skills/README.md +40 -0
- package/skills/brainstorming/SKILL.md +77 -0
- package/skills/debugging/SKILL.md +50 -0
- package/skills/design/SKILL.md +61 -0
- package/skills/dispatching-parallel-agents/SKILL.md +128 -0
- package/skills/executing-plans/SKILL.md +70 -0
- package/skills/finishing-a-development-branch/SKILL.md +169 -0
- package/skills/humanize/SKILL.md +123 -0
- package/skills/init-pipeline/SKILL.md +124 -0
- package/skills/prepare-next/SKILL.md +20 -0
- package/skills/receiving-code-review/SKILL.md +123 -0
- package/skills/requesting-code-review/SKILL.md +105 -0
- package/skills/requesting-code-review/code-reviewer.md +108 -0
- package/skills/run-audit/SKILL.md +197 -0
- package/skills/scan-project/SKILL.md +41 -0
- package/skills/self-audit/SKILL.md +153 -0
- package/skills/subagent-driven-development/SKILL.md +154 -0
- package/skills/subagent-driven-development/code-quality-reviewer-prompt.md +26 -0
- package/skills/subagent-driven-development/implementer-prompt.md +102 -0
- package/skills/subagent-driven-development/spec-reviewer-prompt.md +61 -0
- package/skills/tdd/SKILL.md +23 -0
- package/skills/using-git-worktrees/SKILL.md +163 -0
- package/skills/using-skills/SKILL.md +95 -0
- package/skills/verification/SKILL.md +22 -0
- package/skills/wazir/SKILL.md +463 -0
- package/skills/writing-plans/SKILL.md +30 -0
- package/skills/writing-skills/SKILL.md +157 -0
- package/skills/writing-skills/anthropic-best-practices.md +122 -0
- package/skills/writing-skills/persuasion-principles.md +50 -0
- package/templates/README.md +20 -0
- package/templates/artifacts/README.md +10 -0
- package/templates/artifacts/accepted-learning.md +19 -0
- package/templates/artifacts/accepted-learning.template.json +12 -0
- package/templates/artifacts/author.md +74 -0
- package/templates/artifacts/author.template.json +19 -0
- package/templates/artifacts/clarification.md +21 -0
- package/templates/artifacts/clarification.template.json +12 -0
- package/templates/artifacts/execute-notes.md +19 -0
- package/templates/artifacts/implementation-plan.md +21 -0
- package/templates/artifacts/implementation-plan.template.json +11 -0
- package/templates/artifacts/learning-proposal.md +19 -0
- package/templates/artifacts/next-run-handoff.md +21 -0
- package/templates/artifacts/plan-review.md +19 -0
- package/templates/artifacts/proposed-learning.template.json +12 -0
- package/templates/artifacts/research.md +21 -0
- package/templates/artifacts/research.template.json +12 -0
- package/templates/artifacts/review-findings.md +19 -0
- package/templates/artifacts/review.template.json +11 -0
- package/templates/artifacts/run-manifest.template.json +8 -0
- package/templates/artifacts/spec-challenge.md +19 -0
- package/templates/artifacts/spec-challenge.template.json +11 -0
- package/templates/artifacts/spec.md +21 -0
- package/templates/artifacts/spec.template.json +12 -0
- package/templates/artifacts/verification-proof.md +19 -0
- package/templates/artifacts/verification-proof.template.json +11 -0
- package/templates/examples/accepted-learning.example.json +14 -0
- package/templates/examples/author.example.json +152 -0
- package/templates/examples/clarification.example.json +15 -0
- package/templates/examples/docs-claim.example.json +8 -0
- package/templates/examples/export-manifest.example.json +7 -0
- package/templates/examples/host-export-package.example.json +11 -0
- package/templates/examples/implementation-plan.example.json +17 -0
- package/templates/examples/proposed-learning.example.json +13 -0
- package/templates/examples/research.example.json +15 -0
- package/templates/examples/research.example.md +6 -0
- package/templates/examples/review.example.json +17 -0
- package/templates/examples/run-manifest.example.json +9 -0
- package/templates/examples/spec-challenge.example.json +14 -0
- package/templates/examples/spec.example.json +21 -0
- package/templates/examples/verification-proof.example.json +21 -0
- package/templates/examples/wazir-manifest.example.yaml +65 -0
- package/templates/task-definition-schema.md +99 -0
- package/tooling/README.md +20 -0
- package/tooling/src/adapters/context-mode.js +50 -0
- package/tooling/src/capture/command.js +376 -0
- package/tooling/src/capture/store.js +99 -0
- package/tooling/src/capture/usage.js +270 -0
- package/tooling/src/checks/branches.js +50 -0
- package/tooling/src/checks/brand-truth.js +110 -0
- package/tooling/src/checks/changelog.js +231 -0
- package/tooling/src/checks/command-registry.js +36 -0
- package/tooling/src/checks/commits.js +102 -0
- package/tooling/src/checks/docs-drift.js +103 -0
- package/tooling/src/checks/docs-truth.js +201 -0
- package/tooling/src/checks/runtime-surface.js +156 -0
- package/tooling/src/cli.js +116 -0
- package/tooling/src/command-options.js +56 -0
- package/tooling/src/commands/validate.js +320 -0
- package/tooling/src/doctor/command.js +91 -0
- package/tooling/src/export/command.js +77 -0
- package/tooling/src/export/compiler.js +498 -0
- package/tooling/src/guards/loop-cap-guard.js +52 -0
- package/tooling/src/guards/protected-path-write-guard.js +67 -0
- package/tooling/src/index/command.js +152 -0
- package/tooling/src/index/storage.js +1061 -0
- package/tooling/src/index/summarizers.js +261 -0
- package/tooling/src/loaders.js +18 -0
- package/tooling/src/project-root.js +22 -0
- package/tooling/src/recall/command.js +225 -0
- package/tooling/src/schema-validator.js +30 -0
- package/tooling/src/state-root.js +40 -0
- package/tooling/src/status/command.js +71 -0
- package/wazir.manifest.yaml +135 -0
- package/workflows/README.md +19 -0
- package/workflows/author.md +42 -0
- package/workflows/clarify.md +38 -0
- package/workflows/design-review.md +46 -0
- package/workflows/design.md +44 -0
- package/workflows/discover.md +37 -0
- package/workflows/execute.md +48 -0
- package/workflows/learn.md +38 -0
- package/workflows/plan-review.md +42 -0
- package/workflows/plan.md +39 -0
- package/workflows/prepare-next.md +37 -0
- package/workflows/review.md +40 -0
- package/workflows/run-audit.md +41 -0
- package/workflows/spec-challenge.md +41 -0
- package/workflows/specify.md +38 -0
- package/workflows/verify.md +37 -0
|
@@ -0,0 +1,1101 @@
|
|
|
1
|
+
# Session Management Security
|
|
2
|
+
|
|
3
|
+
> Expertise module for AI agents implementing secure session handling.
|
|
4
|
+
> Sources: OWASP Session Management Cheat Sheet, NIST SP 800-63B, PCI-DSS 4.0, CWE/CVE databases, PortSwigger research, vendor advisories (2024-2026).
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 1. Threat Landscape
|
|
9
|
+
|
|
10
|
+
### 1.1 Attack Categories
|
|
11
|
+
|
|
12
|
+
| Attack | Description | Prevalence |
|
|
13
|
+
|--------|-------------|------------|
|
|
14
|
+
| Session Hijacking | Attacker steals a valid session token and replays it to impersonate the victim | Critical — rising sharply with infostealer malware |
|
|
15
|
+
| Session Fixation | Attacker sets a known session ID before the victim authenticates, then reuses it | High — CVE-2024-7341 (Keycloak), CVE-2024-38513 (GoFiber) |
|
|
16
|
+
| Session Prediction | Attacker guesses or brute-forces session IDs due to weak entropy | Medium — still found in custom frameworks |
|
|
17
|
+
| Cookie Theft via XSS | Injected script reads `document.cookie` and exfiltrates session tokens | Critical — primary vector when HttpOnly is missing |
|
|
18
|
+
| CSRF via Weak Session Config | Cross-site requests ride on ambient cookies lacking SameSite protection | High — mitigated by SameSite=Lax default in modern browsers |
|
|
19
|
+
| Token Replay (MFA Bypass) | Stolen session tokens bypass MFA because authentication already completed | Critical — 17 billion stolen cookie records recaptured from dark web in 2024 |
|
|
20
|
+
|
|
21
|
+
### 1.2 Real-World Breaches
|
|
22
|
+
|
|
23
|
+
**Chrome Extension Supply Chain Attack (December 2024):**
|
|
24
|
+
Attackers injected malicious code into legitimate Chrome browser extensions used by enterprises for social media advertising. The compromised extensions exfiltrated browser cookies and session tokens during a 25-hour window, granting attackers access to authenticated sessions on Facebook Ads and AI platforms. Multiple companies were affected before the extensions were revoked.
|
|
25
|
+
|
|
26
|
+
**Keycloak SAML Session Fixation (CVE-2024-7341):**
|
|
27
|
+
Keycloak's SAML adapter failed to rotate the session ID at login time. An attacker who obtained a pre-authentication session cookie could wait for the victim to authenticate, then reuse the same cookie to access the victim's authenticated session. Affected all Keycloak versions using SAML adapters until patched.
|
|
28
|
+
|
|
29
|
+
**GoFiber Session Fixation (CVE-2024-38513):**
|
|
30
|
+
GoFiber versions prior to 2.52.5 allowed attackers to supply arbitrary `session_id` values. Because the framework accepted attacker-controlled session identifiers without regeneration, authenticated sessions could be hijacked after a victim logged in with the attacker-supplied ID.
|
|
31
|
+
|
|
32
|
+
**Ivanti EPM Stored XSS to Session Hijacking (CVE-2025-10573, CVSS 9.6):**
|
|
33
|
+
Ivanti Endpoint Manager versions through 2024 SU4 allowed unauthenticated JavaScript injection. Attackers leveraged stored XSS to steal admin session cookies, escalating to full endpoint management control.
|
|
34
|
+
|
|
35
|
+
**Microsoft 365 & Okta Token Theft Campaigns (2024-2025):**
|
|
36
|
+
Adversary-in-the-middle (AiTM) phishing kits like EvilProxy and Evilginx captured session tokens after victims completed MFA. Stolen tokens were replayed from attacker infrastructure, bypassing conditional access policies. Multiple Fortune 500 companies reported unauthorized access to email and cloud resources.
|
|
37
|
+
|
|
38
|
+
### 1.3 Trends
|
|
39
|
+
|
|
40
|
+
- **Infostealer malware dominance:** Lumma, RisePro, Vidar, Stealc, and RedLine shifted focus from password theft to session cookie/token exfiltration. Over 17 billion stolen cookie records were found on dark web markets in 2024.
|
|
41
|
+
- **Post-MFA session theft:** Attackers increasingly target the session layer because stealing a live session bypasses all authentication controls including MFA.
|
|
42
|
+
- **Identity-based hijacking:** Modern session hijacking is no longer network-based (sniffing). It is identity-based, performed over the public internet against cloud applications using stolen cookies, tokens, and session IDs.
|
|
43
|
+
- **Browser extension as attack vector:** Supply chain attacks on browser extensions provide direct access to cookies and session storage within the browser context.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 2. Core Security Principles
|
|
48
|
+
|
|
49
|
+
### 2.1 Session ID Entropy
|
|
50
|
+
|
|
51
|
+
Session identifiers MUST be cryptographically random and unpredictable.
|
|
52
|
+
|
|
53
|
+
- **Minimum 128 bits of entropy** (OWASP recommendation). This translates to at least 32 hexadecimal characters or 22 base64 characters.
|
|
54
|
+
- Use a CSPRNG (Cryptographically Secure Pseudo-Random Number Generator): `crypto.randomBytes()` in Node.js, `secrets.token_hex()` in Python, `SecureRandom` in Java.
|
|
55
|
+
- NEVER derive session IDs from user data (username, email, timestamp, sequential counters).
|
|
56
|
+
- NEVER expose session IDs in URLs, error messages, or logs.
|
|
57
|
+
|
|
58
|
+
### 2.2 Secure Cookie Attributes
|
|
59
|
+
|
|
60
|
+
Every session cookie MUST set all of the following attributes:
|
|
61
|
+
|
|
62
|
+
| Attribute | Value | Purpose |
|
|
63
|
+
|-----------|-------|---------|
|
|
64
|
+
| `HttpOnly` | `true` | Prevents JavaScript access — blocks XSS-based cookie theft |
|
|
65
|
+
| `Secure` | `true` | Cookie transmitted only over HTTPS — blocks network sniffing |
|
|
66
|
+
| `SameSite` | `Strict` or `Lax` | Restricts cross-origin cookie sending — mitigates CSRF |
|
|
67
|
+
| `Path` | `/` (or narrowest scope) | Limits cookie scope to intended paths |
|
|
68
|
+
| `Domain` | Omit or set to exact domain | Prevents cookie leakage to subdomains when omitted |
|
|
69
|
+
| `Max-Age` / `Expires` | Set explicit value | Controls cookie lifetime — avoid persistent session cookies |
|
|
70
|
+
|
|
71
|
+
**SameSite values explained:**
|
|
72
|
+
- `Strict`: Cookie never sent on cross-site requests. Strongest protection but breaks legitimate cross-site navigation (e.g., links from email).
|
|
73
|
+
- `Lax` (browser default since Chrome 80): Cookie sent on top-level GET navigations from external sites but not on POST or subresource requests. Good balance for most applications.
|
|
74
|
+
- `None`: Cookie sent on all cross-site requests. Requires `Secure` flag. Only use when cross-origin cookie sharing is genuinely needed (e.g., embedded iframes, OAuth flows).
|
|
75
|
+
|
|
76
|
+
### 2.3 Session Expiration
|
|
77
|
+
|
|
78
|
+
Implement both idle and absolute timeouts:
|
|
79
|
+
|
|
80
|
+
- **Idle timeout:** Destroy session after a period of inactivity (15 minutes for high-security systems per NIST SP 800-63B and PCI-DSS 4.0 Requirement 8.2.8; 30 minutes for general applications).
|
|
81
|
+
- **Absolute timeout:** Destroy session after a maximum lifetime regardless of activity (4-8 hours typical; 24 hours maximum for low-risk applications).
|
|
82
|
+
- **Server-side enforcement:** NEVER rely solely on cookie expiration. The server must track last-activity timestamps and reject expired sessions.
|
|
83
|
+
|
|
84
|
+
### 2.4 Session Rotation on Auth State Change
|
|
85
|
+
|
|
86
|
+
Regenerate the session ID whenever the authentication or privilege state changes:
|
|
87
|
+
|
|
88
|
+
- On successful login (prevents session fixation).
|
|
89
|
+
- On privilege escalation (e.g., entering an admin panel).
|
|
90
|
+
- On password change or MFA enrollment.
|
|
91
|
+
- On any sensitive operation requiring re-authentication.
|
|
92
|
+
|
|
93
|
+
The old session ID must be invalidated server-side immediately after rotation.
|
|
94
|
+
|
|
95
|
+
### 2.5 Server-Side Session Storage
|
|
96
|
+
|
|
97
|
+
- Store session data server-side (Redis, database, encrypted file store). The cookie should contain only the session ID.
|
|
98
|
+
- NEVER store sensitive data (roles, permissions, user attributes) solely in client-side cookies or tokens without server-side validation.
|
|
99
|
+
- Sign session cookies with HMAC to detect tampering (express-session does this by default with the `secret` option).
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## 3. Implementation Patterns
|
|
104
|
+
|
|
105
|
+
### 3.1 Server-Side Sessions with Express and Redis
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
// SECURE: Express session with Redis store
|
|
109
|
+
import express from 'express';
|
|
110
|
+
import session from 'express-session';
|
|
111
|
+
import { createClient } from 'redis';
|
|
112
|
+
import RedisStore from 'connect-redis';
|
|
113
|
+
|
|
114
|
+
const app = express();
|
|
115
|
+
|
|
116
|
+
const redisClient = createClient({
|
|
117
|
+
url: process.env.REDIS_URL,
|
|
118
|
+
socket: {
|
|
119
|
+
tls: true, // Encrypt Redis connection in production
|
|
120
|
+
rejectUnauthorized: true,
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
await redisClient.connect();
|
|
124
|
+
|
|
125
|
+
app.use(session({
|
|
126
|
+
store: new RedisStore({
|
|
127
|
+
client: redisClient,
|
|
128
|
+
prefix: 'sess:', // Namespace session keys
|
|
129
|
+
ttl: 1800, // 30 minutes server-side TTL
|
|
130
|
+
}),
|
|
131
|
+
name: '__Host-sid', // __Host- prefix enforces Secure + Path=/
|
|
132
|
+
secret: process.env.SESSION_SECRET, // Min 256-bit secret; rotate periodically
|
|
133
|
+
resave: false, // Do not save session if unmodified
|
|
134
|
+
saveUninitialized: false, // Do not create session until data stored
|
|
135
|
+
cookie: {
|
|
136
|
+
httpOnly: true,
|
|
137
|
+
secure: true, // HTTPS only
|
|
138
|
+
sameSite: 'lax',
|
|
139
|
+
maxAge: 1800000, // 30 minutes in milliseconds
|
|
140
|
+
path: '/',
|
|
141
|
+
domain: undefined, // Omit to restrict to exact origin
|
|
142
|
+
},
|
|
143
|
+
rolling: true, // Reset expiry on each request (idle timeout)
|
|
144
|
+
}));
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### 3.2 Session Rotation on Login
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
// SECURE: Regenerate session ID after authentication
|
|
151
|
+
app.post('/login', async (req, res) => {
|
|
152
|
+
const user = await authenticate(req.body.email, req.body.password);
|
|
153
|
+
if (!user) {
|
|
154
|
+
return res.status(401).json({ error: 'Invalid credentials' });
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Regenerate session to prevent fixation attacks
|
|
158
|
+
req.session.regenerate((err) => {
|
|
159
|
+
if (err) {
|
|
160
|
+
return res.status(500).json({ error: 'Session error' });
|
|
161
|
+
}
|
|
162
|
+
req.session.userId = user.id;
|
|
163
|
+
req.session.role = user.role;
|
|
164
|
+
req.session.loginTime = Date.now(); // Track absolute timeout
|
|
165
|
+
req.session.lastActivity = Date.now(); // Track idle timeout
|
|
166
|
+
|
|
167
|
+
req.session.save((err) => {
|
|
168
|
+
if (err) {
|
|
169
|
+
return res.status(500).json({ error: 'Session save error' });
|
|
170
|
+
}
|
|
171
|
+
res.json({ message: 'Login successful' });
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### 3.3 Session Rotation on Privilege Escalation
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
// SECURE: Rotate session before granting elevated privileges
|
|
181
|
+
app.post('/admin/elevate', requireAuth, async (req, res) => {
|
|
182
|
+
const verified = await verifyMFA(req.session.userId, req.body.mfaCode);
|
|
183
|
+
if (!verified) {
|
|
184
|
+
return res.status(403).json({ error: 'MFA verification failed' });
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const oldSessionData = { ...req.session };
|
|
188
|
+
req.session.regenerate((err) => {
|
|
189
|
+
if (err) return res.status(500).json({ error: 'Session error' });
|
|
190
|
+
|
|
191
|
+
// Restore user data with elevated flag
|
|
192
|
+
req.session.userId = oldSessionData.userId;
|
|
193
|
+
req.session.role = oldSessionData.role;
|
|
194
|
+
req.session.isElevated = true;
|
|
195
|
+
req.session.elevatedAt = Date.now();
|
|
196
|
+
req.session.save(() => res.json({ message: 'Privileges elevated' }));
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### 3.4 Concurrent Session Limits
|
|
202
|
+
|
|
203
|
+
```typescript
|
|
204
|
+
// SECURE: Limit active sessions per user
|
|
205
|
+
const MAX_SESSIONS_PER_USER = 3;
|
|
206
|
+
|
|
207
|
+
async function enforceSessionLimit(userId: string, currentSessionId: string): Promise<void> {
|
|
208
|
+
const sessionKeys = await redisClient.keys(`sess:user:${userId}:*`);
|
|
209
|
+
|
|
210
|
+
if (sessionKeys.length >= MAX_SESSIONS_PER_USER) {
|
|
211
|
+
// Sort by creation time, remove oldest sessions
|
|
212
|
+
const sessions = await Promise.all(
|
|
213
|
+
sessionKeys.map(async (key) => ({
|
|
214
|
+
key,
|
|
215
|
+
data: JSON.parse(await redisClient.get(key) || '{}'),
|
|
216
|
+
}))
|
|
217
|
+
);
|
|
218
|
+
sessions.sort((a, b) => (a.data.loginTime || 0) - (b.data.loginTime || 0));
|
|
219
|
+
|
|
220
|
+
// Destroy oldest sessions exceeding the limit
|
|
221
|
+
const toRemove = sessions.slice(0, sessions.length - MAX_SESSIONS_PER_USER + 1);
|
|
222
|
+
for (const session of toRemove) {
|
|
223
|
+
if (session.key !== `sess:user:${userId}:${currentSessionId}`) {
|
|
224
|
+
await redisClient.del(session.key);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Register current session
|
|
230
|
+
await redisClient.set(
|
|
231
|
+
`sess:user:${userId}:${currentSessionId}`,
|
|
232
|
+
JSON.stringify({ loginTime: Date.now() }),
|
|
233
|
+
{ EX: 1800 }
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### 3.5 Secure Remember-Me Implementation
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
// SECURE: Remember-me with separate long-lived token (NOT the session cookie)
|
|
242
|
+
import crypto from 'crypto';
|
|
243
|
+
|
|
244
|
+
interface RememberMeToken {
|
|
245
|
+
selector: string; // Lookup key (indexed, not secret)
|
|
246
|
+
validator: string; // Secret token (hashed in DB)
|
|
247
|
+
userId: string;
|
|
248
|
+
expiresAt: Date;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
async function createRememberMeToken(userId: string): Promise<string> {
|
|
252
|
+
const selector = crypto.randomBytes(16).toString('hex');
|
|
253
|
+
const validator = crypto.randomBytes(32).toString('hex');
|
|
254
|
+
const hashedValidator = crypto
|
|
255
|
+
.createHash('sha256')
|
|
256
|
+
.update(validator)
|
|
257
|
+
.digest('hex');
|
|
258
|
+
|
|
259
|
+
await db.rememberMeTokens.create({
|
|
260
|
+
selector,
|
|
261
|
+
validator: hashedValidator, // Store HASH, not plaintext
|
|
262
|
+
userId,
|
|
263
|
+
expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000), // 30 days
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
// Return selector:validator as cookie value
|
|
267
|
+
return `${selector}:${validator}`;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
async function validateRememberMe(token: string): Promise<string | null> {
|
|
271
|
+
const [selector, validator] = token.split(':');
|
|
272
|
+
if (!selector || !validator) return null;
|
|
273
|
+
|
|
274
|
+
const record = await db.rememberMeTokens.findOne({
|
|
275
|
+
where: { selector, expiresAt: { $gt: new Date() } },
|
|
276
|
+
});
|
|
277
|
+
if (!record) return null;
|
|
278
|
+
|
|
279
|
+
const hashedValidator = crypto
|
|
280
|
+
.createHash('sha256')
|
|
281
|
+
.update(validator)
|
|
282
|
+
.digest('hex');
|
|
283
|
+
|
|
284
|
+
// Constant-time comparison to prevent timing attacks
|
|
285
|
+
if (!crypto.timingSafeEqual(
|
|
286
|
+
Buffer.from(hashedValidator),
|
|
287
|
+
Buffer.from(record.validator)
|
|
288
|
+
)) {
|
|
289
|
+
return null;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Rotate the token on use (one-time use)
|
|
293
|
+
await db.rememberMeTokens.destroy({ where: { selector } });
|
|
294
|
+
return record.userId;
|
|
295
|
+
}
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### 3.6 JWT as Sessions — Tradeoffs
|
|
299
|
+
|
|
300
|
+
JWTs are NOT recommended as session tokens for most web applications. Use them only when statelessness is a hard architectural requirement (e.g., microservices, cross-domain SSO).
|
|
301
|
+
|
|
302
|
+
| Aspect | Server-Side Sessions | JWT Sessions |
|
|
303
|
+
|--------|---------------------|--------------|
|
|
304
|
+
| Revocation | Immediate (delete from store) | Requires blocklist or short expiry |
|
|
305
|
+
| Scalability | Requires shared store (Redis) | Stateless — no shared store |
|
|
306
|
+
| Size | Small cookie (~32 bytes) | Large cookie (1-2 KB+) |
|
|
307
|
+
| Security | Server controls all data | Claims visible in Base64 payload |
|
|
308
|
+
| Logout | Instant invalidation | Token valid until expiry unless blocklisted |
|
|
309
|
+
| Replay | Detectable via server state | No built-in replay detection |
|
|
310
|
+
|
|
311
|
+
**If you must use JWTs:**
|
|
312
|
+
- Use short-lived access tokens (5-15 minutes).
|
|
313
|
+
- Implement refresh token rotation with one-time-use enforcement.
|
|
314
|
+
- Maintain a server-side blocklist for revocation (defeats the stateless benefit).
|
|
315
|
+
- Never store JWTs in `localStorage` (XSS-vulnerable). Use HttpOnly cookies.
|
|
316
|
+
- Validate signature, `iss`, `aud`, `exp`, and `nbf` claims on every request.
|
|
317
|
+
- Pin the algorithm server-side — never trust the `alg` header from the token.
|
|
318
|
+
|
|
319
|
+
### 3.7 JWT Refresh Token Flow
|
|
320
|
+
|
|
321
|
+
```typescript
|
|
322
|
+
// SECURE: JWT refresh token rotation
|
|
323
|
+
import jwt from 'jsonwebtoken';
|
|
324
|
+
|
|
325
|
+
const ACCESS_TOKEN_TTL = '15m';
|
|
326
|
+
const REFRESH_TOKEN_TTL = '7d';
|
|
327
|
+
|
|
328
|
+
async function issueTokenPair(userId: string): Promise<{
|
|
329
|
+
accessToken: string;
|
|
330
|
+
refreshToken: string;
|
|
331
|
+
}> {
|
|
332
|
+
const accessToken = jwt.sign(
|
|
333
|
+
{ sub: userId, type: 'access' },
|
|
334
|
+
process.env.JWT_ACCESS_SECRET!,
|
|
335
|
+
{
|
|
336
|
+
expiresIn: ACCESS_TOKEN_TTL,
|
|
337
|
+
algorithm: 'RS256', // Asymmetric — use private key to sign
|
|
338
|
+
issuer: 'https://app.example.com',
|
|
339
|
+
audience: 'https://api.example.com',
|
|
340
|
+
}
|
|
341
|
+
);
|
|
342
|
+
|
|
343
|
+
const refreshTokenId = crypto.randomBytes(32).toString('hex');
|
|
344
|
+
const refreshToken = jwt.sign(
|
|
345
|
+
{ sub: userId, jti: refreshTokenId, type: 'refresh' },
|
|
346
|
+
process.env.JWT_REFRESH_SECRET!,
|
|
347
|
+
{ expiresIn: REFRESH_TOKEN_TTL, algorithm: 'RS256' }
|
|
348
|
+
);
|
|
349
|
+
|
|
350
|
+
// Store refresh token metadata server-side for rotation tracking
|
|
351
|
+
await db.refreshTokens.create({
|
|
352
|
+
id: refreshTokenId,
|
|
353
|
+
userId,
|
|
354
|
+
family: refreshTokenId, // Token family for rotation detection
|
|
355
|
+
expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
return { accessToken, refreshToken };
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
async function rotateRefreshToken(oldRefreshToken: string): Promise<{
|
|
362
|
+
accessToken: string;
|
|
363
|
+
refreshToken: string;
|
|
364
|
+
} | null> {
|
|
365
|
+
let payload: jwt.JwtPayload;
|
|
366
|
+
try {
|
|
367
|
+
payload = jwt.verify(oldRefreshToken, process.env.JWT_REFRESH_SECRET!, {
|
|
368
|
+
algorithms: ['RS256'], // Pin algorithm — reject HS256
|
|
369
|
+
issuer: 'https://app.example.com',
|
|
370
|
+
}) as jwt.JwtPayload;
|
|
371
|
+
} catch {
|
|
372
|
+
return null; // Invalid or expired token
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const storedToken = await db.refreshTokens.findOne({
|
|
376
|
+
where: { id: payload.jti },
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
if (!storedToken) {
|
|
380
|
+
// Token reuse detected — possible theft. Revoke entire family.
|
|
381
|
+
await db.refreshTokens.destroy({
|
|
382
|
+
where: { family: payload.jti },
|
|
383
|
+
});
|
|
384
|
+
// Alert security team
|
|
385
|
+
await alertSecurityTeam('Refresh token reuse detected', {
|
|
386
|
+
userId: payload.sub,
|
|
387
|
+
tokenId: payload.jti,
|
|
388
|
+
});
|
|
389
|
+
return null;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// Invalidate old refresh token (one-time use)
|
|
393
|
+
await db.refreshTokens.destroy({ where: { id: payload.jti } });
|
|
394
|
+
|
|
395
|
+
// Issue new pair
|
|
396
|
+
return issueTokenPair(payload.sub as string);
|
|
397
|
+
}
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
### 3.8 Session Logout and Revocation
|
|
401
|
+
|
|
402
|
+
```typescript
|
|
403
|
+
// SECURE: Complete session destruction
|
|
404
|
+
app.post('/logout', requireAuth, (req, res) => {
|
|
405
|
+
const sessionId = req.sessionID;
|
|
406
|
+
|
|
407
|
+
req.session.destroy((err) => {
|
|
408
|
+
if (err) {
|
|
409
|
+
return res.status(500).json({ error: 'Logout failed' });
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// Clear the session cookie explicitly
|
|
413
|
+
res.clearCookie('__Host-sid', {
|
|
414
|
+
httpOnly: true,
|
|
415
|
+
secure: true,
|
|
416
|
+
sameSite: 'lax',
|
|
417
|
+
path: '/',
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
// Also remove remember-me cookie if present
|
|
421
|
+
res.clearCookie('remember_me', {
|
|
422
|
+
httpOnly: true,
|
|
423
|
+
secure: true,
|
|
424
|
+
sameSite: 'lax',
|
|
425
|
+
path: '/',
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
res.json({ message: 'Logged out' });
|
|
429
|
+
});
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
// SECURE: Revoke all sessions for a user (e.g., password change, account compromise)
|
|
433
|
+
async function revokeAllSessions(userId: string): Promise<void> {
|
|
434
|
+
const keys = await redisClient.keys(`sess:*`);
|
|
435
|
+
for (const key of keys) {
|
|
436
|
+
const data = await redisClient.get(key);
|
|
437
|
+
if (data) {
|
|
438
|
+
const session = JSON.parse(data);
|
|
439
|
+
if (session.userId === userId) {
|
|
440
|
+
await redisClient.del(key);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
// Also revoke all refresh tokens
|
|
445
|
+
await db.refreshTokens.destroy({ where: { userId } });
|
|
446
|
+
// Also revoke all remember-me tokens
|
|
447
|
+
await db.rememberMeTokens.destroy({ where: { userId } });
|
|
448
|
+
}
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
---
|
|
452
|
+
|
|
453
|
+
## 4. Vulnerability Catalog
|
|
454
|
+
|
|
455
|
+
### V-01: Predictable Session IDs (CWE-330)
|
|
456
|
+
**Risk:** Session prediction / brute-force.
|
|
457
|
+
```typescript
|
|
458
|
+
// VULNERABLE: Sequential or timestamp-based session IDs
|
|
459
|
+
const sessionId = `session_${Date.now()}_${counter++}`;
|
|
460
|
+
|
|
461
|
+
// SECURE: Cryptographically random session ID
|
|
462
|
+
import crypto from 'crypto';
|
|
463
|
+
const sessionId = crypto.randomBytes(32).toString('hex'); // 256-bit entropy
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
### V-02: Missing HttpOnly Flag (CWE-1004)
|
|
467
|
+
**Risk:** XSS-based cookie theft via `document.cookie`.
|
|
468
|
+
```typescript
|
|
469
|
+
// VULNERABLE
|
|
470
|
+
res.cookie('sid', sessionId, { httpOnly: false });
|
|
471
|
+
|
|
472
|
+
// SECURE
|
|
473
|
+
res.cookie('sid', sessionId, { httpOnly: true, secure: true, sameSite: 'lax' });
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
### V-03: Missing Secure Flag (CWE-614)
|
|
477
|
+
**Risk:** Session cookie transmitted over HTTP, vulnerable to network sniffing.
|
|
478
|
+
```typescript
|
|
479
|
+
// VULNERABLE: Cookie sent over HTTP
|
|
480
|
+
app.use(session({ cookie: { secure: false } }));
|
|
481
|
+
|
|
482
|
+
// SECURE: Cookie restricted to HTTPS
|
|
483
|
+
app.use(session({ cookie: { secure: true } }));
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
### V-04: Session Fixation — No Rotation on Login (CWE-384)
|
|
487
|
+
**Risk:** Attacker sets session ID before authentication, hijacks post-login session.
|
|
488
|
+
```typescript
|
|
489
|
+
// VULNERABLE: No session regeneration after login
|
|
490
|
+
app.post('/login', (req, res) => {
|
|
491
|
+
const user = authenticate(req.body);
|
|
492
|
+
req.session.userId = user.id; // Same session ID as before login!
|
|
493
|
+
res.redirect('/dashboard');
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
// SECURE: Regenerate session on login
|
|
497
|
+
app.post('/login', (req, res) => {
|
|
498
|
+
const user = authenticate(req.body);
|
|
499
|
+
req.session.regenerate((err) => { // New session ID issued
|
|
500
|
+
req.session.userId = user.id;
|
|
501
|
+
req.session.save(() => res.redirect('/dashboard'));
|
|
502
|
+
});
|
|
503
|
+
});
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
### V-05: Session ID in URL (CWE-598)
|
|
507
|
+
**Risk:** Session ID leaked via Referer headers, browser history, proxy logs, shared links.
|
|
508
|
+
```
|
|
509
|
+
// VULNERABLE
|
|
510
|
+
https://app.example.com/dashboard?sid=abc123def456
|
|
511
|
+
|
|
512
|
+
// SECURE: Session ID in HttpOnly cookie only — never in URL
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
### V-06: Overly Long Session Expiration (CWE-613)
|
|
516
|
+
**Risk:** Extended attack window for stolen session tokens.
|
|
517
|
+
```typescript
|
|
518
|
+
// VULNERABLE: Session valid for 30 days with no idle timeout
|
|
519
|
+
app.use(session({ cookie: { maxAge: 30 * 24 * 60 * 60 * 1000 } }));
|
|
520
|
+
|
|
521
|
+
// SECURE: 30-minute idle timeout + 8-hour absolute timeout
|
|
522
|
+
app.use(session({
|
|
523
|
+
cookie: { maxAge: 1800000 },
|
|
524
|
+
rolling: true,
|
|
525
|
+
}));
|
|
526
|
+
// Plus server-side absolute timeout check in middleware
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
### V-07: No Absolute Session Timeout (CWE-613)
|
|
530
|
+
**Risk:** Rolling idle timeout can be kept alive indefinitely by automated requests.
|
|
531
|
+
```typescript
|
|
532
|
+
// VULNERABLE: Only idle timeout, no absolute limit
|
|
533
|
+
function checkSession(req, res, next) {
|
|
534
|
+
if (Date.now() - req.session.lastActivity > IDLE_TIMEOUT) {
|
|
535
|
+
return req.session.destroy(() => res.status(401).end());
|
|
536
|
+
}
|
|
537
|
+
req.session.lastActivity = Date.now();
|
|
538
|
+
next();
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// SECURE: Both idle AND absolute timeout
|
|
542
|
+
function checkSession(req, res, next) {
|
|
543
|
+
const now = Date.now();
|
|
544
|
+
if (now - req.session.lastActivity > IDLE_TIMEOUT) {
|
|
545
|
+
return req.session.destroy(() => res.status(401).json({ error: 'Session expired' }));
|
|
546
|
+
}
|
|
547
|
+
if (now - req.session.loginTime > ABSOLUTE_TIMEOUT) {
|
|
548
|
+
return req.session.destroy(() => res.status(401).json({ error: 'Session expired' }));
|
|
549
|
+
}
|
|
550
|
+
req.session.lastActivity = now;
|
|
551
|
+
next();
|
|
552
|
+
}
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
### V-08: Insecure Remember-Me (CWE-640)
|
|
556
|
+
**Risk:** Remember-me token that is just a plaintext user ID or re-uses the session cookie.
|
|
557
|
+
```typescript
|
|
558
|
+
// VULNERABLE: Predictable remember-me token
|
|
559
|
+
res.cookie('remember', Buffer.from(user.id).toString('base64'), {
|
|
560
|
+
maxAge: 30 * 24 * 60 * 60 * 1000,
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
// SECURE: Cryptographic selector:validator pattern with hashed storage
|
|
564
|
+
// (See section 3.5 for full implementation)
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
### V-09: JWT Without Revocation Mechanism (CWE-613)
|
|
568
|
+
**Risk:** Stolen JWT valid until expiry; no way to invalidate compromised tokens.
|
|
569
|
+
```typescript
|
|
570
|
+
// VULNERABLE: Long-lived JWT with no revocation
|
|
571
|
+
const token = jwt.sign({ userId }, secret, { expiresIn: '30d' });
|
|
572
|
+
|
|
573
|
+
// SECURE: Short-lived access token + refresh rotation + server-side blocklist
|
|
574
|
+
const accessToken = jwt.sign({ sub: userId }, secret, { expiresIn: '15m' });
|
|
575
|
+
// Plus refresh token rotation (see section 3.7)
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
### V-10: Algorithm Confusion in JWT (CWE-327)
|
|
579
|
+
**Risk:** Attacker changes `alg` header from RS256 to HS256, signs with public key.
|
|
580
|
+
```typescript
|
|
581
|
+
// VULNERABLE: Trusting the alg header from the token
|
|
582
|
+
const payload = jwt.verify(token, publicKey);
|
|
583
|
+
|
|
584
|
+
// SECURE: Pin algorithm server-side
|
|
585
|
+
const payload = jwt.verify(token, publicKey, { algorithms: ['RS256'] });
|
|
586
|
+
```
|
|
587
|
+
|
|
588
|
+
### V-11: Session Data in localStorage (CWE-922)
|
|
589
|
+
**Risk:** Any XSS vulnerability exposes all data in localStorage.
|
|
590
|
+
```typescript
|
|
591
|
+
// VULNERABLE: Storing tokens in localStorage
|
|
592
|
+
localStorage.setItem('authToken', token);
|
|
593
|
+
|
|
594
|
+
// SECURE: Store in HttpOnly cookie (inaccessible to JavaScript)
|
|
595
|
+
// Or keep access token in memory only (cleared on tab close)
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
### V-12: Missing SameSite Attribute (CWE-1275)
|
|
599
|
+
**Risk:** Cookie sent with cross-site requests, enabling CSRF attacks.
|
|
600
|
+
```typescript
|
|
601
|
+
// VULNERABLE (legacy browsers without SameSite default)
|
|
602
|
+
res.cookie('sid', sessionId, { httpOnly: true, secure: true });
|
|
603
|
+
|
|
604
|
+
// SECURE
|
|
605
|
+
res.cookie('sid', sessionId, { httpOnly: true, secure: true, sameSite: 'lax' });
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
### V-13: Insufficient Session Destruction on Logout (CWE-613)
|
|
609
|
+
**Risk:** Server-side session persists after user clicks logout.
|
|
610
|
+
```typescript
|
|
611
|
+
// VULNERABLE: Clear cookie but leave server-side session intact
|
|
612
|
+
app.post('/logout', (req, res) => {
|
|
613
|
+
res.clearCookie('sid');
|
|
614
|
+
res.redirect('/login');
|
|
615
|
+
});
|
|
616
|
+
|
|
617
|
+
// SECURE: Destroy server-side session AND clear cookie
|
|
618
|
+
app.post('/logout', (req, res) => {
|
|
619
|
+
req.session.destroy(() => {
|
|
620
|
+
res.clearCookie('__Host-sid', {
|
|
621
|
+
httpOnly: true, secure: true, sameSite: 'lax', path: '/',
|
|
622
|
+
});
|
|
623
|
+
res.redirect('/login');
|
|
624
|
+
});
|
|
625
|
+
});
|
|
626
|
+
```
|
|
627
|
+
|
|
628
|
+
### V-14: No Concurrent Session Control (CWE-307)
|
|
629
|
+
**Risk:** Unlimited sessions allow undetected parallel use of stolen credentials.
|
|
630
|
+
```
|
|
631
|
+
// VULNERABLE: No limit on active sessions per user
|
|
632
|
+
|
|
633
|
+
// SECURE: Track and limit active sessions (see section 3.4)
|
|
634
|
+
// Alert user when new session is created from unexpected location
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
### V-15: Session Cookie Without __Host- Prefix (CWE-784)
|
|
638
|
+
**Risk:** Cookie can be overwritten by subdomain or non-secure context.
|
|
639
|
+
```typescript
|
|
640
|
+
// VULNERABLE: Regular cookie name
|
|
641
|
+
res.cookie('sid', value, { secure: true, path: '/' });
|
|
642
|
+
|
|
643
|
+
// SECURE: __Host- prefix enforces Secure + Path=/ + no Domain attribute
|
|
644
|
+
res.cookie('__Host-sid', value, { secure: true, path: '/', httpOnly: true });
|
|
645
|
+
```
|
|
646
|
+
|
|
647
|
+
---
|
|
648
|
+
|
|
649
|
+
## 5. Security Checklist
|
|
650
|
+
|
|
651
|
+
### Cookie Configuration
|
|
652
|
+
- [ ] `HttpOnly` flag set on all session cookies
|
|
653
|
+
- [ ] `Secure` flag set on all session cookies
|
|
654
|
+
- [ ] `SameSite` attribute set to `Strict` or `Lax`
|
|
655
|
+
- [ ] `__Host-` prefix used for session cookie name
|
|
656
|
+
- [ ] `Domain` attribute omitted (restricts to exact origin)
|
|
657
|
+
- [ ] `Path` set to narrowest required scope
|
|
658
|
+
|
|
659
|
+
### Session ID Security
|
|
660
|
+
- [ ] Session IDs generated with CSPRNG (minimum 128-bit entropy)
|
|
661
|
+
- [ ] Session IDs never appear in URLs, logs, or error messages
|
|
662
|
+
- [ ] Session IDs are not derived from user data or timestamps
|
|
663
|
+
- [ ] Session cookie value is signed (HMAC) to detect tampering
|
|
664
|
+
|
|
665
|
+
### Session Lifecycle
|
|
666
|
+
- [ ] Session ID regenerated on login (prevents fixation)
|
|
667
|
+
- [ ] Session ID regenerated on privilege escalation
|
|
668
|
+
- [ ] Session ID regenerated on password change
|
|
669
|
+
- [ ] Idle timeout enforced server-side (15-30 minutes)
|
|
670
|
+
- [ ] Absolute timeout enforced server-side (4-8 hours)
|
|
671
|
+
- [ ] Session fully destroyed on logout (server-side + cookie cleared)
|
|
672
|
+
- [ ] All sessions revoked on password change / account compromise
|
|
673
|
+
|
|
674
|
+
### Session Storage
|
|
675
|
+
- [ ] Session data stored server-side (Redis, database) — not in cookies
|
|
676
|
+
- [ ] Redis connection encrypted with TLS in production
|
|
677
|
+
- [ ] Session store uses key prefix and dedicated database number
|
|
678
|
+
- [ ] Session secrets rotated periodically (support multiple secrets)
|
|
679
|
+
|
|
680
|
+
### Concurrent Sessions & Monitoring
|
|
681
|
+
- [ ] Maximum concurrent sessions enforced per user
|
|
682
|
+
- [ ] Users notified of new session creation from new device/location
|
|
683
|
+
- [ ] Concurrent sessions from different IPs flagged for review
|
|
684
|
+
- [ ] Session creation/destruction events logged for audit
|
|
685
|
+
|
|
686
|
+
### JWT-Specific (if applicable)
|
|
687
|
+
- [ ] Access token lifetime 15 minutes or less
|
|
688
|
+
- [ ] Refresh token rotation with one-time use
|
|
689
|
+
- [ ] Token family tracking for reuse detection
|
|
690
|
+
- [ ] Algorithm pinned server-side (`algorithms: ['RS256']`)
|
|
691
|
+
- [ ] All claims validated (`iss`, `aud`, `exp`, `nbf`, `sub`)
|
|
692
|
+
- [ ] JWTs stored in HttpOnly cookies, not localStorage
|
|
693
|
+
- [ ] Server-side blocklist for immediate revocation
|
|
694
|
+
|
|
695
|
+
---
|
|
696
|
+
|
|
697
|
+
## 6. Tools & Automation
|
|
698
|
+
|
|
699
|
+
### 6.1 Session Token Analysis
|
|
700
|
+
|
|
701
|
+
| Tool | Purpose | Usage |
|
|
702
|
+
|------|---------|-------|
|
|
703
|
+
| **Burp Suite Sequencer** | Analyze randomness/entropy of session tokens | Capture tokens via Proxy, send to Sequencer, run statistical analysis (FIPS tests, character-level analysis) |
|
|
704
|
+
| **Burp Suite Intruder** | Test session fixation and rotation | Replay pre-auth session IDs post-login to verify regeneration |
|
|
705
|
+
| **Burp Suite Repeater** | Manipulate and replay session cookies | Modify cookie attributes, test logout effectiveness |
|
|
706
|
+
|
|
707
|
+
### 6.2 Cookie Security Scanners
|
|
708
|
+
|
|
709
|
+
| Tool | Purpose | Command / URL |
|
|
710
|
+
|------|---------|---------------|
|
|
711
|
+
| **Mozilla Observatory** | Scan cookie and header security | `observatory.mozilla.org` or CLI: `observatory <domain>` |
|
|
712
|
+
| **SecurityHeaders.com** | Check response headers including Set-Cookie | `securityheaders.com/?q=<domain>` |
|
|
713
|
+
| **CookieServe** | Analyze cookie attributes for a domain | `cookieserve.com` |
|
|
714
|
+
| **curl** | Manual cookie attribute inspection | `curl -I -v https://example.com/login 2>&1 \| grep -i set-cookie` |
|
|
715
|
+
|
|
716
|
+
### 6.3 Automated Security Testing
|
|
717
|
+
|
|
718
|
+
| Tool | Purpose | Notes |
|
|
719
|
+
|------|---------|-------|
|
|
720
|
+
| **OWASP ZAP** | Automated session management testing | Session management scanner, cookie checks, active scan rules |
|
|
721
|
+
| **Nikto** | Web server scanner | Checks for session-related misconfigurations |
|
|
722
|
+
| **testssl.sh** | TLS configuration testing | Verifies HTTPS enforcement for Secure cookies |
|
|
723
|
+
| **eslint-plugin-security** | Static analysis for Node.js | Flags insecure cookie configurations in code |
|
|
724
|
+
| **semgrep** | Custom rules for session patterns | Write rules to detect missing HttpOnly, session fixation patterns |
|
|
725
|
+
|
|
726
|
+
### 6.4 Runtime Monitoring
|
|
727
|
+
|
|
728
|
+
| Tool | Purpose |
|
|
729
|
+
|------|---------|
|
|
730
|
+
| **Splunk** | Detect concurrent sessions from different IPs (built-in detection rules for AWS, Azure AD, O365) |
|
|
731
|
+
| **Elastic SIEM** | Session anomaly detection with ML-based behavioral analysis |
|
|
732
|
+
| **Datadog ASM** | Application-level session attack detection |
|
|
733
|
+
| **AWS GuardDuty** | Detect compromised credentials via session anomalies |
|
|
734
|
+
|
|
735
|
+
---
|
|
736
|
+
|
|
737
|
+
## 7. Platform-Specific Guidance
|
|
738
|
+
|
|
739
|
+
### 7.1 Web Applications — Storage Comparison
|
|
740
|
+
|
|
741
|
+
| Storage | XSS Accessible | CSRF Risk | Capacity | Persistence | Recommendation |
|
|
742
|
+
|---------|---------------|-----------|----------|-------------|----------------|
|
|
743
|
+
| HttpOnly Cookie | No | Yes (mitigated by SameSite) | ~4 KB | Configurable | **Recommended for session IDs** |
|
|
744
|
+
| localStorage | Yes | No | ~5-10 MB | Until cleared | **Never for session tokens** |
|
|
745
|
+
| sessionStorage | Yes | No | ~5-10 MB | Tab lifetime | Acceptable for non-sensitive temporary data only |
|
|
746
|
+
| In-memory (JS variable) | Yes (if XSS) | No | Unlimited | Page lifetime | Acceptable for short-lived access tokens in SPAs |
|
|
747
|
+
|
|
748
|
+
**Rule:** Session identifiers and authentication tokens belong in HttpOnly cookies. Period.
|
|
749
|
+
|
|
750
|
+
### 7.2 Single-Page Applications (SPAs)
|
|
751
|
+
|
|
752
|
+
SPAs present unique session management challenges:
|
|
753
|
+
|
|
754
|
+
**Recommended architecture — Backend-For-Frontend (BFF):**
|
|
755
|
+
1. The BFF acts as a confidential OAuth client, keeping tokens server-side.
|
|
756
|
+
2. The SPA communicates with the BFF using HttpOnly session cookies.
|
|
757
|
+
3. The BFF proxies API requests, attaching access tokens from its server-side store.
|
|
758
|
+
4. Token refresh happens server-side, invisible to the browser.
|
|
759
|
+
|
|
760
|
+
```
|
|
761
|
+
[Browser/SPA] <--HttpOnly cookie--> [BFF Server] <--Access Token--> [API Server]
|
|
762
|
+
```
|
|
763
|
+
|
|
764
|
+
**If BFF is not feasible (public client SPA):**
|
|
765
|
+
- Use Authorization Code Flow with PKCE.
|
|
766
|
+
- Store access tokens in memory only (JavaScript closure or module scope).
|
|
767
|
+
- Use refresh token rotation with one-time-use tokens.
|
|
768
|
+
- Set refresh token as HttpOnly cookie with strict SameSite.
|
|
769
|
+
- Implement silent refresh before access token expiry.
|
|
770
|
+
- Note: Third-party cookie blocking in Safari and upcoming Chrome changes affect silent refresh via iframes. Plan for BFF migration.
|
|
771
|
+
|
|
772
|
+
**Content Security Policy for SPAs:**
|
|
773
|
+
```
|
|
774
|
+
Content-Security-Policy: default-src 'self'; script-src 'self'; connect-src 'self' https://api.example.com; frame-ancestors 'none';
|
|
775
|
+
```
|
|
776
|
+
|
|
777
|
+
### 7.3 Mobile Applications
|
|
778
|
+
|
|
779
|
+
**iOS — Keychain Services:**
|
|
780
|
+
```swift
|
|
781
|
+
// SECURE: Store session token in iOS Keychain
|
|
782
|
+
import Security
|
|
783
|
+
|
|
784
|
+
func storeToken(_ token: String, forKey key: String) -> Bool {
|
|
785
|
+
let data = token.data(using: .utf8)!
|
|
786
|
+
let query: [String: Any] = [
|
|
787
|
+
kSecClass as String: kSecClassGenericPassword,
|
|
788
|
+
kSecAttrAccount as String: key,
|
|
789
|
+
kSecValueData as String: data,
|
|
790
|
+
kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
|
|
791
|
+
]
|
|
792
|
+
SecItemDelete(query as CFDictionary) // Remove existing
|
|
793
|
+
let status = SecItemAdd(query as CFDictionary, nil)
|
|
794
|
+
return status == errSecSuccess
|
|
795
|
+
}
|
|
796
|
+
```
|
|
797
|
+
|
|
798
|
+
**Android — EncryptedSharedPreferences with Keystore:**
|
|
799
|
+
```kotlin
|
|
800
|
+
// SECURE: Store session token with Android Keystore-backed encryption
|
|
801
|
+
import androidx.security.crypto.EncryptedSharedPreferences
|
|
802
|
+
import androidx.security.crypto.MasterKey
|
|
803
|
+
|
|
804
|
+
val masterKey = MasterKey.Builder(context)
|
|
805
|
+
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
|
|
806
|
+
.build()
|
|
807
|
+
|
|
808
|
+
val prefs = EncryptedSharedPreferences.create(
|
|
809
|
+
context,
|
|
810
|
+
"secure_session_prefs",
|
|
811
|
+
masterKey,
|
|
812
|
+
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
|
813
|
+
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
|
814
|
+
)
|
|
815
|
+
|
|
816
|
+
prefs.edit().putString("session_token", token).apply()
|
|
817
|
+
```
|
|
818
|
+
|
|
819
|
+
**Mobile session management rules:**
|
|
820
|
+
- Never store tokens in plaintext SharedPreferences or UserDefaults.
|
|
821
|
+
- Use hardware-backed encryption (Secure Enclave on iOS, Hardware Security Module on Android).
|
|
822
|
+
- Set `kSecAttrAccessibleWhenUnlockedThisDeviceOnly` on iOS to prevent iCloud Keychain sync and lock-screen access.
|
|
823
|
+
- Implement certificate pinning to prevent AiTM token interception.
|
|
824
|
+
- Support remote session revocation via push notification or next-request check.
|
|
825
|
+
- Enforce biometric re-authentication for sensitive operations.
|
|
826
|
+
|
|
827
|
+
---
|
|
828
|
+
|
|
829
|
+
## 8. Incident Patterns
|
|
830
|
+
|
|
831
|
+
### 8.1 Session Hijacking Detection
|
|
832
|
+
|
|
833
|
+
**Indicators of Compromise:**
|
|
834
|
+
- Same session ID used from multiple IP addresses within a short time window (< 5 minutes).
|
|
835
|
+
- Geographically impossible travel: session used from two locations farther apart than possible given the time interval.
|
|
836
|
+
- User-Agent string change within the same session.
|
|
837
|
+
- Sudden spike in API requests from a session that was previously idle.
|
|
838
|
+
- Session used outside the user's established behavioral baseline (unusual hours, unusual endpoints).
|
|
839
|
+
|
|
840
|
+
**Detection Queries (Splunk example):**
|
|
841
|
+
```spl
|
|
842
|
+
index=web_logs sourcetype=access_combined
|
|
843
|
+
| stats dc(client_ip) as unique_ips, values(client_ip) as ip_list by session_id
|
|
844
|
+
| where unique_ips > 1
|
|
845
|
+
| lookup geoip ip_list OUTPUT lat, lon, country
|
|
846
|
+
| eval distance=haversine(lat_1, lon_1, lat_2, lon_2)
|
|
847
|
+
| where distance > 500
|
|
848
|
+
| table session_id, ip_list, unique_ips, distance
|
|
849
|
+
```
|
|
850
|
+
|
|
851
|
+
### 8.2 Concurrent Session Anomalies
|
|
852
|
+
|
|
853
|
+
**Detection logic:**
|
|
854
|
+
1. Track `(user_id, session_id, ip_address, user_agent, geo)` for every authenticated request.
|
|
855
|
+
2. Alert when a user has active sessions from more than N distinct IP addresses simultaneously.
|
|
856
|
+
3. Alert when a new session is created from a geo-location inconsistent with the user's history.
|
|
857
|
+
4. Correlate with known infostealer malware indicators (e.g., sessions originating from residential proxy networks).
|
|
858
|
+
|
|
859
|
+
### 8.3 Incident Response Steps
|
|
860
|
+
|
|
861
|
+
**Immediate (0-15 minutes):**
|
|
862
|
+
1. Revoke all active sessions for the affected user (`revokeAllSessions(userId)`).
|
|
863
|
+
2. Invalidate all refresh tokens and remember-me tokens.
|
|
864
|
+
3. Force password reset and MFA re-enrollment.
|
|
865
|
+
4. Block the attacker's IP/session from the WAF.
|
|
866
|
+
|
|
867
|
+
**Short-term (15-60 minutes):**
|
|
868
|
+
5. Analyze access logs to determine scope: what data was accessed, what actions were taken.
|
|
869
|
+
6. Check if the stolen session was used to modify account settings (email, MFA, password).
|
|
870
|
+
7. Revert any unauthorized changes to the account.
|
|
871
|
+
8. Notify the affected user via a verified out-of-band channel.
|
|
872
|
+
|
|
873
|
+
**Investigation (1-24 hours):**
|
|
874
|
+
9. Determine the theft vector: XSS, malware, phishing proxy, network interception, or insider.
|
|
875
|
+
10. If XSS: identify and patch the injection point, scan for similar vulnerabilities.
|
|
876
|
+
11. If infostealer: coordinate with endpoint security team for device remediation.
|
|
877
|
+
12. If phishing proxy: report phishing domain, update email filters.
|
|
878
|
+
13. Review session management configuration for gaps identified during the incident.
|
|
879
|
+
|
|
880
|
+
**Post-incident:**
|
|
881
|
+
14. Implement additional controls based on root cause (e.g., add device binding, IP pinning, anomaly detection).
|
|
882
|
+
15. Update detection rules to catch similar attacks earlier.
|
|
883
|
+
16. Conduct lessons-learned review and update this playbook.
|
|
884
|
+
|
|
885
|
+
---
|
|
886
|
+
|
|
887
|
+
## 9. Compliance & Standards
|
|
888
|
+
|
|
889
|
+
### 9.1 OWASP Top 10 — A07:2021 Identification and Authentication Failures
|
|
890
|
+
|
|
891
|
+
Session management falls under A07:2021. Key requirements:
|
|
892
|
+
- Session IDs must not be exposed in URLs.
|
|
893
|
+
- Session IDs must be rotated after successful login.
|
|
894
|
+
- Sessions must be properly invalidated during logout and idle timeout.
|
|
895
|
+
- Session IDs must have sufficient entropy to resist prediction attacks.
|
|
896
|
+
|
|
897
|
+
### 9.2 NIST SP 800-63B — Digital Identity Guidelines
|
|
898
|
+
|
|
899
|
+
- **Session binding:** Bind session to the authenticated subscriber. Session secrets must be at least 64 bits of entropy (OWASP recommends 128-bit minimum).
|
|
900
|
+
- **Reauthentication:** Require reauthentication after 15 minutes of inactivity for AAL2 (multi-factor) and AAL3 (hardware token) sessions.
|
|
901
|
+
- **Session limits:** Set absolute session lifetime limits. Reauthentication required periodically even for active sessions.
|
|
902
|
+
- **Storage:** Session secrets stored in secure, httpOnly cookies. Must be sent only over authenticated, protected (TLS) channels.
|
|
903
|
+
|
|
904
|
+
### 9.3 PCI-DSS 4.0
|
|
905
|
+
|
|
906
|
+
- **Requirement 8.2.8 (mandatory since March 31, 2025):** If a user session has been idle for more than 15 minutes, the user must re-authenticate. This applies to all access to the Cardholder Data Environment (CDE).
|
|
907
|
+
- **Requirement 8.3:** Multi-factor authentication required for all personnel with access to the CDE. Each session (VPN, CDE application) requires its own MFA challenge.
|
|
908
|
+
- **Requirement 8.6:** System and application accounts used for automated access must have session controls including regular credential rotation and monitoring.
|
|
909
|
+
- **Replay protection:** Methods to defend against replay attacks include unique session identifiers, timestamps, and time-based one-time passwords.
|
|
910
|
+
|
|
911
|
+
### 9.4 CWE References Summary
|
|
912
|
+
|
|
913
|
+
| CWE | Name | Section |
|
|
914
|
+
|-----|------|---------|
|
|
915
|
+
| CWE-330 | Use of Insufficiently Random Values | V-01 |
|
|
916
|
+
| CWE-1004 | Sensitive Cookie Without HttpOnly Flag | V-02 |
|
|
917
|
+
| CWE-614 | Sensitive Cookie in HTTPS Session Without Secure Attribute | V-03 |
|
|
918
|
+
| CWE-384 | Session Fixation | V-04 |
|
|
919
|
+
| CWE-598 | Use of GET Request Method With Sensitive Query Strings | V-05 |
|
|
920
|
+
| CWE-613 | Insufficient Session Expiration | V-06, V-07, V-09, V-13 |
|
|
921
|
+
| CWE-640 | Weak Password Recovery Mechanism for Forgotten Password | V-08 |
|
|
922
|
+
| CWE-327 | Use of a Broken or Risky Cryptographic Algorithm | V-10 |
|
|
923
|
+
| CWE-922 | Insecure Storage of Sensitive Information | V-11 |
|
|
924
|
+
| CWE-1275 | Sensitive Cookie with Improper SameSite Attribute | V-12 |
|
|
925
|
+
| CWE-784 | Reliance on Cookies without Validation and Integrity | V-15 |
|
|
926
|
+
|
|
927
|
+
---
|
|
928
|
+
|
|
929
|
+
## 10. Code Examples — Vulnerable vs. Secure Pairs
|
|
930
|
+
|
|
931
|
+
### 10.1 Complete Express Session Setup
|
|
932
|
+
|
|
933
|
+
```typescript
|
|
934
|
+
// === VULNERABLE SESSION CONFIGURATION ===
|
|
935
|
+
import express from 'express';
|
|
936
|
+
import session from 'express-session';
|
|
937
|
+
|
|
938
|
+
const app = express();
|
|
939
|
+
app.use(session({
|
|
940
|
+
secret: 'keyboard cat', // Weak, hardcoded secret
|
|
941
|
+
resave: true, // Unnecessary writes
|
|
942
|
+
saveUninitialized: true, // Creates sessions for anonymous users
|
|
943
|
+
cookie: {}, // No security attributes set
|
|
944
|
+
}));
|
|
945
|
+
|
|
946
|
+
// === SECURE SESSION CONFIGURATION ===
|
|
947
|
+
import express from 'express';
|
|
948
|
+
import session from 'express-session';
|
|
949
|
+
import { createClient } from 'redis';
|
|
950
|
+
import RedisStore from 'connect-redis';
|
|
951
|
+
import crypto from 'crypto';
|
|
952
|
+
|
|
953
|
+
const app = express();
|
|
954
|
+
|
|
955
|
+
const redisClient = createClient({
|
|
956
|
+
url: process.env.REDIS_URL,
|
|
957
|
+
socket: { tls: process.env.NODE_ENV === 'production' },
|
|
958
|
+
});
|
|
959
|
+
await redisClient.connect();
|
|
960
|
+
|
|
961
|
+
app.set('trust proxy', 1); // Trust first proxy (for Secure cookie behind LB)
|
|
962
|
+
|
|
963
|
+
app.use(session({
|
|
964
|
+
store: new RedisStore({ client: redisClient, prefix: 'sess:', ttl: 1800 }),
|
|
965
|
+
name: '__Host-sid',
|
|
966
|
+
secret: [
|
|
967
|
+
process.env.SESSION_SECRET_CURRENT!, // Current secret
|
|
968
|
+
process.env.SESSION_SECRET_PREVIOUS!, // Previous secret (for rotation)
|
|
969
|
+
],
|
|
970
|
+
resave: false,
|
|
971
|
+
saveUninitialized: false,
|
|
972
|
+
rolling: true,
|
|
973
|
+
cookie: {
|
|
974
|
+
httpOnly: true,
|
|
975
|
+
secure: true,
|
|
976
|
+
sameSite: 'lax',
|
|
977
|
+
maxAge: 1800000,
|
|
978
|
+
path: '/',
|
|
979
|
+
},
|
|
980
|
+
}));
|
|
981
|
+
```
|
|
982
|
+
|
|
983
|
+
### 10.2 Session Timeout Middleware
|
|
984
|
+
|
|
985
|
+
```typescript
|
|
986
|
+
// === VULNERABLE: No timeout enforcement ===
|
|
987
|
+
// (relying solely on cookie maxAge — client can modify)
|
|
988
|
+
|
|
989
|
+
// === SECURE: Server-side dual timeout enforcement ===
|
|
990
|
+
const IDLE_TIMEOUT_MS = 15 * 60 * 1000; // 15 minutes
|
|
991
|
+
const ABSOLUTE_TIMEOUT_MS = 8 * 60 * 60 * 1000; // 8 hours
|
|
992
|
+
|
|
993
|
+
function sessionTimeoutMiddleware(
|
|
994
|
+
req: express.Request,
|
|
995
|
+
res: express.Response,
|
|
996
|
+
next: express.NextFunction
|
|
997
|
+
): void {
|
|
998
|
+
if (!req.session?.userId) {
|
|
999
|
+
return next(); // No authenticated session
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
const now = Date.now();
|
|
1003
|
+
|
|
1004
|
+
// Check absolute timeout
|
|
1005
|
+
if (req.session.loginTime && now - req.session.loginTime > ABSOLUTE_TIMEOUT_MS) {
|
|
1006
|
+
return req.session.destroy(() => {
|
|
1007
|
+
res.clearCookie('__Host-sid', {
|
|
1008
|
+
httpOnly: true, secure: true, sameSite: 'lax', path: '/',
|
|
1009
|
+
});
|
|
1010
|
+
res.status(401).json({ error: 'Session expired', reason: 'absolute_timeout' });
|
|
1011
|
+
});
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
// Check idle timeout
|
|
1015
|
+
if (req.session.lastActivity && now - req.session.lastActivity > IDLE_TIMEOUT_MS) {
|
|
1016
|
+
return req.session.destroy(() => {
|
|
1017
|
+
res.clearCookie('__Host-sid', {
|
|
1018
|
+
httpOnly: true, secure: true, sameSite: 'lax', path: '/',
|
|
1019
|
+
});
|
|
1020
|
+
res.status(401).json({ error: 'Session expired', reason: 'idle_timeout' });
|
|
1021
|
+
});
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
// Update last activity timestamp
|
|
1025
|
+
req.session.lastActivity = now;
|
|
1026
|
+
next();
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
app.use(sessionTimeoutMiddleware);
|
|
1030
|
+
```
|
|
1031
|
+
|
|
1032
|
+
### 10.3 Device/IP Binding for Session Integrity
|
|
1033
|
+
|
|
1034
|
+
```typescript
|
|
1035
|
+
// SECURE: Bind session to device fingerprint for anomaly detection
|
|
1036
|
+
interface SessionMetadata {
|
|
1037
|
+
userId: string;
|
|
1038
|
+
ip: string;
|
|
1039
|
+
userAgent: string;
|
|
1040
|
+
loginTime: number;
|
|
1041
|
+
lastActivity: number;
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
function sessionIntegrityCheck(
|
|
1045
|
+
req: express.Request,
|
|
1046
|
+
res: express.Response,
|
|
1047
|
+
next: express.NextFunction
|
|
1048
|
+
): void {
|
|
1049
|
+
if (!req.session?.userId) return next();
|
|
1050
|
+
|
|
1051
|
+
const currentIp = req.ip;
|
|
1052
|
+
const currentUA = req.get('user-agent') || '';
|
|
1053
|
+
|
|
1054
|
+
// Strict: block if IP changes (may cause issues with mobile/VPN users)
|
|
1055
|
+
// Recommended: alert but allow, require re-auth for sensitive operations
|
|
1056
|
+
if (req.session.ip && req.session.ip !== currentIp) {
|
|
1057
|
+
logger.warn('Session IP change detected', {
|
|
1058
|
+
sessionId: req.sessionID,
|
|
1059
|
+
userId: req.session.userId,
|
|
1060
|
+
originalIp: req.session.ip,
|
|
1061
|
+
currentIp,
|
|
1062
|
+
});
|
|
1063
|
+
// For high-security: force re-authentication
|
|
1064
|
+
// For standard: log and continue
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
if (req.session.userAgent && req.session.userAgent !== currentUA) {
|
|
1068
|
+
logger.warn('Session User-Agent change detected', {
|
|
1069
|
+
sessionId: req.sessionID,
|
|
1070
|
+
userId: req.session.userId,
|
|
1071
|
+
});
|
|
1072
|
+
// User-Agent change is a stronger signal of hijacking
|
|
1073
|
+
return req.session.destroy(() => {
|
|
1074
|
+
res.status(401).json({ error: 'Session invalidated', reason: 'integrity_check' });
|
|
1075
|
+
});
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
next();
|
|
1079
|
+
}
|
|
1080
|
+
```
|
|
1081
|
+
|
|
1082
|
+
---
|
|
1083
|
+
|
|
1084
|
+
## References
|
|
1085
|
+
|
|
1086
|
+
- [OWASP Session Management Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html)
|
|
1087
|
+
- [OWASP Top 10 A07:2021 — Identification and Authentication Failures](https://owasp.org/Top10/A07_2021-Identification_and_Authentication_Failures/)
|
|
1088
|
+
- [OWASP Testing Guide — Session Management Testing](https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/06-Session_Management_Testing/)
|
|
1089
|
+
- [NIST SP 800-63B — Digital Identity Guidelines](https://pages.nist.gov/800-63-3/sp800-63b.html)
|
|
1090
|
+
- [PCI-DSS 4.0 Requirement 8.2.8 — Session Timeout](https://pcidssguide.com/pci-dss-session-timeout-requirements/)
|
|
1091
|
+
- [CWE-384: Session Fixation](https://cwe.mitre.org/data/definitions/384.html)
|
|
1092
|
+
- [PortSwigger — JWT Attacks](https://portswigger.net/web-security/jwt)
|
|
1093
|
+
- [PortSwigger — Burp Sequencer Documentation](https://portswigger.net/burp/documentation/desktop/tools/sequencer/getting-started)
|
|
1094
|
+
- [Auth0 — Refresh Token Rotation](https://auth0.com/blog/securing-single-page-applications-with-refresh-token-rotation/)
|
|
1095
|
+
- [Curity — SPA Best Practices](https://curity.io/resources/learn/spa-best-practices/)
|
|
1096
|
+
- [Redis — Session Management](https://redis.io/solutions/session-management/)
|
|
1097
|
+
- [Mozilla — Secure Cookie Configuration](https://developer.mozilla.org/en-US/docs/Web/Security/Practical_implementation_guides/Cookies)
|
|
1098
|
+
- [SpyCloud — 2024 Cybercrime Report](https://spycloud.com/blog/2024-cybercrime-update-and-2025-predictions/)
|
|
1099
|
+
- [Push Security — 2024 Identity Breaches](https://pushsecurity.com/blog/2024-identity-breaches)
|
|
1100
|
+
- [The Hacker News — Session Hijacking 2.0](https://thehackernews.com/2024/09/session-hijacking-20-latest-way-that.html)
|
|
1101
|
+
- [Red Sentry — JWT Vulnerabilities 2026](https://redsentry.com/resources/blog/jwt-vulnerabilities-list-2026-security-risks-mitigation-guide)
|