# Nextcloud Core Security

> >

- **Type:** Skill
- **Install:** `agentstack add skill-impertio-studio-nextcloud-claude-skill-package-nextcloud-core-security`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [Impertio-Studio](https://agentstack.voostack.com/s/impertio-studio)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [Impertio-Studio](https://github.com/Impertio-Studio)
- **Source:** https://github.com/Impertio-Studio/Nextcloud-Claude-Skill-Package/tree/main/skills/source/nextcloud-core/nextcloud-core-security

## Install

```sh
agentstack add skill-impertio-studio-nextcloud-claude-skill-package-nextcloud-core-security
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# nextcloud-core-security

## Quick Reference

### Controller Security Defaults

Every controller method enforces ALL of the following unless explicitly overridden with attributes:

| Default | Effect | Override Attribute |
|---------|--------|--------------------|
| Admin-only | Non-admin users receive HTTP 403 | `#[NoAdminRequired]` |
| Authenticated | Anonymous users redirected to login | `#[PublicPage]` |
| 2FA required | Users without completed 2FA are blocked | `#[NoTwoFactorRequired]` |
| CSRF validated | Request must include CSRF token or `OCS-APIRequest: true` header | `#[NoCSRFRequired]` |

**ALWAYS** start from the default secure posture and relax only what is needed. The default is the most restrictive configuration possible.

### Security Attributes (NC 27+)

| Attribute | Effect | Use Case |
|-----------|--------|----------|
| `#[NoAdminRequired]` | Allow non-admin authenticated users | Regular user-facing endpoints |
| `#[PublicPage]` | No login required | Public APIs, share pages |
| `#[NoCSRFRequired]` | Skip CSRF token validation | API endpoints using bearer/basic auth |
| `#[NoTwoFactorRequired]` | Bypass 2FA requirement | 2FA setup pages themselves |
| `#[UserRateLimit(limit: N, period: S)]` | Rate limit for logged-in users | Sensitive operations |
| `#[AnonRateLimit(limit: N, period: S)]` | Rate limit for anonymous users | Public endpoints |
| `#[BruteForceProtection(action: 'name')]` | Throttle repeated failures | Login, token validation |

**Legacy annotations** (pre-NC 27): `@NoAdminRequired`, `@NoCSRFRequired`, `@PublicPage`. ALWAYS use PHP 8 attributes for NC 28+ apps.

### Critical Warnings

**NEVER** assume a controller method without attributes is public -- the default is admin-only, authenticated, 2FA-required, CSRF-validated. Forgetting `#[NoAdminRequired]` means regular users get HTTP 403.

**NEVER** combine `#[PublicPage]` + `#[NoCSRFRequired]` on state-changing endpoints without additional authentication (bearer token, API key, or `OCS-APIRequest: true` header). This creates a CSRF vulnerability.

**NEVER** use `#[NoCSRFRequired]` on browser-facing form endpoints -- CSRF protection exists to prevent cross-site attacks on session-authenticated users.

**NEVER** call `$response->throttle()` on successful attempts -- only on failures. Throttling success slows legitimate users.

**NEVER** disable brute force protection on authentication endpoints.

**NEVER** forget to return the `$response` from `afterController()` middleware -- the response will be silently lost.

---

## Middleware Chain Architecture

Middleware provides cross-cutting security concerns. The chain follows Django's pattern with four hooks executed in a specific order:

### Hook Execution Order

```
Request arrives
    |
    v
[Middleware 1] beforeController()    (forward order: 1 -> 2 -> 3)
[Middleware 2] beforeController()
[Middleware 3] beforeController()
    |
    v
Controller method executes
    |
    v  (if exception thrown, skip to afterException)
[Middleware 3] afterController()     (reverse order: 3 -> 2 -> 1)
[Middleware 2] afterController()
[Middleware 1] afterController()
    |
    v
[Middleware 3] beforeOutput()        (reverse order: 3 -> 2 -> 1)
[Middleware 2] beforeOutput()
[Middleware 1] beforeOutput()
    |
    v
Response sent
```

### Exception Handling Flow

```
Exception thrown during controller execution
    |
    v
[Middleware 3] afterException()      (reverse order: 3 -> 2 -> 1)
    |-- if handled (returns Response): continue to afterController chain
    |-- if not handled: propagate to next middleware
[Middleware 2] afterException()
[Middleware 1] afterException()
```

### Hook Signatures

| Hook | Signature | Direction | Purpose |
|------|-----------|-----------|---------|
| `beforeController` | `($controller, $methodName)` | Forward | Pre-execution checks, auth |
| `afterException` | `($controller, $methodName, $exception): Response` | Reverse | Exception recovery |
| `afterController` | `($controller, $methodName, $response): Response` | Reverse | Response modification |
| `beforeOutput` | `($controller, $methodName, $output): string` | Reverse | Output manipulation |

### Middleware Registration

```php
// App-level middleware (runs only for your app's controllers)
public function register(IRegistrationContext $context): void {
    $context->registerMiddleware(MySecurityMiddleware::class);
}

// Global middleware (NC 26+, runs across ALL apps)
$context->registerMiddleware(MonitoringMiddleware::class, true);
```

**ALWAYS** register middleware in `Application::register()`, not in `boot()`. Middleware must be available before any controller executes.

---

## Content Security Policy

### Per-Response CSP

```php
use OCP\AppFramework\Http\ContentSecurityPolicy;

$response = new TemplateResponse('myapp', 'main');
$csp = new ContentSecurityPolicy();
$csp->addAllowedImageDomain('https://images.example.com');
$csp->addAllowedConnectDomain('https://api.example.com');
$response->setContentSecurityPolicy($csp);
```

### Global CSP via Event Listener

Register a listener for `AddContentSecurityPolicyEvent` to modify CSP across all responses from your app.

### CSP Method Overview

| Method | Directive | Default |
|--------|-----------|---------|
| `allowInlineScript(bool)` | script-src 'unsafe-inline' | false |
| `allowInlineStyle(bool)` | style-src 'unsafe-inline' | false |
| `allowEvalScript(bool)` | script-src 'unsafe-eval' | false |
| `useStrictDynamic(bool)` | script-src 'strict-dynamic' | true |
| `addAllowedScriptDomain(string)` | script-src | -- |
| `addAllowedStyleDomain(string)` | style-src | -- |
| `addAllowedFontDomain(string)` | font-src | -- |
| `addAllowedImageDomain(string)` | img-src | -- |
| `addAllowedConnectDomain(string)` | connect-src | -- |
| `addAllowedMediaDomain(string)` | media-src | -- |
| `addAllowedObjectDomain(string)` | object-src | -- |
| `addAllowedFrameDomain(string)` | frame-src | -- |
| `addAllowedChildSrcDomain(string)` | child-src | -- |

**ALWAYS** use the most restrictive CSP possible. Only add domains that your app actually needs.

**NEVER** use `allowInlineScript(true)` or `allowEvalScript(true)` unless absolutely required -- these weaken XSS protection significantly.

---

## Security Events Catalog

| Event | Since | Purpose |
|-------|-------|---------|
| `BeforeUserLoggedInEvent` | NC 18 | Pre-login hook for custom validation |
| `PostLoginEvent` | NC 18 | Post-login hook for auditing |
| `LoginFailedEvent` | NC 19 | Failed login (known user) |
| `AnyLoginFailedEvent` | NC 26 | Any login failure (broader scope) |
| `UserFirstTimeLoggedInEvent` | NC 28 | First-ever login for onboarding |
| `TokenInvalidatedEvent` | NC 32 | Auth token revoked |
| `TwoFactorProviderChallengeFailed` | NC 28 | 2FA challenge failed |
| `TwoFactorProviderChallengePassed` | NC 28 | 2FA challenge succeeded |

Register listeners via `IRegistrationContext::registerEventListener()` in `Application::register()`.

---

## Authentication Mechanisms

| Method | Use Case | Details |
|--------|----------|---------|
| Session + CSRF | Browser-based access | Default for web UI |
| Basic Auth (app password) | Desktop/mobile clients | Via Login Flow v2 |
| OIDC Bearer Token | SSO integration | `Authorization: Bearer ID_TOKEN` |
| `OCS-APIRequest: true` header | API clients | Alternative to CSRF token |

**ALWAYS** use Login Flow v2 to obtain app passwords for external clients. NEVER store user passwords directly.

---

## Decision Trees

### Choosing Security Attributes

```
Is this endpoint admin-only?
  YES -> Use no attributes (default is admin-only)
  NO  -> Add #[NoAdminRequired]
         |
         Does it need to work without login?
           YES -> Add #[PublicPage]
           NO  -> Stop here
         |
         Is it called by an API client (not browser)?
           YES -> Add #[NoCSRFRequired]
           NO  -> Keep CSRF protection
         |
         Is it a 2FA setup page?
           YES -> Add #[NoTwoFactorRequired]
           NO  -> Keep 2FA requirement
```

### Choosing Rate Limiting

```
Is the endpoint sensitive (auth, data modification)?
  NO  -> No rate limiting needed
  YES -> Is it accessible to anonymous users?
           YES -> Add #[AnonRateLimit(limit: N, period: S)]
           NO  -> (skip)
         Is it accessible to authenticated users?
           YES -> Add #[UserRateLimit(limit: N, period: S)]
           NO  -> (skip)
         Does it involve credentials/tokens?
           YES -> Add #[BruteForceProtection(action: 'name')]
                  Call $response->throttle() on FAILURE only
           NO  -> Rate limiting is sufficient
```

---

## Reference Links

- [references/methods.md](references/methods.md) -- Security attributes, middleware methods, CSP methods
- [references/examples.md](references/examples.md) -- Security patterns, CSP configuration, middleware implementation
- [references/anti-patterns.md](references/anti-patterns.md) -- Security mistakes and how to avoid them

### Official Sources

- https://docs.nextcloud.com/server/latest/developer_manual/digging_deeper/middleware.html
- https://docs.nextcloud.com/server/latest/developer_manual/basics/controllers.html
- https://docs.nextcloud.com/server/latest/developer_manual/digging_deeper/security.html

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [Impertio-Studio](https://github.com/Impertio-Studio)
- **Source:** [Impertio-Studio/Nextcloud-Claude-Skill-Package](https://github.com/Impertio-Studio/Nextcloud-Claude-Skill-Package)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-impertio-studio-nextcloud-claude-skill-package-nextcloud-core-security
- Seller: https://agentstack.voostack.com/s/impertio-studio
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
