क्रॉस-साइट स्क्रिप्टिंग (XSS)

Cross-site scripting (XSS) एक ऐसा defect है जिसमें एक user का दिया हुआ text web page में इस तरह लिखा जाता है कि browser उसे text नहीं बल्कि HTML या JavaScript की तरह पढ़ता है। Browser यह फ़र्क नहीं कर सकता कि script developers ने लिखी है या किसी attacker ने comment field में type की है, इसलिए inject हुआ code victim के session के साथ चलता है: वह victim को दिखने वाली चीज़ें पढ़ सकता है, victim के नाम से action कर सकता है या page को फिर से बना सकता है। OWASP Top 10:2025 XSS को अपनी injection category का हिस्सा मानता है, यानी वही root cause जो पिछले lesson के SQL injection में था, बस यहाँ interpreter browser है।

उदाहरण: एक customer अपना display name qa7<b>x</b>qa7 रखता है। Profile page उसे ठीक वैसा ही दिखाता है जैसा type किया गया, क्योंकि template ने उसे encode किया। लेकिन support agents की queue में x bold दिखता है: उस page ने नाम को सीधे HTML में जोड़ दिया, इसलिए कोई भी customer display name में जो script डाले, वह queue खोलने वाले हर agent के browser में चल सकती है।

यह क्यों ज़रूरी है

Product के लिए XSS एक मामूली text field को entry point बना देता है: HttpOnly के बिना session cookie पढ़ी जा सकती है, victim के नाम से forms submit हो सकते हैं, असली page के ऊपर नकली login box बनाया जा सकता है, और admin screen पर stored XSS attacker को सबसे ज़्यादा privileged users सौंप देता है। Tester के लिए यह उन गिने-चुने security defects में से है जिन्हें साधारण functional testing भी पकड़ सकती है, क्योंकि user input दिखाने वाला हर feature इसका candidate है।

यह ऐसा defect भी है जिसे आप बिना attack tools और बिना नुकसान के खोज सकते हैं: field में type किया एक unique marker और page source पर एक नज़र इस सवाल का जवाब दे देती है कि "यह value text मानी जा रही है या markup?"। तीनों variants जानने से पता चलता है कि marker कहाँ type करना है और कहाँ ढूँढना है।

Reflected, stored और DOM-based XSS

हर XSS का आकार एक ही है: एक untrusted value किसी source से आती है और ऐसे sink तक पहुँचती है जो उसे code की तरह interpret करता है। Variants इस बात में अलग हैं कि value कहाँ इंतज़ार करती है और उसे markup में कौन बदलता है; OWASP Web Security Testing Guide इन्हें अलग-अलग टेस्ट करता है क्योंकि steps और exposed लोग अलग होते हैं।

Reflected XSS: value request में जाती है, आम तौर पर query parameter या form field में, और response उसे तुरंत वापस दिखा देता है, जैसे search page पर "0 results for …"। इसे सिर्फ़ भेजने वाला देखता है, इसलिए attacker को victim से वह request भिजवानी पड़ती है, आम तौर पर एक crafted link खुलवाकर। Stored XSS: value save होती है (review, ticket, profile field, file name) और बाद में दूसरे users को दिखाई जाती है, staff समेत। किसी link की ज़रूरत नहीं और हर देखने वाला exposed है, इसीलिए यह आम तौर पर सबसे severe होता है। DOM-based XSS: server का response साफ़ है, लेकिन page का अपना JavaScript location.search, location.hash, document.referrer या postMessage event जैसे source को पढ़कर innerHTML, document.write या eval जैसे sink में लिख देता है। URL में # के बाद का हिस्सा server को कभी नहीं भेजा जाता, इसलिए इस variant का server logs में कोई निशान नहीं हो सकता, और कोई server-side encoding इसे ठीक नहीं कर सकती।

