ฉันขอให้เบราว์เซอร์ไม่เรียกใช้ <script> s ภายในองค์ประกอบได้หรือไม่?


84

เป็นไปได้หรือไม่ที่จะบอกให้เบราว์เซอร์ไม่เรียกใช้ JavaScript จากบางส่วนของเอกสาร HTML

ชอบ:

<div script="false"> ...

อาจเป็นประโยชน์ในฐานะคุณลักษณะด้านความปลอดภัยเพิ่มเติม สคริปต์ทั้งหมดที่ฉันต้องการถูกโหลดในส่วนเฉพาะของเอกสาร ไม่ควรมีสคริปต์ในส่วนอื่น ๆ ของเอกสารและหากมีไม่ควรเรียกใช้


1
ไม่ใช่ว่าฉันรู้ นี่เป็นความคิดเห็นมากกว่าคำตอบเพราะฉันไม่สามารถพูดได้ 100%
freefaller

4
@ chiastic-security คุณสามารถข้าม DOM ได้หลังจากโหลด DOM แล้วเท่านั้น โดยจุด JS ใด ๆ ในบล็อกนั้นจะดำเนินการ
COWLS

8
สิ่งที่ใกล้เคียงที่สุดที่ฉันคิดได้คือนโยบายความปลอดภัยของเนื้อหาซึ่งคุณสามารถ จำกัด สคริปต์ตามต้นกำเนิด (บางทีนั่นอาจเป็นสิ่งที่คุณต้องการ) เช่นการระบุว่าscript-src:"self"คุณอนุญาตให้สคริปต์จากโดเมนของคุณทำงานในเพจเท่านั้น หากคุณสนใจอ่านบทความนี้จากไมค์เวสต์เกี่ยวกับซีเอสพี
Christoph

1
@ chiastic-security คุณวางแผนที่จะผ่อนคลายข้อ จำกัด ที่ระบุในส่วนหัวได้อย่างไรหลังจากส่งส่วนหัวไปแล้ว อย่างไรก็ตามเนื่องจาก CSP ปิดใช้งานสคริปต์แบบอินไลน์ตามค่าเริ่มต้นและสคริปต์ระยะไกลจะไม่โหลดเว้นแต่จะอยู่ในรายการที่อนุญาตพิเศษเหตุใดคุณจึงต้องรวมเข้ากับสิ่งอื่น CSP น่าจะเป็นทางออกที่ดีที่สุดของ OP ฉันหวังว่าจะมีคนออกจากคำตอบ CSP
Dagg Nabbit

6
หากคุณกังวลว่าจะมีคนฉีดบล็อกโดยพลการลงใน HTML ของคุณวิธีแก้ปัญหาที่คุณเสนอจะป้องกันไม่ให้พวกเขาฉีด</div>เพื่อปิดองค์ประกอบ DOM นี้จากนั้นเริ่มต้นใหม่<div>ซึ่งจะเป็นพี่น้องของสคริปต์ที่ไม่ทำงาน
Damien_The_Unbeliever

คำตอบ:


92

ใช่คุณสามารถ :-) คำตอบคือ: นโยบายความปลอดภัยของเนื้อหา (CSP)

เบราว์เซอร์ที่ทันสมัยส่วนใหญ่รองรับแฟล็กนี้ซึ่งจะบอกให้เบราว์เซอร์โหลดโค้ด JavaScript จากไฟล์ภายนอกที่เชื่อถือได้เท่านั้นและไม่อนุญาตให้ใช้โค้ด JavaScript ภายในทั้งหมด! ข้อเสียเพียงอย่างเดียวคือคุณไม่สามารถใช้ JavaScript แบบอินไลน์ใด ๆ ในทั้งหน้าของคุณได้ (ไม่ใช่เฉพาะในหน้าเดียว<div>) แม้ว่าอาจมีวิธีแก้ปัญหาโดยการรวม div แบบไดนามิกจากไฟล์ภายนอกที่มีนโยบายความปลอดภัยอื่น แต่ฉันไม่แน่ใจเกี่ยวกับเรื่องนั้น

แต่ถ้าคุณสามารถเปลี่ยนไซต์ของคุณเพื่อโหลด JavaScript ทั้งหมดจากไฟล์ JavaScript ภายนอกคุณสามารถปิดใช้งาน JavaScript แบบอินไลน์พร้อมกับส่วนหัวนี้ได้!

นี่คือบทช่วยสอนที่ดีพร้อมตัวอย่าง: HTML5Rocks Tutorial

หากคุณสามารถกำหนดค่าเซิร์ฟเวอร์เพื่อส่งค่าสถานะ HTTP-Header นี้โลกจะน่าอยู่ขึ้น!


