Sikkerhet i moderne webapplikasjoner
Sikkerhet i moderne webapplikasjoner
Cybersikkerhet er ikke lenger et tillegg - det er grunnlaget for ethvert moderne webprosjekt. Basert på erfaring fra Inside NEXT Ecosystem og Laravel v12/Filament v3.3-økosystemet, her er essensielle sikkerhetsprinsipper som utviklere må mestre i 2025.
OWASP Top 10 i 2025
1. Injection-angrep
SQL-injection og andre injection-angrep fortsetter å være en kritisk trussel. Laravel's Eloquent ORM og Query Builder gir innebygd beskyttelse når brukt korrekt.
// ❌ FEIL måte - sårbar for SQL injection
$query = "SELECT * FROM users WHERE email = '" . $_POST["email"] . "'";
$users = DB::select($query);
// ✅ RIKTIG måte - med Laravel Query Builder
$users = DB::table("users")
->where("email", request("email"))
->get();
// ✅ Eller med Eloquent
$users = User::where("email", request("email"))->get();
// ✅ Med prepared statements for komplekse queries
$users = DB::select("SELECT * FROM users WHERE email = ? AND status = ?",
[request("email"), "active"]
);
NoSQL Injection beskyttelse:
// ✅ Valider MongoDB queries
$filter = [
'email' => ['$regex' => '^' . preg_quote(request('email')) . '$', '$options' => 'i']
];
2. Autentisering og Session Management
Sikker brukerautentisering er kritisk for applikasjonssikkerhet.
// ✅ Laravel beste praksis for innlogging
class LoginController extends Controller
{
public function login(Request $request)
{
$credentials = $request->validate([
"email" => "required|email|max:255",
"password" => "required|min:8|max:255"
]);
// Rate limiting på innloggingsforsøk
$key = 'login_attempts:' . $request->ip();
if (RateLimiter::tooManyAttempts($key, 5)) {
return back()->withErrors([
'email' => 'For mange innloggingsforsøk. Prøv igjen om ' .
RateLimiter::availableIn($key) . ' sekunder.'
]);
}
if (Auth::attempt($credentials, $request->boolean('remember'))) {
$request->session()->regenerate();
RateLimiter::clear($key);
// Log suksessfullt innlogg
Log::info('Bruker logget inn', [
'user_id' => Auth::id(),
'ip' => $request->ip(),
'user_agent' => $request->userAgent()
]);
return redirect()->intended('/dashboard');
}
RateLimiter::hit($key, 300); // 5 minutter timeout
return back()->withErrors([
"email" => "Ugyldig påloggingsinfo."
])->onlyInput('email');
}
}
To-faktor autentisering (2FA):
// ✅ Implementer 2FA med Laravel Fortify
use Laravel\Fortify\TwoFactorAuthenticatable;
class User extends Authenticatable
{
use TwoFactorAuthenticatable;
// Krev 2FA for administrative roller
public function requiresTwoFactorAuth(): bool
{
return $this->hasRole(['admin', 'moderator']);
}
}
3. Sensitive Data Exposure
Beskytt følsomme data både i transit og i hvile.
// ✅ Kryptering av sensitive felteer
use Illuminate\Contracts\Encryption\Encrypter;
class User extends Model
{
protected $casts = [
'social_security_number' => 'encrypted',
'credit_card_number' => 'encrypted',
'medical_records' => 'encrypted:array'
];
protected $hidden = [
'password',
'remember_token',
'two_factor_secret',
'social_security_number'
];
}
// ✅ Environment-basert konfigurasjoner
// .env
DB_PASSWORD=your_secure_password
MAIL_PASSWORD=your_mail_password
STRIPE_SECRET=your_stripe_secret
// ✅ Bruk Laravel Vault for hemmeligheter i produksjon
4. XML External Entities (XXE)
Beskytt mot XML-baserte angrep.
// ✅ Sikker XML-behandling
class XmlProcessor
{
public function parseXml(string $xmlString): \SimpleXMLElement
{
// Deaktiver external entities
libxml_disable_entity_loader(true);
$xml = simplexml_load_string($xmlString, 'SimpleXMLElement', LIBXML_NOCDATA);
if ($xml === false) {
throw new InvalidArgumentException('Ugyldig XML format');
}
return $xml;
}
}
5. Broken Access Control
Implementer robust tilgangskontroll på alle nivåer.
// ✅ Laravel Policy-basert autorisasjon
class PostPolicy
{
public function update(User $user, Post $post): bool
{
return $user->id === $post->user_id || $user->hasRole('admin');
}
public function delete(User $user, Post $post): bool
{
return ($user->id === $post->user_id && $post->created_at->diffInHours() < 24)
|| $user->hasRole('admin');
}
}
// ✅ Middleware for rolle-basert tilgang
class EnsureUserHasRole
{
public function handle(Request $request, Closure $next, string $role): Response
{
if (!$request->user() || !$request->user()->hasRole($role)) {
abort(403, 'Utilstrekkelige tilganger');
}
return $next($request);
}
}
// ✅ Filament Resource med tilgangskontroll
class UserResource extends Resource
{
public static function canViewAny(): bool
{
return auth()->user()->hasRole(['admin', 'user_manager']);
}
public static function canCreate(): bool
{
return auth()->user()->hasRole('admin');
}
}
API Sikkerhet
Rate Limiting og Throttling
// ✅ Avansert rate limiting
Route::middleware([
'throttle:api',
'throttle:60,1,login' // Spesiell limit for innlogging
])->group(function () {
Route::post('/login', [AuthController::class, 'login']);
});
// ✅ Custom rate limiter
class ApiRateLimiter
{
public function boot()
{
RateLimiter::for('api', function (Request $request) {
$limit = $request->user()?->isPremium() ? 10000 : 1000;
return Limit::perMinute($limit)
->by($request->user()?->id ?: $request->ip())
->response(function () {
return response()->json([
'message' => 'Rate limit overskredet'
], 429);
});
});
}
}
API Autentisering
// ✅ Laravel Sanctum for API tokens
class ApiAuthController extends Controller
{
public function login(Request $request)
{
$credentials = $request->validate([
'email' => 'required|email',
'password' => 'required'
]);
if (!Auth::attempt($credentials)) {
return response()->json([
'message' => 'Ugyldig påloggingsinfo'
], 401);
}
$user = $request->user();
$token = $user->createToken('API Token', [
'read-posts',
'create-posts'
])->plainTextToken;
return response()->json([
'access_token' => $token,
'token_type' => 'Bearer',
'expires_in' => config('sanctum.expiration') * 60
]);
}
}
CORS Konfigurasjoner
// ✅ config/cors.php - Streng CORS-policy
return [
'paths' => ['api/*', 'sanctum/csrf-cookie'],
'allowed_methods' => ['GET', 'POST', 'PUT', 'DELETE'],
'allowed_origins' => [
'https://yourdomain.com',
'https://admin.yourdomain.com'
],
'allowed_origins_patterns' => [
'/^https:\/\/[\w\-]+\.yourdomain\.com$/'
],
'allowed_headers' => [
'Content-Type',
'Authorization',
'X-Requested-With',
'X-CSRF-TOKEN'
],
'exposed_headers' => ['X-Pagination-Total'],
'max_age' => 86400,
'supports_credentials' => true,
];
Security Headers og CSP
Content Security Policy
// ✅ Middleware for sikkerhetshoder
class SecurityHeadersMiddleware
{
public function handle(Request $request, Closure $next): Response
{
$response = $next($request);
// Content Security Policy
$csp = implode('; ', [
"default-src 'self'",
"script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net",
"style-src 'self' 'unsafe-inline' https://fonts.googleapis.com",
"font-src 'self' https://fonts.gstatic.com",
"img-src 'self' data: https:",
"connect-src 'self'",
"frame-ancestors 'none'",
"base-uri 'self'",
"form-action 'self'"
]);
$response->headers->set('Content-Security-Policy', $csp);
$response->headers->set('X-Content-Type-Options', 'nosniff');
$response->headers->set('X-Frame-Options', 'DENY');
$response->headers->set('X-XSS-Protection', '1; mode=block');
$response->headers->set('Referrer-Policy', 'strict-origin-when-cross-origin');
$response->headers->set('Permissions-Policy', 'camera=(), microphone=(), geolocation=()');
return $response;
}
}
HTTPS og HSTS
// ✅ Force HTTPS i produksjon
class ForceHttpsMiddleware
{
public function handle(Request $request, Closure $next): Response
{
if (!$request->secure() && app()->environment('production')) {
return redirect()->secure($request->getRequestUri(), 301);
}
$response = $next($request);
// HTTP Strict Transport Security
if ($request->secure()) {
$response->headers->set('Strict-Transport-Security',
'max-age=31536000; includeSubDomains; preload');
}
return $response;
}
}
Input Validering og Sanitering
Laravel Form Requests
// ✅ Omfattende validering
class CreatePostRequest extends FormRequest
{
public function authorize(): bool
{
return $this->user()->can('create', Post::class);
}
public function rules(): array
{
return [
'title' => [
'required',
'string',
'max:255',
'regex:/^[a-zA-Z0-9\s\-\_\.\!]+$/' // Kun tillatte tegn
],
'content' => [
'required',
'string',
'max:10000',
new NoMaliciousContent() // Custom regel
],
'tags' => 'array|max:5',
'tags.*' => 'string|max:50|alpha_dash',
'image' => [
'nullable',
'image',
'max:2048', // 2MB
'dimensions:min_width=100,min_height=100,max_width=2000,max_height=2000'
]
];
}
protected function prepareForValidation(): void
{
$this->merge([
'title' => trim(strip_tags($this->title)),
'content' => $this->sanitizeHtml($this->content)
]);
}
private function sanitizeHtml(string $html): string
{
return HTMLPurifier::clean($html, [
'HTML.Allowed' => 'p,br,strong,em,ul,ol,li,a[href],blockquote'
]);
}
}
Custom Validering Rules
// ✅ Custom sikkerhet rules
class NoMaliciousContent implements Rule
{
private array $maliciousPatterns = [
'/<script[^>]*>.*?<\/script>/is',
'/javascript:/i',
'/on\w+\s*=/i',
'/<iframe[^>]*>.*?<\/iframe>/is'
];
public function passes($attribute, $value): bool
{
foreach ($this->maliciousPatterns as $pattern) {
if (preg_match($pattern, $value)) {
return false;
}
}
return true;
}
public function message(): string
{
return 'Innholdet inneholder ikke-tillatt kode.';
}
}
File Upload Sikkerhet
// ✅ Sikker filupplasting
class FileUploadController extends Controller
{
private array $allowedMimes = [
'image/jpeg', 'image/png', 'image/gif', 'image/webp',
'application/pdf', 'text/plain'
];
private array $dangerousExtensions = [
'php', 'phtml', 'php3', 'php4', 'php5', 'pl', 'py',
'jsp', 'asp', 'sh', 'cgi', 'exe', 'bat', 'com'
];
public function upload(Request $request)
{
$request->validate([
'file' => [
'required',
'file',
'max:5120', // 5MB
function ($attribute, $value, $fail) {
if (!$this->isSecureFile($value)) {
$fail('Filen er ikke tillatt eller inneholder skadelig innhold.');
}
}
]
]);
$file = $request->file('file');
$hashedName = hash('sha256', $file->getClientOriginalName() . time());
$extension = $file->getClientOriginalExtension();
// Lagre utenfor web root
$path = $file->storeAs(
'secure-uploads',
$hashedName . '.' . $extension,
'private'
);
return response()->json(['path' => $path]);
}
private function isSecureFile(UploadedFile $file): bool
{
// Sjekk MIME type
if (!in_array($file->getMimeType(), $this->allowedMimes)) {
return false;
}
// Sjekk farlige utvidelser
$extension = strtolower($file->getClientOriginalExtension());
if (in_array($extension, $this->dangerousExtensions)) {
return false;
}
// Virus scanning (krever ClamAV)
if ($this->containsVirus($file)) {
return false;
}
return true;
}
}
Logging og Monitoring
Sikkerhet Logging
// ✅ Strukturert sikkerhetslogging
class SecurityLogger
{
public static function logSuspiciousActivity(string $event, array $context = []): void
{
Log::channel('security')->warning($event, array_merge([
'ip' => request()->ip(),
'user_agent' => request()->userAgent(),
'user_id' => auth()->id(),
'timestamp' => now()->toISOString(),
'session_id' => session()->getId()
], $context));
}
public static function logAuthenticationFailure(string $email): void
{
self::logSuspiciousActivity('Authentication failure', [
'email' => $email,
'severity' => 'medium'
]);
}
public static function logPrivilegeEscalation(User $user, string $action): void
{
self::logSuspiciousActivity('Privilege escalation attempt', [
'user_id' => $user->id,
'action' => $action,
'severity' => 'high'
]);
}
}
// ✅ Filament Admin panel sikkerhet
class AdminPanelProvider extends PanelProvider
{
public function panel(Panel $panel): Panel
{
return $panel
->middleware([
'auth',
'admin-only',
'log-admin-access' // Custom middleware
])
->authGuard('admin')
->login()
->registration(false) // Deaktiver registrering
->passwordReset(false)
->emailVerification(false);
}
}
Monitoring Middleware
// ✅ Overvåking av mistenkelig aktivitet
class SecurityMonitoringMiddleware
{
public function handle(Request $request, Closure $next): Response
{
$startTime = microtime(true);
// Sjekk for SQL injection forsøk
if ($this->containsSqlInjectionPatterns($request)) {
SecurityLogger::logSuspiciousActivity('SQL injection attempt', [
'url' => $request->fullUrl(),
'payload' => $request->all()
]);
return response()->json(['error' => 'Ugyldig forespørsel'], 400);
}
$response = $next($request);
// Log langsomme forespørsler (mulig DoS)
$executionTime = microtime(true) - $startTime;
if ($executionTime > 5.0) {
SecurityLogger::logSuspiciousActivity('Slow request detected', [
'execution_time' => $executionTime,
'url' => $request->fullUrl()
]);
}
return $response;
}
private function containsSqlInjectionPatterns(Request $request): bool
{
$patterns = [
'/union\s+select/i',
'/drop\s+table/i',
'/delete\s+from/i',
'/<script[^>]*>.*?<\/script>/is',
'/or\s+1\s*=\s*1/i'
];
$input = json_encode($request->all());
foreach ($patterns as $pattern) {
if (preg_match($pattern, $input)) {
return true;
}
}
return false;
}
}
Database Sikkerhet
Kryptering og Hashing
// ✅ Database kryptering
class EncryptedModel extends Model
{
protected function casts(): array
{
return [
'sensitive_data' => 'encrypted',
'personal_info' => 'encrypted:array',
'password' => 'hashed' // Laravel 11+
];
}
// For eldre versjoner
public function setPasswordAttribute(string $password): void
{
$this->attributes['password'] = Hash::make($password);
}
}
// ✅ Database tilkoblingssikkerhet
// config/database.php
'mysql' => [
'driver' => 'mysql',
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'options' => [
PDO::MYSQL_ATTR_SSL_CA => env('DB_SSL_CA'),
PDO::MYSQL_ATTR_SSL_CERT => env('DB_SSL_CERT'),
PDO::MYSQL_ATTR_SSL_KEY => env('DB_SSL_KEY'),
PDO::MYSQL_ATTR_SSL_CIPHER => 'DHE-RSA-AES256-SHA',
PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => true,
],
],
Production Security Checklist
Environment Configuration
# ✅ .env produksjon konfigurasjon
APP_ENV=production
APP_DEBUG=false
APP_URL=https://yourdomain.com
# Sterke, unike nøkler
APP_KEY=base64:your-32-character-secret-key
DB_PASSWORD=your-very-strong-database-password
# Sikre session innstillinger
SESSION_SECURE_COOKIE=true
SESSION_HTTP_ONLY=true
SESSION_SAME_SITE=strict
# HTTPS enforcing
FORCE_HTTPS=true
Server Konfigurasjoner
# ✅ Nginx sikkerhetskonfigurasjon
server {
listen 443 ssl http2;
server_name yourdomain.com;
# SSL konfigurasjoner
ssl_certificate /path/to/certificate.pem;
ssl_certificate_key /path/to/private.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512;
# Sikkerhets headers
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
add_header X-XSS-Protection "1; mode=block";
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload";
# Skjul server versjon
server_tokens off;
# Rate limiting
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
location /login {
limit_req zone=login burst=5 nodelay;
try_files $uri $uri/ /index.php?$query_string;
}
}
Mine anbefalinger for 2025
1. Zero Trust Architecture
Implementer "aldri stol, alltid verifiser" prinsippet:
- Hver forespørsel må autentiseres og autoriseres
- Bruk kort-levde tokens (JWT med 15-30 min utløp)
- Implementer mikro-segmentering i nettverkslaget
2. Advanced Threat Protection
// ✅ AI-basert anomalideteksjon
class ThreatDetectionService
{
public function analyzeRequest(Request $request): ThreatLevel
{
$score = 0;
// Sjekk IP reputasjon
if ($this->isKnownMaliciousIp($request->ip())) {
$score += 50;
}
// Analysér forespørselsmønster
if ($this->hasAnomalousPattern($request)) {
$score += 30;
}
// Geolocation anomali
if ($this->isUnusualLocation($request)) {
$score += 20;
}
return ThreatLevel::fromScore($score);
}
}
3. Container Security (Docker/Kubernetes)
# ✅ Sikker Docker konfigurasjon
FROM php:8.3-fpm-alpine
# Ikke kjør som root
RUN addgroup -g 1001 -S appuser && \
adduser -S appuser -G appuser -u 1001
USER appuser
# Minimal image med kun nødvendige pakker
RUN apk add --no-cache \
nginx \
supervisor
# Sikkerhet scanning
LABEL security.scanner="trivy"
4. Infrastructure as Code Security
# ✅ Terraform sikkerhetskonfigurasjon
resource "aws_s3_bucket_public_access_block" "private" {
bucket = aws_s3_bucket.app_data.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
resource "aws_db_instance" "main" {
allocated_storage = 20
storage_encrypted = true
engine = "mysql"
engine_version = "8.0"
instance_class = "db.t3.micro"
# Sikkerhet
backup_retention_period = 7
backup_window = "03:00-04:00"
maintenance_window = "sun:04:00-sun:05:00"
# Nettverk sikkerhet
vpc_security_group_ids = [aws_security_group.database.id]
db_subnet_group_name = aws_db_subnet_group.main.name
# Overvåking
monitoring_interval = 60
enabled_cloudwatch_logs_exports = ["error", "general", "slow-query"]
}
5. Essential Security Tools Integration
- Implementer 2FA/MFA på alle administrative kontoer
- Bruk HTTPS overalt med HSTS headers
- Valider all input både på frontend og backend
- Hold avhengigheter oppdatert med automatiserte sikkerhetsscanninger
- Utfør regelmessige penetrasjonstester og code reviews
- Implementer bug bounty program for større applikasjoner
- Bruk Web Application Firewall (WAF) for ekstra beskyttelse
- Sett opp Security Operations Center (SOC) for kontinuerlig overvåking
6. Compliance og Personvern
// ✅ GDPR compliance verktøy
class GdprComplianceService
{
public function handleDataDeletionRequest(User $user): void
{
DB::transaction(function () use ($user) {
// Anonymiser data som må beholdes for legal compliance
$user->update([
'email' => 'deleted_user_' . $user->id . '@anonymized.local',
'name' => 'Slettet bruker',
'phone' => null,
'address' => null
]);
// Slett tilknyttet personlig data
$user->personalData()->delete();
$user->preferences()->delete();
// Log for compliance
Log::info('GDPR data deletion completed', ['user_id' => $user->id]);
});
}
}
Konklusjon
Sikkerhet er en kontinuerlig prosess, ikke et engangsprosjekt. Med Laravel v12, Filament v3.3, og moderne web-teknologier har vi kraftige verktøy tilgjengelig, men de må brukes riktig og oppdateres jevnlig.
Husk: Den beste sikkerheten kommer fra å bygge sikre praksis inn i utviklingsprosessen fra dag én, ikke som et ettertanke. Invester i sikkerhetstrening for teamet, automatiser sikkerhetstesting, og hold deg oppdatert på de nyeste truslene og forsvarsteknikkene.
I 2025 er ikke spørsmålet om du vil bli angrepet, men når. Vær forberedt.