diff --git a/functions/api/verify.js b/functions/api/verify.js deleted file mode 100644 index a525612..0000000 --- a/functions/api/verify.js +++ /dev/null @@ -1,20 +0,0 @@ -export async function onRequestPost(context) { - const { request } = context; - const body = await request.json(); - const { levelId, flag } = body; - - // Logika validasi sederhana - // Dalam produksi, gunakan database atau secret - const expectedFlag = `CTF{SECRET_${levelId}}`; - - if (flag === expectedFlag) { - return new Response(JSON.stringify({ success: true, message: 'Flag benar!' }), { - headers: { 'Content-Type': 'application/json' }, - }); - } - - return new Response(JSON.stringify({ success: false, message: 'Flag salah!' }), { - status: 400, - headers: { 'Content-Type': 'application/json' }, - }); -} diff --git a/src/pages/api/get-flag.js b/src/pages/api/get-flag.js new file mode 100644 index 0000000..18f2d2b --- /dev/null +++ b/src/pages/api/get-flag.js @@ -0,0 +1,37 @@ +export const prerender = false; + +export async function POST({ request, locals }) { + const body = await request.json(); + const { username } = body; + + if (!username) { + return new Response(JSON.stringify({ error: 'Username is required' }), { status: 400 }); + } + + const normalizedUsername = username.trim().toLowerCase(); + const secret = locals.runtime.env.FLAG_SECRET; + + if (!secret) { + return new Response(JSON.stringify({ error: 'Server configuration error' }), { status: 500 }); + } + + const encoder = new TextEncoder(); + const keyData = encoder.encode(secret); + const messageData = encoder.encode(normalizedUsername); + + const cryptoKey = await crypto.subtle.importKey( + 'raw', + keyData, + { name: 'HMAC', hash: 'SHA-256' }, + false, + ['sign'] + ); + + const signature = await crypto.subtle.sign('HMAC', cryptoKey, messageData); + const hashArray = Array.from(new Uint8Array(signature)); + const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); + + return new Response(JSON.stringify({ flag: `CTF{${hashHex}}` }), { + headers: { 'Content-Type': 'application/json' }, + }); +} diff --git a/src/pages/api/verify-flag.js b/src/pages/api/verify-flag.js new file mode 100644 index 0000000..f874f64 --- /dev/null +++ b/src/pages/api/verify-flag.js @@ -0,0 +1,46 @@ +export const prerender = false; + +export async function POST({ request, locals }) { + const body = await request.json(); + const { username, flag } = body; + + if (!username || !flag) { + return new Response(JSON.stringify({ error: 'Username and flag are required' }), { status: 400 }); + } + + const normalizedUsername = username.trim().toLowerCase(); + const secret = locals.runtime.env.FLAG_SECRET; + + if (!secret) { + return new Response(JSON.stringify({ error: 'Server configuration error' }), { status: 500 }); + } + + const encoder = new TextEncoder(); + const keyData = encoder.encode(secret); + const messageData = encoder.encode(normalizedUsername); + + const cryptoKey = await crypto.subtle.importKey( + 'raw', + keyData, + { name: 'HMAC', hash: 'SHA-256' }, + false, + ['sign'] + ); + + const signature = await crypto.subtle.sign('HMAC', cryptoKey, messageData); + const hashArray = Array.from(new Uint8Array(signature)); + const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); + + const expectedFlag = `CTF{${hashHex}}`; + + if (flag === expectedFlag) { + return new Response(JSON.stringify({ success: true, message: 'Flag valid!' }), { + headers: { 'Content-Type': 'application/json' }, + }); + } + + return new Response(JSON.stringify({ success: false, message: 'Flag invalid!' }), { + status: 400, + headers: { 'Content-Type': 'application/json' }, + }); +}