I denna artikel undersöker vi hur du aktiverar CORS (Cross-Origin Resource Sharing) med HTTPOnly-cookies för att skydda dina åtkomsttokens.
I dagens webbutvecklingslandskap distribueras ofta backend-servrar och frontend-klienter på separata domäner. Därför är det nödvändigt att servern aktiverar CORS för att tillåta webbläsare att kommunicera med servern.
För att uppnå bättre skalbarhet använder servrar ofta tillståndslös autentisering. Detta innebär att tokens lagras och hanteras på klientsidan snarare än på serversidan, som sessioner. För att öka säkerheten är det rekommenderat att lagra dessa tokens i HTTPOnly-cookies.
Varför blockeras begäranden över ursprung?
Låt oss anta att din frontend-applikation finns på https://app.adminvista.com. En skriptfil som laddas från https://app.adminvista.com kan endast skicka begäranden till resurser från samma ursprung.
När du försöker skicka en begäran till en annan domän, exempelvis https://api.adminvista.com, en annan port som https://app.adminvista.com:3000, eller ett annat protokoll som http://app.adminvista.com, kommer denna cross-origin-begäran att blockeras av webbläsaren.
Det är intressant att notera att samma begäran som blockeras av webbläsaren, kan skickas utan problem från en backend-server med hjälp av verktyg som curl eller Postman. Detta beror på säkerhetsskäl och syftar till att skydda användare från attacker, såsom CSRF (Cross-Site Request Forgery).
Låt oss illustrera med ett exempel: Tänk dig en användare som är inloggad på sitt PayPal-konto i sin webbläsare. Om vi kunde skicka en cross-origin-begäran till paypal.com från ett skript som körs på en annan domän, exempelvis malicious.com, utan att webbläsaren hindrade den, skulle det vara som att skicka en begäran från samma ursprung.
En angripare skulle enkelt kunna sprida sin skadliga sida https://malicious.com/transfer-money-to-attacker-account-from-user-paypal-account genom att omvandla den till en kort webbadress för att dölja den verkliga adressen. När en användare klickar på en sådan länk, skulle skriptet på malicious.com skicka en cross-origin-begäran till PayPal och överföra användarens pengar till angriparens konto. Användare som är inloggade på sitt PayPal-konto och som klickar på den skadliga länken skulle förlora sina pengar. På så sätt kan angripare stjäla pengar utan användarens vetskap.
Det är av detta skäl som webbläsare blockerar alla cross-origin-begäranden.
Vad är CORS (Cross-Origin Resource Sharing)?
CORS är en säkerhetsmekanism baserad på HTTP-headers. Servern använder den för att informera webbläsaren om att godkänna cross-origin-förfrågningar från betrodda domäner. Genom att aktivera CORS-headers på servern undviks att begäranden blockeras av webbläsaren.
Hur fungerar CORS?
Servern anger de betrodda domänerna i sin CORS-konfiguration. När en begäran skickas till servern, inkluderar svaret information i headern som indikerar om den begärande domänen är betrodd eller inte.
Det finns två typer av CORS-begäranden:
- Enkel begäran
- Preflight-begäran
Enkel begäran:
- Webbläsaren skickar en begäran till en domän, till exempel https://app.adminvista.com.
- Servern svarar med en respons som innehåller information om tillåtna metoder och ursprung.
- Webbläsaren kontrollerar att värdet i requestens origin-header (https://app.adminvista.com) matchar värdet i access-control-allow-origin (https://app.adminvista.com) eller att ett jokertecken används.
Om dessa värden inte matchar kommer webbläsaren att ge ett CORS-fel.
- Preflight-begäran:
- Beroende på parametrar i cross-origin-begäran, såsom metoder (PUT, DELETE), anpassade headers eller innehållstyper, skickar webbläsaren en preflight-OPTIONS-begäran. Denna kontroll avgör om den faktiska begäran är säker att skicka eller inte.
När webbläsaren mottar ett godkännande (statuskod 204, vilket betyder inget innehåll), kommer den att undersöka parametrarna i åtkomstkontrollen för den faktiska begäran. Om dessa parametrar är tillåtna, skickas den faktiska cross-origin-begäran och tas emot.
Om access-control-allow-origin är satt till *, tillåts alla ursprung, vilket är osäkert om du inte har ett specifikt behov av det.
Hur aktiverar man CORS?
För att aktivera CORS för valfri domän, aktivera CORS-headers för att tillåta ursprung, metoder, anpassade headers och autentiseringsuppgifter.
- Webbläsaren tolkar CORS-headern från servern och tillåter faktiska begäranden från klienten endast efter att ha verifierat parametrarna.
- `Access-Control-Allow-Origin`: Används för att ange specifika domäner (https://app.geekflate.com, https://lab.adminvista.com) eller ett jokertecken (`*`).
- `Access-Control-Allow-Methods`: Används för att ange tillåtna HTTP-metoder (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS). Ange endast de metoder som behövs.
- `Access-Control-Allow-Headers`: Används för att specificera de headers som är tillåtna (t.ex. `authorization`, `csrf-token`).
- `Access-Control-Allow-Credentials`: Ett booleskt värde som anger om cross-origin-referenser (t.ex. cookies, autentiseringshuvuden) ska tillåtas.
`Access-Control-Max-Age`: Anger hur länge webbläsaren ska cachelagra resultatet av en preflight-förfrågan.
`Access-Control-Expose-Headers`: Används för att specificera vilka headers som är tillgängliga för skript på klientsidan.
För att aktivera CORS i Apache och Nginx, följ deras respektive dokumentation.
const express = require('express'); const app = express() app.get('/users', function (req, res, next) { res.json({msg: 'user get'}) }); app.post('/users', function (req, res, next) { res.json({msg: 'user create'}) }); app.put('/users', function (req, res, next) { res.json({msg: 'User update'}) }); app.listen(80, function () { console.log('CORS-enabled web server listening on port 80') })
Aktivera CORS i ExpressJS
Låt oss titta på ett exempel på en ExpressJS-app utan CORS:
npm install cors
I exemplet ovan har vi skapat API-endpoints för användare med POST, PUT och GET metoder, men inte DELETE.
För att enkelt aktivera CORS i ExpressJS, kan du installera cors-modulen:
app.use(cors({ origin: '*' }));
Access-Control-Allow-Origin
app.use(cors({ origin: 'https://app.adminvista.com' }));
Aktiverar CORS för alla domäner
app.use(cors({ origin: [ 'https://app.geekflare.com', 'https://lab.geekflare.com' ] }));
Aktiverar CORS för en enskild domän
För att tillåta CORS för ursprung som https://app.adminvista.com och https://lab.adminvista.com:
app.use(cors({ origin: [ 'https://app.geekflare.com', 'https://lab.geekflare.com' ], methods: ['GET', 'PUT', 'POST'] }));
Access-Control-Allow-Methods
För att aktivera CORS för alla metoder, kan du utelämna detta alternativ i ExpressJS CORS-modulen. Men för att aktivera specifika metoder (GET, POST, PUT):
app.use(cors({ origin: [ 'https://app.geekflare.com', 'https://lab.geekflare.com' ], methods: ['GET', 'PUT', 'POST'], allowedHeaders: ['Content-Type', 'Authorization', 'x-csrf-token'] }));
Access-Control-Allow-Headers
Används för att tillåta andra headers än standard att skickas med faktiska förfrågningar.
app.use(cors({ origin: [ 'https://app.geekflare.com', 'https://lab.geekflare.com' ], methods: ['GET', 'PUT', 'POST'], allowedHeaders: ['Content-Type', 'Authorization', 'x-csrf-token'], credentials: true }));
Access-Control-Allow-Redigeringar
Lämna detta utelämnat om du inte vill ange för webbläsaren att tillåta referenser i begäran, även om `withCredentials` är satt till `true`.
app.use(cors({ origin: [ 'https://app.geekflare.com', 'https://lab.geekflare.com' ], methods: ['GET', 'PUT', 'POST'], allowedHeaders: ['Content-Type', 'Authorization', 'x-csrf-token'], credentials: true, maxAge: 600 }));
Access-Control-Max-Age
Används för att instruera webbläsaren att cacha preflight-responsen under en viss tid i sekunder. Utelämna detta om du inte vill cacha responsen.
app.use(cors({ origin: [ 'https://app.geekflare.com', 'https://lab.geekflare.com' ], methods: ['GET', 'PUT', 'POST'], allowedHeaders: ['Content-Type', 'Authorization', 'x-csrf-token'], credentials: true, maxAge: 600, exposedHeaders: ['Content-Range', 'X-Content-Range'] }));
Det cachade preflight-svaret kommer att vara tillgängligt i 10 minuter i webbläsaren.
app.use(cors({ origin: [ 'https://app.geekflare.com', 'https://lab.geekflare.com' ], methods: ['GET', 'PUT', 'POST'], allowedHeaders: ['Content-Type', 'Authorization', 'x-csrf-token'], credentials: true, maxAge: 600, exposedHeaders: ['*', 'Authorization'] }));
Access-Control-Expose-Headers
Om du sätter ett jokertecken i `exposedHeaders` kommer det inte att exponera auktoriseringsheadern. Därför måste du explicit ange den som nedan:
Ovanstående konfiguration kommer att exponera både alla rubriker och auktoriseringsrubriker.
- Vad är en HTTP-cookie?
- En cookie är en liten datamängd som servern skickar till klientens webbläsare. Webbläsaren skickar sedan tillbaka alla cookies som är relaterade till samma domän vid varje förfrågan.
- Cookies har attribut som kan definieras för att styra deras beteende.
- `Namn`: Cookiens namn.
- `Värde`: Den data som lagras i cookien.
- `Domän`: Cookies skickas endast till den specificerade domänen.
- `Sökväg`: Cookies skickas endast för förfrågningar vars URL-prefix matchar sökvägen. Exempelvis skickas cookies med sökvägen `admin/` inte till `https://adminvista.com/expire/` utan till `https://adminvista.com/admin/`.
- `Max-Age/Expires`: Anger när cookien ska upphöra att gälla.
- `HTTPOnly`: Om satt till `true` kan endast backend-servern komma åt cookien, inte skript på klientsidan.
- Säker: Cookien skickas endast över en SSL/TLS-domän om detta attribut är satt till `true`.
- `sameSite`: Används för att kontrollera cookies som skickas med förfrågningar mellan olika webbplatser. Accepterar värdena `Strict`, `Lax`, eller `None`. För mer information, se MDN. För att använda `sameSite=None` måste `secure` vara satt till `true`.
Varför HTTPOnly-cookies för tokens?
Att lagra åtkomsttokens som skickas från servern i klientlagring såsom `localStorage`, `IndexedDB`, eller i cookies utan HTTPOnly-flaggan är mer utsatt för XSS-attacker. Om någon av dina sidor är känslig för en XSS-attack, kan angripare använda användarnas tokens som lagrats i webbläsaren.
HTTPOnly-cookies sätts och hämtas endast av servern/backend, inte på klientsidan.
- Skript på klientsidan kan inte komma åt HTTPOnly-cookies. HTTPOnly-cookies är därför inte lika utsatta för XSS-attacker och är säkrare, eftersom de endast är tillgängliga för servern.
- Aktivera HTTPOnly-cookies i en CORS-aktiverad backend
- För att aktivera cookies i CORS krävs följande inställningar i applikationen/servern:
- `Access-Control-Allow-Credentials`-headern måste vara satt till `true`.
`Access-Control-Allow-Origin` och `Access-Control-Allow-Headers` får inte vara ett jokertecken.
const express = require('express'); const app = express(); const cors = require('cors'); app.use(cors({ origin: [ 'https://app.geekflare.com', 'https://lab.geekflare.com' ], methods: ['GET', 'PUT', 'POST'], allowedHeaders: ['Content-Type', 'Authorization', 'x-csrf-token'], credentials: true, maxAge: 600, exposedHeaders: ['*', 'Authorization'] })); app.post('/login', function (req, res, next) { res.cookie('access_token', access_token, { expires: new Date(Date.now() + (3600 * 1000 * 24 * 180 * 1)), //sekund min tim dag år secure: true, // sätt till true om du använder https eller samesite är none httpOnly: true, // endast backend sameSite: 'none' // sätt till none för cross-request }); res.json({ msg: 'Login Successfully', access_token }); }); app.listen(80, function () { console.log('CORS-enabled web server listening on port 80') });
Cookiens `sameSite`-attribut måste vara inställt på `None`.
För att aktivera sameSite till None, måste secure-attributet vara satt till true: Aktivera backend med SSL/TLS-certifikat för att fungera på en domän.
Låt oss se ett exempel på kod som sätter en åtkomsttoken i en HTTPOnly-cookie efter att ha kontrollerat inloggningsuppgifterna.
Du kan konfigurera CORS och HTTPOnly-cookies genom att följa stegen ovan i ditt backend-språk och din webbserver.
var xhr = new XMLHttpRequest(); xhr.open('GET', 'http://api.adminvista.com/user', true); xhr.withCredentials = true; xhr.send(null);
Du kan följa dokumentationen för Apache och Nginx för att aktivera CORS genom att följa ovanstående steg.
fetch('http://api.adminvista.com/user', { credentials: 'include' });
med inloggningsuppgifter för cross-origin-begäran
$.ajax({ url: 'http://api.adminvista.com/user', xhrFields: { withCredentials: true } });
Inloggningsuppgifter (cookie, auktorisering) skickas med som standard för förfrågningar som görs från samma ursprung. Vid cross-origin-förfrågningar måste vi explicit sätta `withCredentials` till `true`.
axios.defaults.withCredentials = true
XMLHttpRequest API
Hämta API
JQuery AjaxAxios. Sammanfattningsvis hoppas jag att den här artikeln hjälper dig att förstå hur CORS fungerar och hur du aktiverar CORS för cross-origin-förfrågningar på servern. Jag hoppas att det även förklarar varför det är säkert att lagra cookies i HTTPOnly och hur autentiseringsuppgifter används i klienterna för förfrågningar med flera ursprung.