Phương pháp hay nhất để nhúng JSON tùy ý vào DOM?


110

Tôi đang nghĩ đến việc nhúng JSON tùy ý vào DOM như thế này:

<script type="application/json" id="stuff">
    {
        "unicorns": "awesome",
        "abc": [1, 2, 3]
    }
</script>

Điều này tương tự như cách người ta có thể lưu trữ một mẫu HTML tùy ý trong DOM để sử dụng sau này với công cụ mẫu JavaScript. Trong trường hợp này, sau đó chúng tôi có thể truy xuất JSON và phân tích cú pháp nó bằng:

var stuff = JSON.parse(document.getElementById('stuff').innerHTML);

Điều này hoạt động , nhưng nó có phải là cách tốt nhất? Điều này có vi phạm bất kỳ thông lệ hoặc tiêu chuẩn tốt nhất nào không?

Lưu ý: Tôi không tìm kiếm các giải pháp thay thế để lưu trữ JSON trong DOM, tôi đã quyết định đó là giải pháp tốt nhất cho vấn đề cụ thể mà tôi đang gặp phải. Tôi chỉ đang tìm cách tốt nhất để làm điều đó.


1
tại sao bạn sẽ không có nó như một vartrong javascript?
Krizz

@Krizz, nó cần phải là một phần của tài liệu tĩnh mà sau này nó được xử lý bởi một chuỗi javascript đóng gói phức tạp. Lưu trữ nó trong DOM là điều tôi muốn làm.
Ben Lee,

@Krizz Tôi đã được đặt ra với một vấn đề tương tự. Tôi muốn đưa dữ liệu vào một trang web khác nhau cho từng người dùng mà không cần thực hiện yêu cầu AJAX. Vì vậy, tôi đã nhúng một số PHP vào một vùng chứa đã làm điều gì đó tương tự như những gì bạn có ở trên để lấy dữ liệu trong javascript.
Patrick Lorio

2
Tôi nghĩ rằng phương pháp ban đầu của bạn thực sự là tốt nhất. Nó hợp lệ 100% trong HTML5, nó mang tính biểu cảm, nó không tạo ra các phần tử "giả" mà bạn sẽ chỉ xóa hoặc ẩn bằng CSS; và nó không yêu cầu bất kỳ mã hóa ký tự nào. Nhược điểm là gì?
Jamie Treworgy

22
Nếu bạn có một chuỗi có giá trị </script><script>alert()</script><script>bên trong đối tượng JSON của mình, bạn sẽ nhận được những điều bất ngờ. Điều này không an toàn trừ khi bạn làm sạch dữ liệu trước.
silviot

Câu trả lời:


77

Tôi nghĩ rằng phương pháp ban đầu của bạn là tốt nhất. Thông số HTML5 thậm chí còn giải quyết việc sử dụng này:

"Khi được sử dụng để bao gồm các khối dữ liệu (trái ngược với tập lệnh), dữ liệu phải được nhúng nội dòng, định dạng của dữ liệu phải được cung cấp bằng thuộc tính type, thuộc tính src không được chỉ định và nội dung của phần tử tập lệnh phải tuân theo các yêu cầu được xác định cho định dạng được sử dụng. "

Đọc tại đây: http://dev.w3.org/html5/spec/Overview.html#the-script-element

Bạn đã làm chính xác điều đó. Những gì không được yêu? Không cần mã hóa ký tự với dữ liệu thuộc tính. Bạn có thể định dạng nó nếu bạn muốn. Nó diễn đạt và mục đích sử dụng rõ ràng. Nó không giống như một cuộc tấn công (ví dụ: sử dụng CSS để ẩn phần tử "tàu sân bay" của bạn). Nó hoàn toàn hợp lệ.


3
Cảm ơn bạn. Trích dẫn từ thông số kỹ thuật đã thuyết phục tôi.
Ben Lee

17
Nó hoàn toàn hợp lệ chỉ khi bạn kiểm tra và làm sạch đối tượng JSON trước: bạn không thể chỉ nhúng dữ liệu gốc của người dùng. Xem bình luận của tôi về câu hỏi.
silviot

1
thêm tự hỏi: đâu là nơi tốt để đặt nó? đầu hay thân, trên hay dưới?
challet

1
Rất tiếc, có vẻ như chính sách CSP có thể / sẽ dừng tất cả scriptcác thẻ.
Larry K

2
Làm cách nào để bảo vệ hiệu quả việc nhúng JSON có chứa </script> và do đó, cho phép chèn HTML? Có điều gì đó chắc chắn / dễ dàng hay tốt hơn là sử dụng các thuộc tính dữ liệu?
jonasfj

23

Theo hướng chung, tôi sẽ thử sử dụng các thuộc tính dữ liệu HTML5 để thay thế. Không có gì ngăn cản bạn nhập JSON hợp lệ. ví dụ:

<div id="mydiv" data-unicorns='{"unicorns":"awesome", "abc":[1,2,3]}' class="hidden"></div>

Nếu bạn đang sử dụng jQuery, thì việc truy xuất nó dễ dàng như sau:

var stuff = JSON.parse($('#mydiv').attr('data-unicorns'));

1
Có ý nghĩa. Mặc dù lưu ý rằng với các dấu ngoặc kép cho tên khóa, JSON.parsesẽ không hoạt động (ít nhất là JSON gốc của Google Chrome sẽ không hoạt động). Đặc tả JSON yêu cầu dấu ngoặc kép. Nhưng điều đó đủ dễ dàng để sửa chữa bằng cách sử dụng các thực thể như ...&lt;unicorns&gt;:....
Ben Lee

4
Tuy nhiên, có một câu hỏi: Có giới hạn nào về độ dài của các thuộc tính trong HTML 5 không?
Ben Lee,

Vâng, điều đó sẽ hoạt động. Bạn cũng có thể chuyển đổi nó để HTML của bạn sử dụng dấu ngoặc kép và dữ liệu JSON sử dụng dấu ngoặc kép.
Horatio Alderaan

1
Ok, đã tìm thấy câu trả lời cho câu hỏi của tôi: stackoverflow.com/questions/1496096/… - điều này là đủ cho mục đích của tôi.
Ben Lee

2
Điều này sẽ không hoạt động đối với một chuỗi đơn, ví dụ: "I am valid JSON"và sử dụng dấu ngoặc kép cho thẻ hoặc dấu ngoặc kép đơn với dấu ngoặc kép trong chuỗi, ví dụ data-unicorns='"My JSON's string"'như dấu ngoặc kép không thoát với mã hóa dưới dạng JSON.
Robbie Averill

13

Phương pháp nhúng json vào thẻ script này tiềm ẩn một vấn đề bảo mật. Giả sử dữ liệu json có nguồn gốc từ đầu vào của người dùng, có thể tạo một thành viên dữ liệu sẽ thoát ra khỏi thẻ script và cho phép đưa trực tiếp vào dom. Xem tại đây:

http://jsfiddle.net/YmhZv/1/

Đây là mũi tiêm

<script type="application/json" id="stuff">
{
    "unicorns": "awesome",
    "abc": [1, 2, 3],
    "badentry": "blah </script><div id='baddiv'>I should not exist.</div><script type="application/json" id='stuff'> ",
}
</script>

Không có cách nào xung quanh việc thoát / mã hóa.


7
Điều này đúng, nhưng nó không thực sự là một lỗ hổng bảo mật của phương pháp. Nếu bạn đã từng đưa thứ gì đó bắt nguồn từ thông tin người dùng nhập vào các trang của mình, bạn phải siêng năng thoát khỏi nó. Phương pháp này vẫn hoạt động miễn là bạn thực hiện các biện pháp phòng ngừa thông thường liên quan đến thông tin nhập của người dùng.
Ben Lee

JSON không phải là một phần của HTML, trình phân tích cú pháp HTML vẫn tiếp tục. Nó giống như khi JSON sẽ là một phần của đoạn văn bản hoặc phần tử div. HTML-thoát nội dung trong chương trình của bạn. Ngoài ra, bạn cũng có thể thoát khỏi những nhát chém. Mặc dù JSON không yêu cầu điều này, nhưng nó vẫn chấp nhận các dấu gạch chéo không cần thiết. Có thể sử dụng cô ấy với mục đích làm cho nó an toàn khi nhúng. Json_encode của PHP thực hiện điều này theo mặc định.
Timo Tijhof

7

Xem Quy tắc # 3.1 trong trang tính ngăn chặn XSS của OWASP.

Giả sử bạn muốn đưa JSON này vào HTML:

{
    "html": "<script>alert(\"XSS!\");</script>"
}