Stored XSS: ग्राहक A का save किया probe एजेंट B के page के अंदर वापस आता है; output पर HTML encoding के साथ वही stored text literally दिखता है।
  • Output encoding के बिना

    • ग्राहक A आपका अपना test account
    • ऐप्लिकेशन server और database
    • एजेंट B staff session वाला browser
    1. 1 ग्राहक A से ऐप्लिकेशन तक: marker probe वाली review submit करें title=qa7<b>x</b>qa7
    2. 2 ऐप्लिकेशन आंतरिक रूप से: title को ठीक वैसे ही store करें जैसे आया storage defect नहीं है: database text रखता है
    3. 3 एजेंट B से ऐप्लिकेशन तक: review queue खोलें GET /agent/reviews
    4. 4 ऐप्लिकेशन से एजेंट B तक: stored title जोड़कर बनाया गया HTML जवाब <td>qa7<b>x</b>qa7</td> फ़ेल
    5. 5 एजेंट B आंतरिक रूप से: browser probe को markup की तरह parse करता है: x bold असली script यहाँ agent के session के साथ चल सकती है फ़ेल

    परिणाम: Stored XSS: कोई भी customer जो script लिखे, वह queue खोलने वाले हर agent के लिए चल सकती है। फ़ेल

  • Output encoding के साथ

    • ग्राहक A आपका अपना test account
    • ऐप्लिकेशन server और database
    • एजेंट B staff session वाला browser
    1. 1 ग्राहक A से ऐप्लिकेशन तक: marker probe वाली review submit करें title=qa7<b>x</b>qa7
    2. 2 ऐप्लिकेशन आंतरिक रूप से: title को ठीक वैसे ही store करें जैसे आया storage defect नहीं है: database text रखता है
    3. 3 एजेंट B से ऐप्लिकेशन तक: review queue खोलें GET /agent/reviews
    4. 4 ऐप्लिकेशन से एजेंट B तक: template page लिखते समय title को encode करता है जवाब <td>qa7&lt;b&gt;x&lt;/b&gt;qa7</td> पास
    5. 5 एजेंट B आंतरिक रूप से: browser characters को literally दिखाता है page का text qa7<b>x</b>qa7 पढ़ता है; कुछ भी markup नहीं बनता पास

    परिणाम: वही stored value harmless है क्योंकि वह server से text के रूप में निकलती है। पास

Reflected  - travels in the request, echoed at once
  GET /search?q=qa7<b>x</b>qa7
  <h2>0 results for qa7<b>x</b>qa7</h2>

Stored     - saved now, rendered to someone else later
  POST /reviews        title=qa7<b>x</b>qa7
  GET  /agent/reviews  (agent B, next day)
  <td>qa7<b>x</b>qa7</td>

DOM-based  - server response clean, the page script writes it
  https://shop.example/help#qa7<b>x</b>qa7   (the part after # never reaches the server)
  status.innerHTML = 'Showing: ' + decodeURIComponent(location.hash.slice(1));

Safe probes: value text है या markup?

XSS साबित करने के लिए script चलाने की ज़रूरत नहीं। Defect यह है कि "user input बिना encoding के page में लिखा जाता है", और एक दिखने वाला, harmless probe इसे साबित कर देता है। Probe को qa7 जैसे unique marker और HTML में मायने रखने वाले characters से बनाएँ: <, >, ", ' और &। Marker की मदद से आप rendered page और उसके HTML source (View Page Source, Ctrl+U) में हर वह जगह खोज सकते हैं जहाँ value पहुँची, उन जगहों समेत जिनकी उम्मीद नहीं थी: <title>, कोई attribute, कोई email, CSV export, कोई error message।