2
+1 มันเจ๋งมากฉันไม่รู้เลยว่ามีอยู่จริง! (หมายเหตุหนึ่งลิงค์วิกิของคุณคือเวอร์ชันภาษาเยอรมันโดยตรง) นี่คือบทสรุปเกี่ยวกับการสนับสนุนเบราว์เซอร์: caniuse.com/#feat=contentsecuritypolicy
BrianH

4
โปรดทราบว่าแม้ว่าคุณจะทำเช่นนี้ แต่การอนุญาตให้ผู้ใช้ป้อนข้อมูลโดยไม่ใช้ Escape บนเพจก็ยังเป็นความคิดที่ไม่ดี โดยพื้นฐานแล้วจะช่วยให้ผู้ใช้สามารถเปลี่ยนหน้าเว็บให้ดูตามที่ต้องการได้ แม้ว่าจะมีการตั้งค่านโยบายความปลอดภัยของเนื้อหา (CSP) ทั้งหมดไว้ที่ค่าสูงสุด (ไม่อนุญาตให้ใช้สคริปต์แบบอินไลน์รูปแบบ ฯลฯ ) ผู้ใช้ยังคงสามารถดำเนินการโจมตีการปลอมแปลงคำขอข้ามไซต์ (CSRF) โดยใช้ภาพ srcs สำหรับคำขอ GET หรือหลอกให้ผู้ใช้เข้ามา คลิกปุ่มส่งแบบฟอร์มสำหรับคำขอ POST
Ajedi32

@ Ajedi32 แน่นอนว่าคุณควรทำความสะอาดอินพุตของผู้ใช้เสมอ แต่ CSP ยังสามารถกำหนดนโยบายสำหรับคำขอ GET เช่นรูปภาพหรือ css ได้ซึ่งจะไม่เพียง แต่บล็อก แต่ยังแจ้งให้เซิร์ฟเวอร์ของคุณทราบอีกด้วย!
Falco

1
@ Falco ฉันอ่านเอกสารและความเข้าใจของฉันคือคุณสามารถ จำกัด คำขอเหล่านั้นเฉพาะโดเมนที่กำหนดเท่านั้นไม่ใช่ชุดของหน้าย่อยเฉพาะบนโดเมน สันนิษฐานว่าโดเมนที่คุณตั้งไว้จะเป็นโดเมนเดียวกับไซต์ของคุณซึ่งหมายความว่าคุณจะยังคงเปิดรับการโจมตี CSRF
Ajedi32

3
@Falco ใช่นั่นคือสิ่งที่ StackExchange ทำกับฟีเจอร์ Stack Snippets ใหม่: blog.stackoverflow.com/2014/09/…หากคุณทำความสะอาดอินพุตอย่างถูกต้องโดเมนแยกก็ไม่จำเป็น
Ajedi32

13

คุณสามารถบล็อก JavaScript ที่โหลดโดย<script>ใช้beforescriptexecuteเหตุการณ์:

<script>
  // Run this as early as possible, it isn't retroactive
  window.addEventListener('beforescriptexecute', function(e) {
    var el = e.target;
    while(el = el.parentElement)
      if(el.hasAttribute('data-no-js'))
        return e.preventDefault(); // Block script
  }, true);
</script>

<script>console.log('Allowed. Console is expected to show this');</script>
<div data-no-js>
  <script>console.log('Blocked. Console is expected to NOT show this');</script>
</div>

โปรดทราบว่าbeforescriptexecuteถูกกำหนดไว้ใน HTML 5.0 แต่ถูกลบออกใน HTML 5.1 Firefox เป็นเบราว์เซอร์หลักเพียงตัวเดียวที่ใช้งานได้

ในกรณีที่คุณกำลังแทรกกลุ่ม HTML ที่ไม่น่าเชื่อถือในหน้าของคุณโปรดทราบว่าสคริปต์การบล็อกภายในองค์ประกอบนั้นจะไม่ให้ความปลอดภัยมากขึ้นเนื่องจาก HTML ที่ไม่น่าเชื่อถือสามารถปิดองค์ประกอบที่เป็นแซนด์บ็อกซ์ได้ดังนั้นสคริปต์จะถูกวางไว้ภายนอกและเรียกใช้

<img onerror="javascript:alert('foo')" src="//" />และสิ่งนี้จะไม่ปิดกั้นสิ่งที่ต้องการ


ซอดูเหมือนจะไม่ได้ผลเท่าที่ควร ฉันควรจะมองไม่เห็นส่วนที่ "ถูกบล็อก" ใช่ไหม?
Salman A

@SalmanA แน่นอน. อาจเป็นไปได้ว่าเบราว์เซอร์ของคุณไม่รองรับbeforescriptexecuteเหตุการณ์ ทำงานบน Firefox
Oriol

