1. Pengenalan Content Security Policy
Content Security Policy (CSP) adalah mekanisme keamanan berbasis HTTP header yang dirancang untuk melindungi aplikasi web dari serangan Cross-Site Scripting (XSS), clickjacking, dan berbagai jenis injection attack lainnya. CSP bekerja dengan cara mendefinisikan whitelist β daftar sumber konten yang diizinkan untuk dimuat dan dieksekusi oleh browser.
Ketika sebuah halaman web tidak memiliki CSP, browser secara default mengizinkan konten dari sumber mana saja β termasuk skrip inline, eval, dan resource dari domain pihak ketiga yang mungkin berbahaya. CSP memberikan kontrol penuh kepada developer untuk menentukan dari mana browser boleh memuat resource.
Mengapa CSP Penting?
| Alasan | Penjelasan |
|---|---|
| Mitigasi XSS | CSP membatasi skrip yang bisa dieksekusi, sehingga attacker tidak bisa menjalankan skrip berbahaya meskipun berhasil menyuntikkannya ke halaman |
| Perlindungan Injection | Memblokir eksekusi skrip dari sumber yang tidak terpercaya, mengurangi dampak dari SQL injection dan DOM injection |
| Clickjacking Prevention | Dengan direktif frame-ancestors, CSP bisa mencegah halaman dimuat di dalam iframe milik attacker |
| Data Exfiltration | Direktif connect-src membatasi ke mana data bisa dikirim, mencegah attacker mengirim data curian ke server mereka |
| Compliance | Banyak standar keamanan dan regulasi (PCI-DSS, HIPAA) merekomendasikan atau mewajibkan implementasi CSP |
Sejarah CSP
CSP pertama kali diperkenalkan pada tahun 2012 oleh Mozilla sebagai proposal untuk mengatasi masalah XSS yang terus menjadi ancaman utama keamanan web. Seiring waktu, CSP berevolusi dari Level 1 ke Level 3, menambahkan fitur-fitur seperti nonce, hash, dan reporting API. Saat ini, CSP didukung oleh semua browser modern termasuk Chrome, Firefox, Safari, dan Edge.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β BROWSER (Client) β β β β βββββββββββββββ ββββββββββββββββ β β β HTML Page ββββββΆβ CSP Header β β β β dimuat β β diterima β β β βββββββββββββββ ββββββββ¬ββββββββ β β β β β ββββββββββΌβββββββββ β β β Policy Parser β β β ββββββββββ¬βββββββββ β β β β β βββββββββββββββββββββΌββββββββββββββββββββ β β βΌ βΌ βΌ β β βββββββββββββββ ββββββββββββββββ ββββββββββββββ β β β Inline Scriptβ β External JS β β Images/ β β β β β Diblokir β β β Diizinkan β β CSS β β β β β (tanpa nonce)β β (dari origin β β (sesuai β β β β β β yang benar) β β policy) β β β βββββββββββββββ ββββββββββββββββ ββββββββββββββ β β β β βββββββββββββββββββββββββββββββββββββββββββββββββββ β β β CSP Violation Report β β β β β Dikirim ke report-uri/report-to endpoint β β β βββββββββββββββββββββββββββββββββββββββββββββββββββ β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
2. Cara Kerja CSP
CSP diimplementasikan melalui HTTP header yang dikirim oleh server ke browser bersama dengan response halaman web. Browser kemudian menganalisis header tersebut dan menerapkan aturan yang didefinisikan sebelum memuat dan mengeksekusi resource apa pun.
Dua Cara Mengirim CSP
# Cara 1: Via HTTP Header (Direkomendasikan)
# Ditambahkan di konfigurasi server (Nginx, Apache, dll)
# Nginx
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline';" always;
# Apache
Header set Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline';"
# Node.js / Express
app.use((req, res, next) => {
res.setHeader('Content-Security-Policy',
"default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline';");
next();
});
# ============================================
# Cara 2: Via HTML Meta Tag (Kurang Fleksibel)
# <meta http-equiv="Content-Security-Policy"
# content="default-src 'self'; script-src 'self';">
# Catatan: Tidak semua direktif didukung via meta tag,
# misalnya report-uri dan frame-ancestors.
Proses Evaluasi CSP
Setiap kali browser ingin memuat resource (script, image, stylesheet, font, dll), browser melakukan pengecekan terhadap policy yang berlaku:
- Browser menerima HTTP response yang mengandung header
Content-Security-Policy - Policy di-parse dan disimpan dalam memori browser
- Ketika ada resource yang akan dimuat, browser memeriksa apakah origin resource tersebut diizinkan oleh direktif yang sesuai
- Jika resource diizinkan β resource dimuat normal
- Jika resource TIDAK diizinkan β resource diblokir dan violation report dikirim (jika reporting dikonfigurasi)
Content-Security-Policyβ Menerapkan policy dan memblokir resource yang melanggarContent-Security-Policy-Report-Onlyβ Hanya melaporkan pelanggaran tanpa memblokir, sangat berguna untuk testing sebelum enforce
3. Direktif-Direktif CSP
CSP memiliki banyak direktif yang masing-masing mengontrol jenis resource tertentu. Memahami setiap direktif adalah kunci untuk membuat policy yang efektif namun tidak memecah fungsionalitas website.
Daftar Direktif CSP Lengkap
| Direktif | Fungsi | Contoh Nilai |
|---|---|---|
default-src | Fallback untuk semua direktif resource yang tidak didefinisikan secara eksplisit | 'self' |
script-src | Menentukan sumber JavaScript yang diizinkan | 'self' 'nonce-abc123' |
style-src | Menentukan sumber CSS yang diizinkan | 'self' 'unsafe-inline' |
img-src | Menentukan sumber gambar yang diizinkan | 'self' data: https: |
font-src | Menentukan sumber font yang diizinkan | 'self' fonts.gstatic.com |
connect-src | Menentukan target untuk XMLHttpRequest, fetch, WebSocket | 'self' api.example.com |
frame-src | Menentukan sumber frame/iframe yang diizinkan | 'self' https://www.youtube.com |
frame-ancestors | Menentukan siapa yang boleh meng-embed halaman dalam iframe | 'self' |
media-src | Menentukan sumber audio dan video yang diizinkan | 'self' cdn.example.com |
object-src | Menentukan sumber plugin (Flash, Java) yang diizinkan | 'none' |
base-uri | Menentukan URL base yang valid untuk tag <base> | 'self' |
form-action | Menentukan target form submission yang diizinkan | 'self' |
worker-src | Menentukan sumber Web Worker dan Service Worker | 'self' |
manifest-src | Menentukan sumber application manifest | 'self' |
prefetch-src | Menentukan sumber yang boleh di-prefetch/pre-render | 'self' |
navigate-to | Menentukan tujuan navigasi yang diizinkan (experimental) | 'self' example.com |
Keyword Values
| Keyword | Arti | Contoh Penggunaan |
|---|---|---|
'self' | Mengizinkan resource dari origin yang sama (scheme + host + port) | script-src 'self' |
'none' | Tidak mengizinkan resource sama sekali | object-src 'none' |
'unsafe-inline' | Mengizinkan inline script dan style (TIDAK direkomendasikan) | script-src 'unsafe-inline' |
'unsafe-eval' | Mengizinkan penggunaan eval() dan sejenisnya | script-src 'unsafe-eval' |
'strict-dynamic' | Mempercayai script yang dimuat oleh script trusted lainnya | script-src 'strict-dynamic' 'nonce-xxx' |
'unsafe-hashes' | Mengizinkan event handler spesifik berdasarkan hash | script-src 'unsafe-hashes' 'sha256-xxx' |
Contoh Policy untuk Berbagai Skenario
# Policy ketat untuk website statis tanpa JavaScript eksternal Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; object-src 'none'; frame-ancestors 'self'; base-uri 'self'; form-action 'self'
# Single Page Application dengan REST API Content-Security-Policy: \ default-src 'self'; \ script-src 'self' 'nonce-r4nd0m'; \ style-src 'self' 'unsafe-inline'; \ img-src 'self' https://cdn.example.com data:; \ font-src 'self' https://fonts.gstatic.com; \ connect-src 'self' https://api.example.com wss://ws.example.com; \ frame-src 'none'; \ object-src 'none'; \ base-uri 'self'; \ form-action 'self'
# Website yang menggunakan CDN untuk static assets Content-Security-Policy: \ default-src 'self'; \ script-src 'self' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com; \ style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://fonts.googleapis.com; \ img-src 'self' https://*.cloudfront.net https://images.unsplash.com data:; \ font-src 'self' https://fonts.gstatic.com; \ connect-src 'self'; \ media-src 'self' https://*.cloudfront.net; \ frame-ancestors 'none'
4. Nonce dan Hash
Salah satu tantangan terbesar dalam implementasi CSP adalah menangani inline script β script yang ditulis langsung di dalam tag HTML. Menggunakan 'unsafe-inline' bukan solusi karena akan mengalahkan tujuan utama CSP. Di sinilah nonce dan hash berperan.
Apa Itu Nonce?
Nonce (Number Used Once) adalah string acak yang dihasilkan secara kriptografis untuk setiap request. Nonce ditambahkan ke CSP header dan juga ke tag script/style yang diizinkan. Browser hanya akan mengeksekusi script yang memiliki nonce yang cocok dengan yang ada di header.
# Langkah 1: Server menghasilkan nonce unik untuk setiap request
# (Di Node.js/Express)
const crypto = require('crypto');
app.use((req, res, next) => {
// Generate nonce random 128-bit, encode base64
const nonce = crypto.randomBytes(16).toString('base64');
// Simpan nonce di res.locals agar bisa diakses di template
res.locals.nonce = nonce;
// Set CSP header dengan nonce
res.setHeader('Content-Security-Policy',
"script-src 'nonce-" + nonce + "' 'strict-dynamic'; " +
"style-src 'self' 'nonce-" + nonce + "'; " +
"default-src 'self'; " +
"object-src 'none'; " +
"base-uri 'self'"
);
next();
});
# ============================================
# Langkah 2: Di template HTML, tambahkan nonce ke tag script
# <script nonce="<%= nonce %>">
# // Inline script yang diizinkan
# console.log('Script ini diizinkan oleh CSP');
# </script>
# Script TANPA nonce akan DIBLOKIR oleh browser:
# <script>
# // Script ini TIDAK akan dieksekusi
# alert('Ini diblokir!');
# </script>
# ============================================
# Konfigurasi Nginx dengan variable untuk nonce
# generate_nonce.conf
perl_set $csp_nonce 'use Digest::MD5 qw(md5_hex);
md5_hex(time() . rand())';
server {
add_header Content-Security-Policy
"script-src 'nonce-$csp_nonce' 'strict-dynamic'; default-src 'self'" always;
}
Apa Itu Hash?
Hash (Subresource Integrity Hash) bekerja dengan cara yang berbeda dari nonce. Alih-alih string acak, hash dihitung dari konten script itu sendiri. Browser menghitung hash dari konten script yang ditemukan di halaman dan membandingkannya dengan hash yang didefinisikan di CSP header.
# Menghitung hash SHA-256 dari konten script
# Contoh script:
# <script>alert('Hello World');</script>
# Cara menghitung hash:
# echo -n "alert('Hello World');" | openssl dgst -sha256 -binary | openssl base64
# Output: qUqP5cyxm6YcTAhz05Hph5gvu9M=
# Atau menggunakan Node.js:
const crypto = require('crypto');
const scriptContent = "alert('Hello World');";
const hash = crypto.createHash('sha256')
.update(scriptContent)
.digest('base64');
// hash = 'qUqP5cyxm6YcTAhz05Hph5gvu9M='
# CSP Header dengan hash:
Content-Security-Policy: script-src 'sha256-qUqP5cyxm6YcTAhz05Hph5gvu9M='
# ============================================
# Kalkulasi hash untuk beberapa script:
# Script 1: document.getElementById('app').innerHTML = 'Loaded';
# Hash: sha256-xTc7sChFfZVR+d9jBhpCKBR7a8goKjP2NqFW2HKBsEM=
# Script 2: window.dataLayer = window.dataLayer || [];
# Hash: sha256-Gg/IBtfCFBWkZAijOBaMGWm3FkSJKSMr+YOyhLBrJq8=
# Gabungan:
Content-Security-Policy: script-src 'sha256-xTc7sChFfZVR+d9jBhpCKBR7a8goKjP2NqFW2HKBsEM=' 'sha256-Gg/IBtfCFBWkZAijOBaMGWm3FkSJKSMr+YOyhLBrJq8='
# ============================================
# Kelebihan Hash vs Nonce:
# β
Hash = deterministik, bisa di-cache
# β
Hash = tidak perlu generate per-request
# β
Hash = lebih aman (tidak bisa diprediksi attacker)
# β Hash = harus dihitung ulang jika konten script berubah
# β Hash = tidak praktis untuk banyak inline script
Perbandingan Nonce vs Hash
| Aspek | Nonce | Hash |
|---|---|---|
| Generasi | Random per-request | Dihitung dari konten script |
| Caching | Sulit (berbeda setiap request) | Mudah (selalu sama untuk konten yang sama) |
| Keamanan | Bergantung pada randomness | Lebih deterministik |
| Kemudahan | Mudah diimplementasikan | Perlu kalkulasi hash |
| Dynamic Content | Cocok untuk konten dinamis | Cocok untuk konten statis |
| Rekomendasi | Dengan 'strict-dynamic' | Untuk script yang jarang berubah |
'strict-dynamic' dan Hierarki Trust
Keyword 'strict-dynamic' memungkinkan script yang sudah trusted (memiliki nonce/hash yang valid) untuk memuat script lain secara dinamis. Ini sangat berguna untuk framework JavaScript yang perlu memuat module secara runtime.
ββββββββββββββββββββββββββββββββββββββββββββββββββββ
β STRICT-DYNAMIC FLOW β
β β
β βββββββββββββββββββββββββββββββββββββββ β
β β <script nonce="abc123"> β β
β β // Script dengan nonce VALID β
β β
β β const s = document.createElement β β
β β ('script'); β β
β β s.src = '/app-bundle.js'; β β
β β document.body.appendChild(s); β β
β β </script> β β
β ββββββββββββββββ¬βββββββββββββββββββββββ β
β β β
β βΌ β
β βββββββββββββββββββββββββββββββββββββββ β
β β /app-bundle.js β β
β β β
Diizinkan karena dimuat oleh β β
β β script yang memiliki nonce β β
β β (parent script terpercaya) β β
β ββββββββββββββββ¬βββββββββββββββββββββββ β
β β β
β βΌ β
β βββββββββββββββββββββββββββββββββββββββ β
β β /plugin.js (dimuat oleh app-bundle) β β
β β β
Juga diizinkan (transitive trust)β β
β βββββββββββββββββββββββββββββββββββββββ β
β β
β βββββββββββββββββββββββββββββββββββββββ β
β β <script src="/evil.js"></script> β β
β β β DIBLOKIR β tanpa nonce dan β β
β β bukan dimuat oleh trusted script β β
β βββββββββββββββββββββββββββββββββββββββ β
ββββββββββββββββββββββββββββββββββββββββββββββββββββ
5. CSP Reporting
Salah satu fitur paling powerful dari CSP adalah kemampuannya untuk melaporkan pelanggaran policy. Dengan reporting, Anda bisa mengetahui ketika ada resource yang diblokir β baik itu serangan nyata atau resource legitimate yang perlu ditambahkan ke whitelist.
Dua Mekanisme Reporting
| Mekanisme | Status | Deskripsi |
|---|---|---|
report-uri | Deprecated | Mekanisme lama β menentukan URL endpoint untuk menerima violation reports |
report-to | Modern (Level 3) | Mekanisme baru β menggunakan Reporting API dengan named endpoint groups |
# ============================================
# Konfigurasi Reporting dengan Reporting API
# ============================================
# 1. Definisikan reporting endpoint (di HTTP header Reporting-Endpoints)
Reporting-Endpoints: csp-endpoint="https://example.com/csp-reports"
# 2. Set CSP header dengan report-to
Content-Security-Policy: \
default-src 'self'; \
script-src 'self' 'nonce-abc123'; \
style-src 'self' 'unsafe-inline'; \
report-to csp-endpoint
# ============================================
# Report-Only Mode (untuk testing)
# ============================================
Content-Security-Policy-Report-Only: \
default-src 'self'; \
script-src 'self' 'nonce-abc123'; \
report-to csp-endpoint
# ============================================
# Contoh Violation Report (JSON yang dikirim browser)
# ============================================
{
"type": "csp-violation",
"age": 10,
"url": "https://example.com/page",
"user_agent": "Mozilla/5.0 ...",
"body": {
"documentURL": "https://example.com/page",
"referrer": "",
"blockedURL": "https://evil.com/malicious.js",
"violatedDirective": "script-src-elem",
"effectiveDirective": "script-src",
"originalPolicy": "script-src 'self' 'nonce-abc123'",
"disposition": "enforce",
"statusCode": 200,
"sample": ""
}
}
# ============================================
# Server-side: Menerima dan memproses reports
# (Node.js/Express)
# ============================================
const express = require('express');
const app = express();
app.post('/csp-reports', express.json({ type: 'application/csp-report' }), (req, res) => {
const report = req.body;
console.log('[CSP VIOLATION]', {
url: report.body?.documentURL,
blocked: report.body?.blockedURL,
directive: report.body?.violatedDirective,
policy: report.body?.originalPolicy,
timestamp: new Date().toISOString()
});
// Simpan ke database untuk analisis
// db.saveViolation(report);
res.status(204).end();
});
# Untuk report-to, server menerima array:
app.post('/csp-reports', express.json({ type: 'application/reports+json' }), (req, res) => {
const reports = req.body; // Array of reports
reports.forEach(report => {
console.log('[CSP]', report.body.blockedURL);
});
res.status(204).end();
});
- β
Mulai dengan
Content-Security-Policy-Report-Onlysebelum enforce - β Monitor reports selama 1-2 minggu untuk menemukan resource legitimate yang perlu di-whitelist
- β Gunakan layanan seperti Report URI (report-uri.com) untuk analisis yang lebih mudah
- β Filter noise dari reports β browser extension dan malware bisa menghasilkan false positives
- β Set up alerting untuk spike tiba-tiba dalam violation reports (indikasi serangan)
6. Implementasi CSP
Implementasi CSP yang baik memerlukan perencanaan dan testing yang cermat. Melompat langsung ke policy yang ketat bisa memecah fungsionalitas website. Berikut adalah panduan step-by-step untuk mengimplementasikan CSP secara bertahap.
Langkah 1: Audit Resource yang Digunakan
// Jalankan di browser console untuk mendeteksi semua resource
// yang dimuat oleh halaman
const resources = performance.getEntriesByType('resource');
const origins = new Set();
resources.forEach(r => {
try {
const url = new URL(r.name);
origins.add(url.origin);
console.log(`[${r.initiatorType}] ${r.name}`);
} catch(e) {
console.log(`[${r.initiatorType}] ${r.name} (inline)`);
}
});
console.log('\n=== Unique Origins ===');
origins.forEach(o => console.log(o));
// Output contoh:
// [script] https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.js
// [link] https://fonts.googleapis.com/css2?family=Inter
// [img] https://images.unsplash.com/photo-123
// [xmlhttprequest] https://api.example.com/v1/data
// === Unique Origins ===
// https://cdn.jsdelivr.net
// https://fonts.googleapis.com
// https://fonts.gstatic.com
// https://images.unsplash.com
// https://api.example.com
Langkah 2: Membuat Strict CSP (Modern Approach)
Pendekatan modern yang direkomendasikan oleh Google dan web security experts menggunakan kombinasi nonce + 'strict-dynamic' tanpa domain whitelist.
# Strict CSP Template (Recommended)
# Tidak perlu whitelist domain β gunakan nonce + strict-dynamic
Content-Security-Policy: \
default-src 'self'; \
script-src 'self' 'nonce-{SERVER_GENERATED_NONCE}' 'strict-dynamic'; \
style-src 'self' 'unsafe-inline'; \
img-src 'self' https: data:; \
font-src 'self' https://fonts.gstatic.com; \
connect-src 'self' https://api.example.com; \
object-src 'none'; \
base-uri 'self'; \
form-action 'self'; \
frame-ancestors 'none'; \
upgrade-insecure-requests
# Keuntungan pendekatan ini:
# β
Tidak perlu whitelist banyak domain
# β
Lebih aman β attacker tidak bisa bypass dengan domain baru
# β
strict-dynamic memudahkan integrasi dengan bundler
# β
object-src 'none' mencegah plugin berbahaya
# β
base-uri 'self' mencegah base tag injection
# β
form-action 'self' mencegah form hijacking
Langkah 3: Konfigurasi Framework Populer
# ============================================
# REACT (Create React App / Next.js)
# ============================================
# Next.js β next.config.js
const crypto = require('crypto');
module.exports = {
async headers() {
return [{
source: '/(.*)',
headers: [
{
key: 'Content-Security-Policy',
value: `
default-src 'self';
script-src 'self' 'nonce-NONCE_PLACEHOLDER' 'strict-dynamic';
style-src 'self' 'unsafe-inline';
img-src 'self' blob: data: https:;
font-src 'self';
connect-src 'self' https://api.example.com;
object-src 'none';
base-uri 'self';
`.replace(/\n/g, '')
}
]
}];
}
};
# ============================================
# VUE.JS (Nuxt.js)
# ============================================
# nuxt.config.js β menggunakan helmet middleware
export default {
serverMiddleware: [
helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'nonce-{dynamic}'"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", 'data:', 'https:'],
connectSrc: ["'self'", 'https://api.example.com'],
objectSrc: ["'none'"],
frameAncestors: ["'none'"]
}
}
})
]
}
# ============================================
# ANGULAR (Universal / Server-Side Rendering)
# ============================================
# Express middleware untuk Angular SSR
app.use((req, res, next) => {
const nonce = crypto.randomBytes(16).toString('base64');
res.locals.cspNonce = nonce;
helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", `'nonce-${nonce}'`, "'strict-dynamic'"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", 'data:', 'https:'],
connectSrc: ["'self'"],
objectSrc: ["'none'"]
}
}
})(req, res, next);
});
Langkah 4: Migrasi Bertahap
- Phase 1 β Monitor Only: Deploy dengan
Content-Security-Policy-Report-Onlydan report endpoint. Kumpulkan data selama 1-2 minggu. - Phase 2 β Whitelist Legitimate: Analisis reports, tambahkan resource legitimate ke policy. Pastikan tidak ada false positives.
- Phase 3 β Enforce: Switch dari Report-Only ke
Content-Security-Policy. Monitor violation reports untuk masalah yang terlewat. - Phase 4 β Tighten: Secara bertahap ketatkan policy β hapus
'unsafe-inline', migrasi ke nonce/hash, kurangi whitelist domain.
7. Best Practices dan Kesalahan Umum
Best Practices
- β
Gunakan
'nonce'+'strict-dynamic'daripada whitelist domain - β
Hindari
'unsafe-inline'untuk script-src (boleh untuk style-src) - β
Hindari
'unsafe-eval'β refactor kode yang menggunakan eval() - β
Selalu set
object-src 'none'untuk mencegah plugin berbahaya - β
Selalu set
base-uri 'self'untuk mencegah base tag injection - β
Gunakan
frame-ancestorssebagai pengganti X-Frame-Options - β Implementasikan reporting sebelum enforce
- β Generate nonce di server menggunakan CSPRNG (crypto.randomBytes)
- β
Gunakan
upgrade-insecure-requestsuntuk migrasi HTTPS - β Test policy di staging sebelum deploy ke production
Kesalahan Umum dalam Implementasi CSP
| Kesalahan | Mengapa Berbahaya | Solusi |
|---|---|---|
Menggunakan 'unsafe-inline' di script-src | Melemahkan perlindungan XSS β inline script berbahaya tetap bisa dieksekusi | Gunakan nonce atau hash |
| Whitelist terlalu banyak domain | Meningkatkan attack surface β attacker bisa memanfaatkan XSS di domain yang di-whitelist | Gunakan 'strict-dynamic' |
| Wildcard (*) di script-src | Mengizinkan script dari mana saja β CSP menjadi tidak berguna | Tentukan sumber spesifik |
| Nonce yang bisa diprediksi | Attacker bisa menebak nonce dan menyuntikkan script berbahaya | Gunakan CSPRNG (crypto.randomBytes) |
| Tanpa object-src 'none' | Plugin seperti Flash bisa dimanfaatkan untuk bypass CSP | Selalu set object-src 'none' |
| Menggunakan CDN yang bisa di-upload | Jika attacker bisa upload ke CDN yang di-whitelist, mereka bisa bypass CSP | Gunakan SRI (Subresource Integrity) |
| Tidak menguji di staging | Policy yang terlalu ketat bisa memecah fungsionalitas website | Selalu gunakan Report-Only dulu |
Tools untuk Testing CSP
| Tool | Fungsi | URL |
|---|---|---|
| CSP Evaluator | Mengevaluasi kekuatan policy CSP | csp-evaluator.withgoogle.com |
| Report URI | Layanan hosted untuk menerima dan menganalisis CSP reports | report-uri.com |
| Security Headers | Scan header keamanan website | securityheaders.com |
| observatory.mozilla.org | Analisis keamanan header oleh Mozilla | observatory.mozilla.org |
| CSP Scanner (Burp Suite) | Scanner CSP di Burp Suite | Burp Suite Extensions |
8. Quiz: Uji Pemahamanmu!
Setelah membaca tutorial di atas, jawablah 5 pertanyaan berikut untuk menguji pemahamanmu tentang Content Security Policy: