Status: The application is not yet ISO/IEC 27001 certified, however a number of technical security controls have been implemented following industry best practices (Laravel Security Best Practices, OWASP Top 10 2021, OWASP ASVS L1/L2) and can be mapped to ISO/IEC 27001:2022 Annex A.
This document serves as implementation evidence proving that security controls are actively enforced at the application/code level.
app/ and app/Http/Middleware/ (Laravel backend)The Amaturalist application applies defense-in-depth through layered Laravel middleware covering authentication, authorization, input validation, HTTP header hardening, admin activity logging, tiered rate limiting, and response sanitization. Summary of verified controls:
| Control Category | Implemented Controls | Primary Location |
|---|---|---|
| Access Control & AuthN/AuthZ | 7 | app/Http/Middleware/ |
| Cryptography & Data Protection | 4 | EncryptCookies, Hash::make, HTTPS/HSTS |
| Logging & Monitoring | 3 | LogAdminActivity, SecurityLogger, AdminActivityLog |
| Input Validation & Output Encoding | 2 | FormRequest, Validator, CSP |
| Network Security | 4 | CORS, Origin validation, TrustProxies, HSTS |
| Rate Limiting / Anti-Abuse | 8 profiles | RouteServiceProvider |
| Total | 28+ | |
| Clause | ISO Control | Implementation | Evidence (file) |
|---|---|---|---|
| A.5.15 | Access control policy | Access policy based on user level (1–4) and capability system | CheckAdminLevel.php, CheckCapability.php |
| A.5.17 | Authentication information | Passwords hashed with bcrypt, JWT tokens issued, remember tokens stripped from responses | SecureApiResponse.php, AuthController |
| A.5.18 | Access rights | Level-based + capability-based ACL, enforced per-route | CheckCapability.php, Kernel.php:73–82 |
| Clause | ISO Control | Implementation | Evidence (file & line) |
|---|---|---|---|
| A.8.2 | Privileged access rights | The admin.level middleware requires level ∈ {2,3,4} and auto-logs out invalid users | CheckAdminLevel.php:48–72 |
| A.8.3 | Information access restriction | Every admin route is protected by middleware; capability guard applies for granular actions | Kernel.php, CheckCapability.php |
| A.8.5 | Secure authentication | JWT (Tymon) for API, session guard for web, explicit handling of expired/blacklisted/invalid tokens | JwtMiddleware.php:24–55, Authenticate.php |
| A.8.6 | Capacity management | Tiered rate limiting per endpoint type (auth, polling, taxa search, observations, cached) | RouteServiceProvider.php:29–124 |
| A.8.9 | Configuration management | Centralized configuration for TrustedProxies, allowed CORS origins, CSP directives | TrustProxies.php, ValidateApiOrigin.php:15–25, SecurityHeaders.php:36–50 |
| A.8.10 | Information deletion | Soft delete (deleted_at) on models, session flush() on unauth | CheckAdminLevel.php:30 |
| A.8.12 | Data leakage prevention | SecureApiResponse strips 7 sensitive fields from all JSON responses; X-Powered-By/Server headers removed | SecureApiResponse.php:14–22, 53–54 |
| A.8.15 | Logging | All write requests (POST/PUT/PATCH/DELETE) by admins are logged: user_id, IP, UA, action, metadata (passwords/tokens excluded) | LogAdminActivity.php:60–80, 186–191 |
| A.8.16 | Monitoring activities | Failed logins, unauthorized access, session timeouts, and rate-limit breaches are logged to the security channel and trigger admin notifications | SecurityLogger.php:16–46, 78–142 |
| A.8.20 | Networks security | Strict CORS, Origin & Referer validation in production, TrustProxies for X-Forwarded-* headers | ValidateApiOrigin.php, TrustProxies.php, ForceCors.php |
| A.8.21 | Security of network services | HSTS (production + HTTPS), CSP, X-Frame-Options SAMEORIGIN, Permissions-Policy | SecurityHeaders.php:22–60 |
| A.8.23 | Web filtering | Content Security Policy restricts script/style/frame sources to a curated CDN whitelist | SecurityHeaders.php:36–51 |
| A.8.24 | Use of cryptography | EncryptCookies (AES via APP_KEY), Hash::make() bcrypt, HTTPS enforced via HSTS | EncryptCookies.php, Kernel.php:35 |
| A.8.25 | Secure development lifecycle | Laravel framework with latest security patches, composer.lock pinned dependencies | composer.lock |
| A.8.26 | Application security requirements | Form Request validation (e.g. LoginRequest) with rate-limit lockout | LoginRequest.php:40–76 |
| A.8.28 | Secure coding | Eloquent ORM (parameterized queries), Blade auto-escape, CSRF token for all web forms | VerifyCsrfToken.php, Laravel framework |
JWT for API — app/Http/Middleware/JwtMiddleware.php
Validates every token and returns distinct responses for invalid, expired, blacklisted, or missing tokens. Failed attempts are logged via Log::warning.
if ($e instanceof TokenExpiredException) {
return response()->json(['error' => 'Token Kadaluarsa', ...], 401);
} elseif ($e instanceof TokenBlacklistedException) {
return response()->json(['error' => 'Token Diblacklist', ...], 401);
}
Session-based for admin — Authenticate.php redirects API clients to JSON 401 and web users to the login page.
Optional Auth — OptionalAuth.php allows public endpoints to still recognize a logged-in user without enforcing authentication (used for features like can_edit).
Admin level — CheckAdminLevel.php
if (!in_array($user->level, [2, 3, 4])) {
Log::warning('Unauthorized admin access attempt', [...]);
Auth::logout();
return redirect()->route('admin.login')->withErrors([...]);
}
Granular capability — CheckCapability.php
Used as capability:publish_agenda,moderate_forum. Fail-safe behavior: if the argument list is empty → 403.
Http/Requests/Auth/LoginRequest.php — Uses RateLimiter allowing 5 attempts per email|IP combination, triggers a Lockout event, and returns a retry-after message.
if (! RateLimiter::tooManyAttempts($this->throttleKey(), 5)) return;
event(new Lockout($this));
throw ValidationException::withMessages([
'email' => trans('auth.throttle', ['seconds' => $seconds, 'minutes' => ceil($seconds / 60)])
]);
app/Providers/RouteServiceProvider.php defines 8 distinct rate limiter profiles: api, api-polling, api-profile, api-taxa-search, api-observations, api-comments, api-public, api-cached. Each profile distinguishes authenticated vs guest requests and uses an SHA-1 hash for token keys (so raw tokens never leak into Redis).
SecurityHeaders.php (global middleware) applies:
X-Frame-Options: SAMEORIGIN (anti-clickjacking)X-Content-Type-Options: nosniffX-XSS-Protection: 1; mode=blockReferrer-Policy: no-referrer-when-downgradecdn.jsdelivr.net, unpkg.com, cdnjs.cloudflare.com, cdn.tiny.cloud, code.jquery.com); object-src 'none'; base-uri 'self'; form-action 'self'Strict-Transport-Security: max-age=31536000; includeSubDomains; preload (production + HTTPS only)Permissions-Policy: camera=(), microphone=(), geolocation=(), interest-cohort=()ValidateApiOrigin.php — In production, validates Origin and Referer headers against a whitelist (amaturalist.com, api.amaturalist.com, etc.). Untrusted foreign-origin requests are rejected with HTTP 403.
VerifyCsrfToken.php is enabled for all web routes; only api/* routes are exempted because they use JWT bearer authentication (stateless). This matches the OWASP recommendation: CSRF tokens for state-changing forms, bearer tokens for API.
SecureApiResponse.php — Middleware in the api group that recursively strips sensitive fields from every JSON response:
protected $sensitiveFields = [
'password', 'remember_token', 'api_token',
'email_verification_token', 'password_reset_token',
'two_factor_secret', 'two_factor_recovery_codes',
];
It also removes X-Powered-By and Server headers to reduce information leaks.
Admin activity — LogAdminActivity.php records every POST/PUT/PATCH/DELETE into the admin_activity_logs table with: user_id, action, description (Indonesian labels), ip_address, user_agent, and metadata JSON. Passwords/tokens are excluded before persistence:
$formData = $request->except(['password', 'password_confirmation', '_token', '_method']);
Security events — App\Services\SecurityLogger broadcasts events to the security log channel and sends SecurityAlert notifications for 4 critical events: LOGIN_FAILED, RATE_LIMIT_EXCEEDED, UNAUTHORIZED_ACCESS, SUSPICIOUS_ACTIVITY.
Hash::make() (bcrypt) is called across 15 controller locations (verified via grep) — including AuthController, UserController, NewPasswordController, RegisteredUserController, ResetPasswordController.EncryptCookies middleware encrypts all cookies using APP_KEY (AES-256-CBC via Laravel's Encrypter).session_regenerate is called on login/logout and on privilege elevation detection (CheckAdminLevel.php:31, 61).Verified: 229+ invocations of $request->validate() / Validator::make() across 82 controllers (via ripgrep). Uses Laravel Validation with type-safe rules such as email, exists:, array|min:1, image|max:2048, etc. The TrimStrings and ConvertEmptyStringsToNull traits are active globally.
TrustProxies.php accepts X-Forwarded-* headers from proxies (Cloudflare/AWS ELB), ensuring $request->ip() returns the real client IP for accurate logging & rate limiting.
| OWASP Risk | Mitigation in Code | Status |
|---|---|---|
| A01 Broken Access Control | CheckAdminLevel, CheckCapability, Authenticate, capability guard | ✅ |
| A02 Cryptographic Failures | bcrypt passwords, EncryptCookies, HSTS, APP_KEY | ✅ |
| A03 Injection | Eloquent ORM (parameterized), $request->validate, Blade auto-escape | ✅ |
| A04 Insecure Design | Rate-limit lockout, defense-in-depth middleware stack | ✅ |
| A05 Security Misconfiguration | SecurityHeaders (CSP/HSTS/XFO), server headers hidden | ✅ |
| A06 Vulnerable Components | composer.lock, Laravel LTS framework | ⚠️ periodic dependency scan needed |
| A07 Identification & Auth Failures | JWT expiry/blacklist, session regen, login rate-limit | ✅ |
| A08 Software & Data Integrity | CSRF token, signed URL (ValidateSignature) | ✅ |
| A09 Logging & Monitoring | LogAdminActivity, SecurityLogger, SecurityAlert notifications | ✅ |
| A10 SSRF | CSP CDN whitelist, no user-supplied URL fetching in middleware | ✅ |
last_activity; if the session is gone, the user is redirected to re-login with a flash message (CheckAdminLevel.php:76).CheckAdminLevel.php:59–61).SecurityAlert notification).admin_activity_logs table is not exposed via any edit/delete endpoint.| Area | Current Status | Recommendation |
|---|---|---|
| Dependency scanning | Manual | Integrate composer audit / Dependabot in CI |
| Secret management | .env file | Vault / AWS Secrets Manager for production |
| Admin MFA | Not present | Add TOTP 2FA for level ≥ 3 |
| Backup & DR policy | Not documented in code | Create a DB backup SOP + restore drill |
| Penetration testing | Not scheduled | Annual pentest by a 3rd-party |
| Incident response plan | Not formalized | Formal IR document + SecurityAlert follow-up runbook |
| Log retention | No automatic rotation | Configure 90-day log rotation + archival |
| CSP unsafe-inline | Still enabled | Migrate to nonce-based CSP |
| Control | File |
|---|---|
| Global middleware stack | app/Http/Kernel.php |
| Security headers | app/Http/Middleware/SecurityHeaders.php |
| API response sanitization | app/Http/Middleware/SecureApiResponse.php |
| Origin validation | app/Http/Middleware/ValidateApiOrigin.php |
| JWT auth | app/Http/Middleware/JwtMiddleware.php |
| Admin level guard | app/Http/Middleware/CheckAdminLevel.php |
| Capability guard | app/Http/Middleware/CheckCapability.php |
| Optional auth | app/Http/Middleware/OptionalAuth.php |
| Admin activity audit | app/Http/Middleware/LogAdminActivity.php |
| CSRF | app/Http/Middleware/VerifyCsrfToken.php |
| Cookie encryption | app/Http/Middleware/EncryptCookies.php |
| Trust proxies | app/Http/Middleware/TrustProxies.php |
| Rate limiter profiles | app/Providers/RouteServiceProvider.php |
| Login lockout | app/Http/Requests/Auth/LoginRequest.php |
| Security event logger | app/Services/SecurityLogger.php |
| Additional docs | SECURITY_CHECKLIST.md, SECURITY_MEASURES.md, SECURITY_CONTROLS_ISO27001.md (Indonesian version) |
Prepared for: Internal audit, vendor documentation, security due diligence, and initial baseline toward ISO/IEC 27001 certification.
Reviewer: Amaturalist Engineering Team Review frequency: Every 6 months or after major changes to middleware/authentication.