อาจเป็นเพราะมันไม่ทำงานใน Chrome โดยมีตัวอย่างข้อมูลตามที่ให้มา แต่ฉันเห็นว่าคุณเพิ่งแปลงเป็นข้อมูลโค้ด :-)
Mark Hurd

beforescriptexecuteดูเหมือนว่าจะไม่รองรับและเบราว์เซอร์หลัก ๆ ส่วนใหญ่จะไม่รองรับ developer.mozilla.org/en-US/docs/Web/Events/beforescriptexecute
Matt Pennington

8

คำถามที่น่าสนใจฉันไม่คิดว่าจะเป็นไปได้ แต่ถึงอย่างนั้นก็ฟังดูเหมือนจะแฮ็ค

หากเนื้อหาของ div นั้นไม่น่าเชื่อถือคุณจะต้องหลบหนีข้อมูลบนฝั่งเซิร์ฟเวอร์ก่อนที่จะส่งในการตอบสนอง HTTP และแสดงผลในเบราว์เซอร์

หากคุณต้องการเพียงลบ<script>แท็กและอนุญาตแท็ก html อื่น ๆ ให้ดึงแท็กออกจากเนื้อหาและปล่อยส่วนที่เหลือ

มองหาการป้องกัน XSS

https://www.owasp.org/index.php/XSS_%28Cross_Site_Scripting%29_Prevention_Cheat_Sheet


7

JavaScript ถูกเรียกใช้งานแบบ "อินไลน์" กล่าวคือตามลำดับที่ปรากฏใน DOM (หากไม่เป็นเช่นนั้นคุณจะไม่แน่ใจว่าตัวแปรบางตัวที่กำหนดไว้ในสคริปต์อื่นสามารถมองเห็นได้เมื่อคุณใช้เป็นครั้งแรก ).

นั่นหมายความว่าในทางทฤษฎีคุณสามารถมีสคริปต์ที่จุดเริ่มต้นของเพจ (เช่น<script>องค์ประกอบแรก) ซึ่งดูผ่าน DOM และลบ<script>องค์ประกอบและตัวจัดการเหตุการณ์ทั้งหมดภายใน<div>ไฟล์.

แต่ในความเป็นจริงนั้นซับซ้อนกว่า: DOM และการโหลดสคริปต์เกิดขึ้นแบบอะซิงโครนัส ซึ่งหมายความว่าเบราว์เซอร์รับประกันเฉพาะว่าสคริปต์สามารถมองเห็นส่วนของ DOM ซึ่งอยู่ก่อนหน้านั้นได้ (เช่นส่วนหัวจนถึงในตัวอย่างของเรา) ไม่มีการค้ำประกันใด ๆ ที่นอกเหนือไปจากนี้ (เกี่ยวข้องกับdocument.write()) ดังนั้นคุณอาจเห็นแท็กสคริปต์ถัดไปหรืออาจจะไม่เห็น

คุณสามารถล็อกonloadเหตุการณ์ของเอกสาร - ซึ่งจะทำให้แน่ใจว่าคุณมี DOM ทั้งหมด - แต่ในเวลานั้นโค้ดที่เป็นอันตรายอาจทำงานได้แล้ว สิ่งที่แย่ลงเมื่อสคริปต์อื่น ๆ จัดการกับ DOM โดยเพิ่มสคริปต์ที่นั่น ดังนั้นคุณจะต้องตรวจสอบทุกการเปลี่ยนแปลงของ DOM ด้วย

โซลูชัน @cowls (การกรองบนเซิร์ฟเวอร์) จึงเป็นทางออกเดียวที่สามารถทำได้ในทุกสถานการณ์


1

หากคุณต้องการแสดงโค้ด JavaScript ในเบราว์เซอร์ของคุณ:

การใช้ JavaScript และ HTML คุณจะต้องใช้เอนทิตี HTMLเพื่อแสดงโค้ด JavaScript และหลีกเลี่ยงการเรียกใช้โค้ดนี้ คุณจะพบรายการเอนทิตี HTML ได้ที่นี่:

หากคุณใช้ภาษาสคริปต์ฝั่งเซิร์ฟเวอร์ (PHP, ASP.NET ฯลฯ ) ส่วนใหญ่อาจมีฟังก์ชันที่จะหลีกเลี่ยงสตริงและแปลงอักขระพิเศษเป็นเอนทิตี HTML ใน PHP คุณจะใช้ "htmlspecialchars ()" หรือ "htmlentities ()" ส่วนหลังครอบคลุมอักขระ HTML ทั้งหมด

หากคุณต้องการแสดงโค้ด JavaScript ของคุณในทางที่ดีให้ลองใช้เครื่องมือเน้นรหัสตัวใดตัวหนึ่ง:


1

