Tôi có thể yêu cầu trình duyệt không chạy <script> trong một phần tử không?


84

Có thể yêu cầu trình duyệt không chạy JavaScript từ các phần cụ thể của tài liệu HTML không?

Giống:

<div script="false"> ...

Nó có thể hữu ích như một tính năng bảo mật bổ sung. Tất cả các tập lệnh tôi muốn được tải trong một phần cụ thể của tài liệu. Không được có tập lệnh nào trong các phần khác của tài liệu và nếu có thì không nên chạy chúng.


1
Không phải là tôi biết. Đây là một bình luận chứ không phải là một câu trả lời, bởi vì tôi không thể nói 100%
freefaller

4
@ chiastic-security Bạn chỉ có thể duyệt qua DOM sau khi DOM được tải. Tại thời điểm đó, bất kỳ JS nào trong khối đó sẽ được thực thi.
cowls

8
Điều gần nhất mà tôi có thể nghĩ đến là chính sách bảo mật nội dung, nơi bạn có thể hạn chế các tập lệnh theo nguồn gốc của chúng (có lẽ đó là những gì bạn muốn). Ví dụ: bằng cách chỉ định script-src:"self"bạn chỉ cho phép các tập lệnh từ miền của bạn chạy trong trang. Nếu bạn quan tâm, hãy đọc bài viết này của Mike West về CSP .
Christoph

1
@ chiastic-security, làm cách nào để bạn có kế hoạch nới lỏng một hạn chế được chỉ định trong tiêu đề sau khi tiêu đề đã được gửi đi? Dù sao, vì CSP vô hiệu hóa các tập lệnh nội tuyến theo mặc định và các tập lệnh từ xa sẽ không tải trừ khi được đưa vào danh sách trắng, tại sao bạn cần kết hợp nó với bất kỳ thứ gì khác? CSP có lẽ là đặt cược tốt nhất của OP; Tôi hy vọng ai đó để lại câu trả lời CSP.
Dagg Nabbit

6
Nếu bạn lo lắng về việc ai đó chèn các khối tùy ý vào HTML của mình, thì giải pháp được đề xuất của bạn sẽ ngăn chặn việc họ chèn một </div>phần tử DOM này vào để đóng phần tử DOM này và sau đó bắt đầu một phần tử mới <div>sẽ là anh chị em của phần tử mà các tập lệnh không chạy như thế nào?
Damien_The_Un Believer

Câu trả lời:


92

CÓ, bạn có thể :-) Câu trả lời là: Chính sách bảo mật nội dung (CSP).

Hầu hết các trình duyệt hiện đại đều hỗ trợ cờ này , cờ này chỉ cho trình duyệt tải mã JavaScript từ tệp bên ngoài đáng tin cậy và không cho phép tất cả mã JavaScript nội bộ! Nhược điểm duy nhất là bạn không thể sử dụng bất kỳ JavaScript nội tuyến nào trong toàn bộ trang của mình (không chỉ cho một trang duy nhất <div>). Mặc dù có thể có một cách giải quyết bằng cách bao gồm động div từ tệp bên ngoài với chính sách bảo mật khác, nhưng tôi không chắc về điều đó.

Nhưng nếu bạn có thể thay đổi trang web của mình để tải tất cả JavaScript từ các tệp JavaScript bên ngoài thì bạn có thể tắt hoàn toàn JavaScript nội tuyến với tiêu đề này!

Đây là một hướng dẫn hay với ví dụ: Hướng dẫn HTML5Rocks

Nếu bạn có thể cấu hình máy chủ để gửi cờ HTTP-Header này thì thế giới sẽ là một nơi tốt hơn!


2
+1 Điều đó thực sự rất tuyệt, tôi không biết nó tồn tại! (một lưu ý, liên kết wiki của bạn trực tiếp đến phiên bản tiếng Đức) Đây là tóm tắt về hỗ trợ trình duyệt: caniuse.com/#feat=contentsecuritypolicy
BrianH