नतीजा source में पढ़ें, सिर्फ़ screen पर नहीं। qa7&lt;b&gt;x&lt;/b&gt;qa7 का मतलब है value HTML-encoded हुई और उस जगह safe है; tags के बीच qa7<b>x</b>qa7 का मतलब है markup स्वीकार हुआ; attribute में देखें कि आपके quote ने उसे बंद तो नहीं कर दिया। Elements panel source नहीं, DOM दिखाता है: वहाँ safe value एक text node होती है, जबकि स्वीकार हुआ markup अलग b element के रूप में दिखता है। Executing probe, जो marker दिखाने के सिवा कुछ नहीं करता, तभी इस्तेमाल करें जब team को सबूत चाहिए कि script चलती है, और सिर्फ़ test environment में, अपने accounts और written permission के साथ। Stored probes बाद में हटा दें: वे database में रह जाते हैं और हफ़्तों बाद colleagues को चौंकाते हैं।

  • Probe हर उस जगह डालें जहाँ user type या upload कर सकता है: forms, URL parameters, file names, imported files, वे API fields जो कोई page बाद में दिखाता है, और वे request headers जिन्हें application reflect करता है, जैसे User-Agent या Referer।
  • हर output देखें, सिर्फ़ input के बगल वाला नहीं: lists, detail pages, admin queues, notifications, previews, printed या PDF versions, और वह JSON जो page किसी <script> block में embed करता है।
  • DOM-based XSS के लिए probe को fragment (#…) में और उन parameters में डालें जिन्हें page का JavaScript पढ़ता है, और server response की बजाय DevTools में DOM देखें।
  • ऐसा system कभी probe न करें जो आपका नहीं है या जिसे टेस्ट करने की written permission आपके पास नहीं है: बिना अनुमति probing बिना script के भी ग़ैरक़ानूनी हो सकती है, और production में stored probe असली users को दिखता है।
Probe                                 What a hit means
qa7<b>x</b>qa7                        "x" in bold: HTML text context is not encoded
qa7"'>x                               your quote closes the attribute in the source
qa7<script>alert('qa7')</script>      a script parsed from the response executes
qa7<img src=x onerror=alert('qa7')>   runs via innerHTML too, where <script> does not

Executing probes: test environment, own accounts, written permission, then remove.

आज़माएँ: स्पेशल कैरेक्टर डेटासेट जनरेटर आज़माएँ: URL एन्कोडर

Defences: context के हिसाब से output encoding, sanitisation और CSP

मुख्य defence है context से मेल खाने वाली output encoding, जिसे input validation और output encoding वाले lesson में पेश किया गया था। XSS testing में अहम यह है कि वह आम तौर पर कहाँ नाकाम होती है। पहला, ग़लत context: HTML entities किसी <script> block, onclick जैसे event-handler attribute या style के अंदर value को नहीं बचातीं; OWASP Cross Site Scripting Prevention Cheat Sheet ऐसी जगहों को dangerous contexts कहती है, जहाँ अकेली encoding XSS को शायद न रोके, और सलाह देती है कि user values को code में लिखने की बजाय data के रूप में पास करें; और href में user के दिए link की scheme को भी https: जैसी allow-list से जाँचना होता है ताकि javascript: reject हो, क्योंकि encoding scheme को जस का तस छोड़ देती है। दूसरा, bypass हुए defaults: template engines और frameworks HTML text को अपने-आप encode करते हैं, इसलिए defects उनके escape hatches में छिपते हैं, जैसे dangerouslySetInnerHTML, v-html या सीधा innerHTML assignment। तीसरा, browser की तरफ़: DOM-based XSS में कोई server encoding शामिल नहीं होती, और fix है innerHTML की जगह textContent जैसा safe sink।

Input validation वहाँ मदद करती है जहाँ format सख़्त है (date, quantity) लेकिन encoding की जगह नहीं ले सकती: O’Brien नाम, <3 वाला message और A&B नाम का product सब जायज़ हैं। जब HTML खुद एक feature है, जैसे rich-text comment में, तो encoding उसे बिगाड़ देगी, और defence है allow-list sanitiser जो <b> और <a href> रखता है पर scripts और event handlers हटा देता है।

Content Security Policy (CSP) दूसरी परत है। Content-Security-Policy response header browser को बताता है कि कौन-सी scripts चल सकती हैं: script-src 'self' सिर्फ़ site के अपने origin की scripts allow करता है, 'nonce-…' या hash कुछ खास inline scripts allow करता है, और 'unsafe-inline' हर inline script और event handler को चलने देता है, जिससे यह सुरक्षा बंद हो जाती है (साथ में nonce या hash भी दिया हो तो आज के browsers इसे ignore करते हैं)। Strict policy के साथ inject हुआ <script> या onerror handler block होकर console में log होता है, जो encoding defect का नुकसान सीमित करता है पर उसे ठीक नहीं करता। Policy को Security Header Analyzer या HTTP Headers Parser से पढ़ें और script-src जाँचें, या वह न हो तो default-src। Content-Security-Policy-Report-Only violations report करता है और कुछ block नहीं करता; teams इससे policy को लागू करने से पहले आज़माती हैं।

Where the value lands       Defect seen in the source         Defence
HTML text    <td>...</td>   <td>qa7<b>x</b>qa7</td>           HTML entity encoding
Attribute    value="..."    value="qa7"'>x"                   entities, quoted attribute
Link         href="..."     href="javascript:..."             allow-list the scheme
DOM sink     innerHTML      <b> rendered from location.hash   el.textContent = value
Rich text    comment        an onerror= attribute survives    allow-list sanitiser

Strict:  Content-Security-Policy: script-src 'self' 'nonce-R4nd0m'; object-src 'none'
Weak:    Content-Security-Policy: script-src 'self' 'unsafe-inline' *

आज़माएँ: HTML एंटिटी एन्कोडर आज़माएँ: सिक्योरिटी हेडर एनालाइज़र आज़माएँ: HTTP हेडर पार्सर

Worked example: एक review जो support desk तक पहुँचती है

एक shop customers को product reviews लिखने देती है और नई reviews support agents को एक queue में दिखाती है। आप staging में दो accounts, customer A और agent B, से टेस्ट करते हैं, team की security checks वाली written permission के तहत। A के रूप में आप qa7<b>x</b>qa7 title और qa7"'>x body वाली review submit करते हैं। Product page title को literally दिखाता है और उसके source में qa7&lt;b&gt;x&lt;/b&gt;qa7 है: encoded। B के रूप में queue में x bold दिखता है और source में <td>qa7<b>x</b>qa7</td> है: staff तक पहुँचने वाला stored XSS। Review की body दोनों pages पर वैसी ही दिखती है जैसी type हुई। Search page /search?q=qa7%3Cb%3Ex%3C%2Fb%3Eqa7 पर "0 results for qa7x" देता है, x bold: reflected XSS। हर response में Content-Security-Policy: script-src 'self' 'unsafe-inline' है, इसलिए यह policy दोनों में से किसी भी जगह असली script को नहीं रोकती।

  • Finding 1, Critical: review titles agent queue में HTML के रूप में render होते हैं। Evidence: input, source line, दोनों roles, screenshot।
  • Finding 2, Major: search term results page पर HTML के रूप में echo होता है। Evidence: exact URL और source line।
  • Finding 3, Minor (hardening): CSP 'unsafe-inline' scripts allow करती है, इसलिए findings 1 और 2 को सीमित नहीं करेगी।
  • Fix के बाद confirmation testing: वही probes दोहराएँ और source में encoded रूप की उम्मीद करें, और जाँचें कि Fast & cheap जैसा जायज़ title अब भी वैसा ही दिखे जैसा type हुआ, Fast &amp; cheap की तरह नहीं।

आम गलतियाँ

  • सिर्फ़ उस field को टेस्ट करना जहाँ value डाली जाती है, न कि हर उस page, role और export को जो उसे बाद में दिखाता है: stored XSS output में रहता है।
  • हर जगह <script>alert(1)</script> जैसा एक ही generic payload इस्तेमाल करना: "script" शब्द block करने वाला filter उसे रोक देता है जबकि <b> probe अब भी render होता है, और missing encoding बनी रहती है।
  • Production में, shared environments में या ऐसे systems पर जिनकी written permission नहीं है, executing या stored probes चलाना, और stored probes छोड़ जाना।
  • Client-side validation, web application firewall से block हुई request या console में CSP violation को defect ठीक होने का सबूत मान लेना।
  • DOM-based XSS के लिए सिर्फ़ server response जाँचना: fragment server तक कभी नहीं पहुँचता, इसलिए browser में DOM देखें।
  • script-src पढ़े बिना CSP को "present" दर्ज करना: 'unsafe-inline' या wildcard host वाली policy XSS के ख़िलाफ़ बहुत कम काम करती है।

QA का नज़रिया

Team में XSS checks हर उस feature की functional testing का हिस्सा हैं जो user का दिया text दिखाता है, किसी अलग security phase का नहीं: हर form के negative tests में marker probe जोड़ें, और जब कोई story rich text, preview, export या नई admin screen लाए, तो refinement में पूछें कि value कैसे encode होगी और क्या template की default escaping bypass हो रही है। Developers से पूछें कि innerHTML, v-html या dangerouslySetInnerHTML कहाँ इस्तेमाल हैं और कौन-सा sanitiser लगा है; security team से पूछें कि CSP enforced है या report-only।

Finding को किसी भी defect की तरह report करें, security framing के साथ: exact input, वह page और role जहाँ वह markup के रूप में render हुआ, HTML source की line, environment, response headers और आपकी severity reasoning (कौन देखता है, किस session के साथ)। Working exploit attach न करें; exploitability security team confirm करती है। Fix को वही probes और & व quotes वाली एक जायज़ value से verify करें, और पूछें कि क्या वही template कहीं और भी इस्तेमाल है, क्योंकि encoding defect शायद ही कभी अकेला होता है।

अभ्यास

Feedback widget के लिए XSS tests plan करें

आपका काम

एक customer feedback feature ऐसी staging site पर deploy है जिसे टेस्ट करने की आपको अनुमति है; उसके pages और response header नीचे दिए हैं। हर page के लिए तय करें कि उसमें कौन-सा XSS variant हो सकता है, exact probe लिखें और बताएँ कि marker कहाँ ढूँढेंगे, secure result बताएँ, और CSP का आकलन करें।

सामग्री
Staging site you are authorised to test: https://feedback.staging.example

Form    POST /feedback      fields: name, message
Pages   /wall               every name and message, shown to all visitors
        /admin/queue        new messages, shown to moderators (role: admin)
        /thanks?name=...    "Thank you, <name>" after submitting
        /wall#<id>          page script:
                              document.getElementById('status').innerHTML =
                                'Showing: ' + decodeURIComponent(location.hash.slice(1));

Response header on every page:
  Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'
चरण
  1. हर page को reflected, stored या DOM-based candidate के रूप में classify करें और बताएँ कि कौन exposed होगा।
  2. Marker qa7 के साथ exact input या URL लिखें जो आप इस्तेमाल करेंगे, और वह output बताएँ जिसे आप जाँचेंगे।
  3. हर page का secure result बताएँ, जैसा वह HTML source या DOM में दिखना चाहिए।
  4. Content-Security-Policy की value पढ़ें और तय करें कि वह severity पर कैसे असर डालती है और report में क्या जाएगा।
मॉडल उत्तर दिखाएँ
मॉडल उत्तर

/wall और /admin/queue: stored candidates; /wall हर visitor को और /admin/queue moderators को expose करता है, जो ज़्यादा severe है। अपने account से दो entries submit करें, एक में दोनों fields में qa7<b>x</b>qa7 और दूसरी में दोनों fields में qa7"'>x, ताकि हर output probe हो, चाहे वह कोई भी field दिखाए; फिर दोनों pages visitor और moderator के रूप में खोलें। Secure result: source में qa7&lt;b&gt;x&lt;/b&gt;qa7 है, किसी attribute के अंदर पहुँचा quote &quot; के रूप में दिखता है, और page characters को literally दिखाता है। काम ख़त्म होने पर stored probes हटा दें।

/thanks?name=: reflected candidate, जो crafted link खोलने वाले किसी को भी expose करता है। /thanks?name=qa7%3Cb%3Ex%3C%2Fb%3Eqa7 (URL-encoded probe) खोलें और heading व <title> जाँचें। Secure result: source में encoded रूप, और Thank you, qa7<b>x</b>qa7 text की तरह दिखे।

/wall#…: DOM-based candidate, जो crafted link खोलने वाले किसी को भी expose करता है। /wall#qa7<b>x</b>qa7 खोलें और Elements panel में status element जाँचें; server को fragment कभी नहीं मिलता, इसलिए उसका response और logs कुछ साबित नहीं करते। Secure result: script textContent इस्तेमाल करती है (या innerHTML से पहले encode करती है) और status literally Showing: qa7<b>x</b>qa7 पढ़ता है।

CSP: script-src मौजूद है, इसलिए scripts पर default-src 'self' नहीं बल्कि वही लागू होता है, और script-src 'self' 'unsafe-inline' किसी भी inline script को चलने देता है, इसलिए इनमें से किसी भी page पर inject हुई script को सीमित नहीं करेगा। इसे अलग hardening finding के रूप में report करें। अगर probes defects की पुष्टि करें, तो stored cases को Critical classify करें, पहले /admin/queue क्योंकि moderators के privileges ज़्यादा हैं, और reflected व DOM-based cases को Major, हर एक के साथ input, location, source line और role।

अक्सर पूछे जाने वाले सवाल

क्या <script>alert(1)</script> safe test payload है?

Alert खुद harmless है, लेकिन stored alert page खोलने वाले हर user को रोकता है, और बिना permission टेस्ट करना, payload चाहे जो हो, ग़ैरक़ानूनी हो सकता है। qa7<b>x</b>qa7 जैसे markup probe से शुरू करें, जो वही missing encoding साबित करता है; executing probe सिर्फ़ test environment में, अपने accounts और permission के साथ इस्तेमाल करें, और बाद में हटा दें।

अगर input validate होता है, तो क्या XSS रुक जाता है?

भरोसे से नहीं। Validation साफ़ ग़लत values reject करती है और strict formats के लिए सही है, लेकिन names, messages और product titles में <, & और quotes जायज़ तौर पर आते हैं, और values APIs और imports से भी आती हैं। हर जगह काम करने वाला defence है output context के हिसाब से encoding, ठीक उस पल जब value page में लिखी जाती है।

CSP ने मेरा probe block कर दिया। क्या page secure है?

नहीं। Encoding defect अब भी मौजूद है और CSP एक mitigation है: policy बाद में कमज़ोर की जा सकती है, browsers और contexts अलग होते हैं, और report-only policy कुछ block नहीं करती। Encoding defect को primary finding के रूप में report करें और CSP को उस चीज़ के रूप में नोट करें जिसने असर सीमित किया।

संदर्भ

  • OWASP Cheat Sheet Series: Cross Site Scripting PreventionOWASP FoundationXSS से बचाव: हर context के अनुसार output encoding, उपयोगकर्ता के लिखे HTML का sanitization, सुरक्षित DOM sinks और framework सुरक्षा, और CSP केवल defence in depth के रूप में।
  • Cross Site Scripting (XSS)OWASP Foundationक्रॉस-साइट स्क्रिप्टिंग क्या है, इसके reflected / stored / DOM वेरिएंट और एन्कोडिंग इसे कैसे रोकती है।
  • OWASP Web Security Testing Guide (WSTG)OWASP Foundationवेब एप्लिकेशन और API की सुरक्षा कमज़ोरियों को परखने की संदर्भ कार्यप्रणाली, एक-एक टेस्ट के रूप में।
  • Content-Security-Policy — MDNMDN Web Docs (Mozilla)CSP हेडर के डायरेक्टिव जो तय करते हैं कि स्क्रिप्ट, स्टाइल और अन्य रिसोर्स कहाँ से लोड हो सकते हैं।

Mutqan tools से अभ्यास करें

XSS रोकने के लिए HTML के विशेष कैरेक्टर (<, >, &, कोट्स) को एंटिटी में एस्केप करें।

एन्कोडिंग

URL, क्वेरी स्ट्रिंग और फ़ॉर्म डेटा में सुरक्षित इस्तेमाल के लिए टेक्स्ट को percent-encode करें।

एन्कोडिंग

पेस्ट किए गए हेडर या किसी सार्वजनिक URL से HTTP सुरक्षा हेडर (CSP, HSTS, X-Frame-Options, Referrer-Policy, Permissions-Policy, COOP/CORP) को ग्रेड दें।

वैलिडेशन

कच्चे HTTP रिक्वेस्ट/रिस्पॉन्स हेडर को व्याख्या सहित एक संरचित तालिका में बदलें।

वेब

और गहराई से

इस topic में आपकी प्रगति

QA रोडमैप का हिस्सा