Tạo một ẩn <div>trong HTML. Tiếp theo, thoát khỏi JSON của bạn bằng cách mã hóa các thực thể không an toàn (ví dụ: &, <,>, ", 'và, /) và đặt nó bên trong phần tử.

<div id="init_data" style="display:none">
        {&#34;html&#34;:&#34;&lt;script&gt;alert(\&#34;XSS!\&#34;);&lt;/script&gt;&#34;}
</div>

Bây giờ bạn có thể truy cập nó bằng cách đọc textContentphần tử bằng JavaScript và phân tích cú pháp nó:

var text = document.querySelector('#init_data').textContent;
var json = JSON.parse(text);
console.log(json); // {html: "<script>alert("XSS!");</script>"}

Tôi tin rằng đây là câu trả lời tốt nhất và an toàn nhất. Lưu ý rằng rất nhiều ký tự JSON phổ biến bị thoát và một số ký tự nhất định được thoát kép, chẳng hạn như dấu ngoặc kép bên trong đối tượng {name: 'Dwayne "The Rock" Johnson'}. Nhưng có lẽ tốt nhất vẫn nên sử dụng phương pháp này vì thư viện khuôn khổ / khuôn mẫu của bạn có thể đã bao gồm một cách an toàn để thực hiện mã hóa HTML. Một giải pháp thay thế sẽ là sử dụng base64, HTML vừa an toàn vừa an toàn để đặt bên trong một chuỗi JS. Thật dễ dàng để mã hóa / giải mã trong JS bằng btoa () / atob () và bạn có thể dễ dàng thực hiện phía máy chủ.
sstur

Một phương pháp thậm chí còn an toàn hơn là sử dụng <data>phần tử chính xác về mặt ngữ nghĩa và bao gồm dữ liệu JSON trong valuethuộc tính. Sau đó, bạn chỉ cần thoát khỏi các dấu ngoặc kép &quotnếu bạn sử dụng dấu ngoặc kép để bao gồm dữ liệu hoặc &#39;nếu bạn sử dụng dấu ngoặc kép (có lẽ tốt hơn).
Rúnar Berg

5

Tôi khuyên bạn nên đặt JSON vào một tập lệnh nội tuyến với một hàm gọi lại (loại JSONP ):

<script>
someCallback({
    "unicorns": "awesome",
    "abc": [1, 2, 3]
});
</script>

Nếu tập lệnh đang thực thi được tải sau tài liệu, bạn có thể lưu trữ nó ở đâu đó, có thể với một đối số định danh bổ sung: someCallback("stuff", { ... });


@BenLee nó sẽ hoạt động rất tốt, với nhược điểm duy nhất là phải xác định hàm gọi lại. Giải pháp đề xuất khác là ngắt các ký tự HTML đặc biệt (ví dụ &) và dấu ngoặc kép, nếu bạn có các ký tự đó trong JSON của mình.
sao chép

Tốt hơn điều này cảm thấy bởi vì bạn không cần phải truy vấn dom để tìm dữ liệu
Jaseem

@copy Giải pháp này vẫn cần thoát (chỉ là một loại khác), hãy xem câu trả lời của MadCoder. Chỉ để nó ở đây cho hoàn chỉnh.
pvgoran

2

Đề xuất của tôi là giữ dữ liệu JSON trong các .jsontệp bên ngoài và sau đó truy xuất các tệp đó qua Ajax. Bạn không đặt mã CSS và JavaScript vào trang web (nội dòng), vậy tại sao bạn lại làm điều đó với JSON?


12
Bạn không đặt nội tuyến CSS và Javascript trong một trang web vì nó thường được chia sẻ giữa các trang khác. Nếu dữ liệu được đề cập được tạo bởi máy chủ một cách rõ ràng cho ngữ cảnh này, thì việc nhúng dữ liệu sẽ hiệu quả hơn nhiều so với việc khởi tạo một yêu cầu khác cho một thứ không thể lưu vào bộ nhớ cache.
Jamie Treworgy

Đó là bởi vì tôi đang cập nhật một hệ thống cũ được thiết kế kém và thay vì thiết kế lại toàn bộ hệ thống, tôi chỉ cần sửa một phần. Lưu trữ JSON trong DOM là cách tốt nhất để khắc phục phần này. Ngoài ra, tôi đồng ý với những gì @jamietre đã nói.
Ben Lee

@jamietre Lưu ý rằng OP đã nói rằng chuỗi JSON này chỉ cần thiết sau này . Câu hỏi là nếu nó luôn cần thiết, hoặc chỉ trong một số trường hợp. Nếu nó chỉ cần thiết trong một số trường hợp, thì bạn nên có nó trong một tệp bên ngoài và chỉ tải nó có điều kiện.
Šime Vidas

2
Tôi đồng ý rằng có rất nhiều "điều gì xảy ra nếu" có thể làm tăng quy mô theo cách này hay cách khác. Nhưng nói chung nếu bạn biết khi nào trang được hiển thị những gì bạn cần - ngay cả khi chỉ có thể - thì tốt hơn là gửi nó ngay lập tức. Giống như, nếu tôi có một số hộp thông tin bắt đầu bị thu gọn, tôi thường muốn đưa nội dung của chúng vào dòng để chúng mở rộng ngay lập tức. Chi phí của một yêu cầu mới là rất nhiều so với chi phí của một chút dữ liệu bổ sung trên yêu cầu hiện có và nó tạo ra trải nghiệm người dùng đáp ứng tốt hơn. Tôi chắc chắn rằng có một điểm phá vỡ.
Jamie Treworgy

2

HTML5 bao gồm một <data>phần tử để giữ cho máy có thể đọc được dữ liệu. Để thay thế — có lẽ an toàn hơn — <script type="application/json">bạn có thể bao gồm dữ liệu JSON của mình bên trong valuethuộc tính của phần tử đó.

const jsonData = document.querySelector('.json-data');
const data = JSON.parse(jsonData.value);

console.log(data)
<data class="json-data" value='
  {
    "unicorns": "awesome",
    "abc": [1, 2, 3],
    "careful": "to escape &#39; quotes"
  }
'></data>

Trong trường hợp này, bạn cần thay thế tất cả các dấu nháy đơn bằng &#39;hoặc bằng &quot;nếu bạn chọn đặt giá trị bằng dấu nháy kép. Nếu không thì nguy cơ tấn công XSS của bạn như các câu trả lời khác đã đề xuất.

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.