4
Lưu ý rằng ngay cả khi bạn làm điều này, thì việc cho phép người dùng không thoát khỏi đầu vào trên một trang vẫn là một ý tưởng tồi. Điều đó về cơ bản sẽ cho phép người dùng thay đổi trang theo cách họ muốn. Ngay cả khi tất cả cài đặt Chính sách bảo mật nội dung (CSP) được đặt ở mức tối đa (không cho phép tập lệnh nội tuyến, kiểu, v.v.), người dùng vẫn có thể thực hiện cuộc tấn công giả mạo yêu cầu trên nhiều trang web (CSRF) bằng cách sử dụng srcs hình ảnh cho các yêu cầu GET hoặc lừa người dùng vào nhấp vào nút gửi biểu mẫu cho các yêu cầu ĐĂNG.
Ajedi32

@ Ajedi32 Tất nhiên bạn phải luôn khử trùng đầu vào của người dùng. Nhưng CSP thậm chí có thể thiết lập các chính sách cho các yêu cầu GET như hình ảnh hoặc css, nó sẽ không chỉ chặn chúng mà còn thông báo cho máy chủ của bạn về điều đó!
Falco

1
@Falco Tôi đã đọc tài liệu và tôi hiểu rằng bạn chỉ có thể hạn chế những yêu cầu đó đối với một miền nhất định, không phải một tập hợp các trang con cụ thể trên miền. Có lẽ miền bạn đặt sẽ giống với trang web của bạn, có nghĩa là bạn vẫn có thể bị tấn công CSRF.
Ajedi32

3
@Falco Vâng, về cơ bản đó là những gì StackExchange đã làm với tính năng Stack Snippets mới: blog.stackoverflow.com/2014/09/… Nếu bạn làm sạch đầu vào đúng cách, thì một miền riêng biệt là không cần thiết.
Ajedi32

13

Bạn có thể chặn JavaScript được tải bằng <script>cách sử dụng beforescriptexecutesự kiện:

<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>

Lưu ý rằng điều đó beforescriptexecuteđã được định nghĩa trong HTML 5.0 nhưng đã bị loại bỏ trong HTML 5.1. Firefox là trình duyệt chính duy nhất đã triển khai nó.

Trong trường hợp bạn đang chèn một loạt HTML không đáng tin cậy vào trang của mình, hãy lưu ý việc chặn các tập lệnh bên trong phần tử đó sẽ không cung cấp bảo mật hơn, vì HTML không đáng tin cậy có thể đóng phần tử hộp cát và do đó tập lệnh sẽ được đặt bên ngoài và chạy.

Và điều này sẽ không chặn những thứ như thế <img onerror="javascript:alert('foo')" src="//" />.


Cỗ máy có vẻ không hoạt động như mong đợi. Tôi sẽ không thể nhìn thấy phần "bị chặn", phải không?
Salman A

@SalmanA Chính xác. Có thể, trình duyệt của bạn không hỗ trợ beforescriptexecutesự kiện. Nó hoạt động trên Firefox.
Oriol

Có thể là vì nó không hoạt động trong Chrome, với đoạn mã như được cung cấp, mặc dù tôi thấy bạn chỉ mới chuyển đổi nó thành đoạn mã :-)
Mark Hurd

beforescriptexecutecó vẻ như nó không được hỗ trợ và sẽ không được hỗ trợ bởi hầu hết các trình duyệt chính. developer.mozilla.org/en-US/docs/Web/Events/beforescriptexecute
Matt Pennington

8

Câu hỏi thú vị, tôi không nghĩ nó có thể. Nhưng ngay cả khi nó có, nó có vẻ như là một hack.

Nếu nội dung của div đó không đáng tin cậy, thì bạn cần phải thoát dữ liệu ở phía máy chủ trước khi nó được gửi trong phản hồi HTTP và hiển thị trong trình duyệt.

Nếu bạn chỉ muốn xóa <script>các thẻ và cho phép các thẻ html khác, thì chỉ cần loại bỏ chúng khỏi nội dung và để lại phần còn lại.

Xem xét việc ngăn chặn XSS.

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


7

