-
-
Save tzkmx/0ce5ef7963772db4dbc9ac7200bbfa82 to your computer and use it in GitHub Desktop.
Decrypting Laravel's session cookie with JavaScript and Cloudflare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
addEventListener('fetch', event => { | |
event.respondWith(handleRequest(event.request)) | |
}) | |
const string2buffer = string => { | |
let tempArray = new Uint8Array(string.length) | |
for(let i = string.length; i--) tempArray[i] = string.charCodeAt(i) | |
return tempArray.buffer | |
} | |
const buffer2string = buffer => { | |
let arr = new Uint8Array(buffer) | |
return String.fromCharCode.apply(String, arr); | |
} | |
const keyEncoded = 'LARAVEL_APP_KEY' | |
// Decrypt a Laravel cookie | |
async function decryptCookie(cookies) { | |
// Grab the encrypted cookie from the header | |
const matches = cookies.match(/laravel_session=([a-zA-Z0-9%=]+)/) | |
// URL decode it | |
const encrypted = decodeURIComponent(matches[1]) | |
// Decode base64, then parse JSON | |
const encryptedParsed = JSON.parse(atob(encrypted)) | |
// Decode our base64-encoded key from above | |
const keyDecoded = atob(keyEncoded) | |
// Create a key object | |
const key = await crypto.subtle.importKey("raw", string2buffer(keyDecoded), "AES-CBC", true, ["encrypt", "decrypt"]); | |
// Finally decrypt the session id | |
const decrypted = await crypto.subtle.decrypt({ name: "AES-CBC", iv: string2buffer(atob(encryptedParsed.iv)) }, key, string2buffer(atob(encryptedParsed.value))); | |
// Convert buffer to plain text and return | |
return buffer2string(decrypted) | |
} | |
/** | |
* Fetch and log a request | |
* @param {Request} request | |
*/ | |
async function handleRequest(request) { | |
const requestCookies = request.headers.get('cookie') | |
if (! requestCookies || (! requestCookies.includes(`laravel_session=`) && ! requestCookies.includes(`remember_web_`))) { | |
return new Response('Missing session ID', { status: 401 }) | |
} | |
// There is a Laravel session cookie, let's decode it to get the session ID | |
if (requestCookies.includes(`laravel_session=`)) { | |
let sessionId = await decryptCookie(requestCookies) | |
// First check in cache, if cached - return from cache | |
const cached = await Sessions.get(sessionId) | |
if (cached) return new Response(cached, { headers: { "Content-Type": "application/json" } }) | |
} | |
// Otherwise, send a request to the origin server | |
let response = await fetch(request) | |
const responseCookies = response.headers.get('cookie') | |
// If origin responded 200 and has a new session cookie | |
// Decode it and save the response | |
if (response.ok && responseCookies && responseCookies.includes(`laravel_session=`)) { | |
let sessionId = await decryptCookie(responseCookies) | |
Sessions.put(sessionId, response.body, {expiration_ttl: 86400 * 7}) | |
} | |
return response | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment