CORS की ग़लत कॉन्फ़िगरेशन (CWE-942): Origin लौटाने से डेटा कैसे लीक होता है और इसे कैसे ठीक करें
जो API हर Origin को उसी origin वाले Access-Control-Allow-Origin और Access-Control-Allow-Credentials: true के साथ जवाब देता है, वह हर वेबसाइट को विज़िटर की cookies के साथ अपने responses पढ़ने देता है। suffix जाँच, null origin और लौटाए गए origins same-origin policy को कैसे तोड़ते हैं, और exact-match allowlist जो इसे ठीक करती है, Express और cors के उदाहरण के साथ।
आसान भाषा में (ELI5)
बैंक का क्लर्क आपका बैलेंस उस व्यक्ति को पढ़कर सुना देता है जिसे आप भेजें, बशर्ते संदेशवाहक एक चिट्ठी दिखाए कि वह किसके लिए काम करता है और क्लर्क की सूची उस नाम को मंज़ूरी दे। एक क्लर्क का नियम है: 'जिस भी नाम के अंत में शर्मा आए, उसे मंज़ूर करो।' एक अजनबी 'वर्मा-शर्मा' की चिट्ठी के साथ संदेशवाहक भेजता है, क्लर्क मंज़ूर कर देता है, और आपका बैलेंस ऐसे व्यक्ति को सुनाया जाता है जिसे आपने कभी नहीं भेजा। Origin लौटाना इसका आलसी रूप है, जिसमें क्लर्क हर नाम को मंज़ूर कर देता है। उपाय नामों की एक छोटी, सटीक सूची है जिसका अक्षर-अक्षर मिलान हो।
इस पेज के मुख्य शब्द
- Same-origin policy
- browser किसी script को केवल उसके अपने origin (scheme, host और port) के responses पढ़ने देते हैं। CORS response headers वह तरीका हैं जिससे सर्वर जानबूझकर नामित origins के लिए यह नियम ढीला करता है।
- Credentials के साथ Access-Control-Allow-Origin
- अगर response
Access-Control-Allow-Originमें caller का origin लिखे औरAccess-Control-Allow-Credentials: trueभेजे, तो उस origin की script उन रिक्वेस्ट के responses पढ़ सकती है जिनमें यूज़र की cookies गई थीं। browser credentials के साथ*को ठुकराते हैं, इसीलिए ग़लत कॉन्फ़िगर किए सर्वर origin को ही लौटा देते हैं। - Origin लौटाना और suffix मिलान
Originheader को response में कॉपी करना हर साइट पर भरोसा करना है।endsWith('example.com')जैसी जाँच, substring मिलान या बिना anchor और बिना escape किए dots वाले regular expressions भीhttps://attacker-example.comको स्वीकार कर लेते हैं।- null origin
- sandbox किए iframes,
data:URLs और कुछ redirectsOrigin: nullभेजते हैं। कोई भी हमलावर इसे बना सकता है, इसलिएnullकी अनुमति देना सबको अनुमति देने जैसा है। - Vary: Origin
- जब
Access-Control-Allow-Originरिक्वेस्ट पर निर्भर हो, तो response मेंVary: Originहोना चाहिए ताकि साझा caches एक origin के CORS headers दूसरे को न दें।corspackage dynamic origins के लिए इसे जोड़ देता है।
हमले का चरण-दर-चरण प्रवाह
पीड़ित API की साइट पर लॉग-इन है
उसके browser में api.example.com की session cookie है जो cross-site रिक्वेस्ट पर भी भेजी जाती है।
वह हमलावर के domain का पेज खोलता है
एक script fetch('https://api.example.com/api/account', { credentials: 'include' }) बुलाती है। https://attacker-example.com का ऐसा पेज जो केवल console में response की लंबाई लिखता है, यह मामला दिखाता है।
API origin को मंज़ूर कर देता है
suffix जाँच attacker-example.com को स्वीकार कर लेती है, इसलिए response Access-Control-Allow-Origin में वही origin और साथ में Access-Control-Allow-Credentials: true भेजता है।
script निजी डेटा पढ़ लेती है
browser खाते का JSON हमलावर की script को दे देता है, जो उसे कहीं भी भेज सकती है। 2016 में James Kettle ने यह पैटर्न कई Bitcoin exchanges पर पाया, जहाँ इससे API keys और wallet डेटा उजागर होते थे।
सोर्स कोड: कमज़ोर बनाम सुरक्षित कार्यान्वयन
// server.js: example.com पर ख़त्म होने वाला हर origin और null cookies के साथ पढ़ सकते हैं
import express from 'express';
import cors from 'cors';
import { accountFor, requireSession } from './account.js';
const app = express();
// true लौटाने पर cors caller का Origin credentials के साथ लौटा देता है
app.use(cors({
origin: (origin, callback) => callback(null, !origin || origin === 'null' || origin.endsWith('example.com')),
credentials: true,
}));
// मंज़ूर origins के लिए पढ़ने योग्य निजी खाता डेटा
app.get('/api/account', requireSession, (req, res) => {
res.json(accountFor(req.session.userId));
});
// server.js: केवल दो सटीक origins
import express from 'express';
import cors from 'cors';
import { accountFor, requireSession } from './account.js';
const app = express();
// scheme और host सहित पूरे origins, सटीक तुलना
const ALLOWED_ORIGINS = new Set(['https://app.example.com', 'https://admin.example.com']);
// अनजान या ग़ायब Origin को कोई CORS header नहीं; cors Vary: Origin जोड़ता है
app.use(cors({
origin: (origin, callback) => callback(null, origin !== undefined && ALLOWED_ORIGINS.has(origin)),
credentials: true,
methods: ['GET', 'POST'],
maxAge: 600,
}));
// निजी डेटा कभी साझा caches में नहीं जाता
app.get('/api/account', requireSession, (req, res) => {
res.set('Cache-Control', 'no-store');
res.json(accountFor(req.session.userId));
});
इंजीनियरिंग और सिस्टम सुरक्षा चेकलिस्ट
- पूरे origins (scheme, host और port) की exact-match allowlist रखें और
Setlookup से तुलना करें;Originकभी न लौटाएँ और suffix, substring या बिना anchor वाली regex जाँच न करें। nullorigin की कभी अनुमति न दें, और जिस origin पर आपका नियंत्रण नहीं उसे credentials की अनुमति कभी न दें।- जब भी
Access-Control-Allow-Originहर रिक्वेस्ट के हिसाब से चुना जाए तोVary: Originभेजें, और निजी डेटा वाले responses परCache-Control: no-storeभेजें। - session cookies को
SameSite=LaxयाStrictपर सेट करें ताकि cross-site रिक्वेस्ट उन्हें न ले जाएँ। - CI में ऐसा टेस्ट जोड़ें जो authenticated routes पर
Origin: https://attacker.exampleऔरOrigin: nullभेजे औरAccess-Control-Allow-Originलौटने पर फ़ेल हो।