JavaScript được thực thi "nội tuyến", tức là theo thứ tự xuất hiện trong DOM (nếu không phải như vậy, bạn không bao giờ có thể chắc chắn rằng một số biến được xác định trong một tập lệnh khác sẽ hiển thị khi bạn sử dụng nó lần đầu tiên ).

Vì vậy, điều đó có nghĩa là về lý thuyết, bạn có thể có một tập lệnh ở đầu trang (tức là <script>phần tử đầu tiên ) xem qua DOM và loại bỏ tất cả các <script>phần tử và trình xử lý sự kiện bên trong của bạn <div>.

Nhưng thực tế phức tạp hơn: DOM và quá trình tải tập lệnh diễn ra không đồng bộ. Điều này có nghĩa là trình duyệt chỉ đảm bảo rằng một tập lệnh có thể nhìn thấy phần của DOM nằm trước nó (tức là tiêu đề cho đến nay trong ví dụ của chúng tôi). Không có đảm bảo cho bất cứ điều gì ngoài (điều này liên quan đến document.write()). Vì vậy, bạn có thể thấy thẻ script tiếp theo hoặc có thể, bạn không thấy.

Bạn có thể nắm bắt onloadsự kiện của tài liệu - điều này sẽ đảm bảo bạn có toàn bộ DOM - nhưng tại thời điểm đó, mã độc hại có thể đã được thực thi. Mọi thứ trở nên tồi tệ hơn khi các tập lệnh khác thao túng DOM, thêm các tập lệnh vào đó. Vì vậy, bạn cũng sẽ phải kiểm tra mọi thay đổi của DOM.

Vì vậy, giải pháp @cowls (lọc trên máy chủ) là giải pháp duy nhất có thể hoạt động trong mọi tình huống.


1

Nếu bạn đang tìm cách hiển thị mã JavaScript trong trình duyệt của mình:

Sử dụng JavaScript và HTML, bạn sẽ phải sử dụng các thực thể HTML để hiển thị mã JavaScript và tránh để mã này được thực thi. Tại đây, bạn có thể tìm thấy danh sách các thực thể HTML:

Nếu bạn đang sử dụng ngôn ngữ kịch bản phía máy chủ (PHP, ASP.NET, v.v.), hầu hết có một hàm sẽ thoát khỏi một chuỗi và chuyển đổi các ký tự đặc biệt thành các thực thể HTML. Trong PHP, bạn sẽ sử dụng "htmlspecialchars ()" hoặc "htmlentities ()". Cái sau bao gồm tất cả các ký tự HTML.

Nếu bạn đang muốn hiển thị mã JavaScript của mình một cách đẹp mắt, thì hãy thử một trong các công cụ đánh dấu mã:


1

Tôi có một lý thuyết:

  • Bao bọc phần cụ thể của tài liệu bên trong noscriptthẻ.
  • Sử dụng các hàm DOM để loại bỏ tất cả scriptcác thẻ bên trong noscriptthẻ sau đó mở gói nội dung của nó.

Ví dụ chứng minh khái niệm:

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>


Nếu nội dung trong div là không đáng tin cậy, tôi đoán nó được đưa ra câu hỏi. Họ chỉ có thể đóng <noscript>thẻ và sau đó tiêm những gì họ thích.
cowls

Có, giải pháp thích hợp là khắc phục sự cố ở phía máy chủ. Những gì tôi đang làm qua JavaScript tốt hơn nên được thực hiện ở phía máy chủ.
Salman A

0

Nếu bạn muốn kích hoạt lại các thẻ tập lệnh sau này, giải pháp của tôi là phá vỡ môi trường trình duyệt để bất kỳ tập lệnh nào chạy sẽ gặp lỗi khá sớm. Tuy nhiên, nó không hoàn toàn đáng tin cậy, vì vậy bạn không thể sử dụng nó như một tính năng bảo mật.

Nếu bạn cố gắng truy cập các thuộc tính chung, Chrome sẽ đưa ra một ngoại lệ.

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

Tôi đang ghi đè lên tất cả các thuộc tính windowcó thể ghi đè , nhưng bạn cũng có thể mở rộng nó để phá vỡ chức năng khác.

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) {}
        }
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.