ฉันมีทฤษฎี:

  • ห่อส่วนเฉพาะของเอกสารไว้ในnoscriptแท็ก
  • ใช้ฟังก์ชัน DOM เพื่อละทิ้งscriptแท็กทั้งหมดภายในnoscriptแท็กจากนั้นแกะเนื้อหาออก

ตัวอย่างการพิสูจน์แนวคิด:

window.onload = function() {
    var noscripts = /* _live_ list */ document.getElementsByTagName("noscript"),
        memorydiv = document.createElement("div"),
        scripts = /* _live_ list */ memorydiv.getElementsByTagName("script"),
        i,
        j;
    for (i = noscripts.length - 1; i >= 0; --i) {
        memorydiv.innerHTML = noscripts[i].textContent || noscripts[i].innerText;
        for (j = scripts.length - 1; j >= 0; --j) {
            memorydiv.removeChild(scripts[j]);
        }
        while (memorydiv.firstChild) {
            noscripts[i].parentNode.insertBefore(memorydiv.firstChild, noscripts[i]);
        }
        noscripts[i].parentNode.removeChild(noscripts[i]);
    }
};
body { font: medium/1.5 monospace; }
p, h1 { margin: 0; }
<h1>Sample Content</h1>
<p>1. This paragraph is embedded in HTML</p>
<script>document.write('<p style="color: red;">2. This paragraph is generated by JavaScript</p>');</script>
<p>3. This paragraph is embedded in HTML</p>
<h1>Sample Content in No-JavaScript Zone</h1>
<noscript>
    <p>1. This paragraph is embedded in HTML</p>
    <script>document.write('<p style="color: red;">2. This paragraph is generated by JavaScript</p>');</script>
    <p>3. This paragraph is embedded in HTML</p>
</noscript>
<noscript>
    <p>1. This paragraph is embedded in HTML</p>
    <script>document.write('<p style="color: red;">2. This paragraph is generated by JavaScript</p>');</script>
    <p>3. This paragraph is embedded in HTML</p>
</noscript>


หากเนื้อหาใน div ไม่น่าเชื่อถือซึ่งฉันเดาว่าจะได้รับคำถาม พวกเขาสามารถปิด<noscript>แท็กแล้วฉีดสิ่งที่พวกเขาชอบ
COWLS

ใช่วิธีแก้ปัญหาที่เหมาะสมคือการแก้ไขปัญหาที่ฝั่งเซิร์ฟเวอร์ สิ่งที่ฉันทำผ่าน JavaScript ควรทำในฝั่งเซิร์ฟเวอร์จะดีกว่า
Salman A

0

หากคุณต้องการเปิดใช้งานแท็กสคริปต์อีกครั้งในภายหลังวิธีแก้ปัญหาของฉันคือทำลายสภาพแวดล้อมของเบราว์เซอร์เพื่อให้สคริปต์ใด ๆ ที่ทำงานอยู่จะทำให้เกิดข้อผิดพลาดค่อนข้างเร็ว อย่างไรก็ตามมันไม่น่าเชื่อถือทั้งหมดดังนั้นคุณจึงไม่สามารถใช้เป็นคุณสมบัติความปลอดภัยได้

หากคุณพยายามเข้าถึงคุณสมบัติส่วนกลาง Chrome จะทำให้เกิดข้อยกเว้น

setTimeout("Math.random()")
// => VM116:25 Uncaught Error: JavaScript Execution Inhibited  

ฉันเขียนทับคุณสมบัติที่เขียนทับได้ทั้งหมดwindowแต่คุณสามารถขยายเพื่อทำลายฟังก์ชันการทำงานอื่น ๆ ได้

window.allowJSExecution = inhibitJavaScriptExecution();
function inhibitJavaScriptExecution(){

    var windowProperties = {};
    var Object = window.Object
    var console = window.console
    var Error = window.Error

    function getPropertyDescriptor(object, propertyName){
        var descriptor = Object.getOwnPropertyDescriptor(object, propertyName);
        if (!descriptor) {
            return getPropertyDescriptor(Object.getPrototypeOf(object), propertyName);
        }
        return descriptor;
    }

    for (var propName in window){
        try {
            windowProperties[propName] = getPropertyDescriptor(window, propName)
            Object.defineProperty(window, propName, {
                get: function(){
                    throw Error("JavaScript Execution Inhibited")
                },
                set: function(){
                    throw Error("JavaScript Execution Inhibited")
                },
                configurable: true
            })
        } catch (err) {}
    }

    return function allowJSExecution(){
        for (var propName in window){
            if (!(propName in windowProperties)) {
                delete windowProperties[propName]
            }
        }

        for (var propName in windowProperties){
            try {
                Object.defineProperty(window, propName, windowProperties[propName])
            } catch (err) {}
        }
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.