Bir tarayıcıdan bir öğe içinde <script> çalıştırmamasını isteyebilir miyim?


84

Tarayıcılara bir HTML belgesinin belirli bölümlerinden JavaScript çalıştırmamalarını söylemek mümkün müdür?

Sevmek:

<div script="false"> ...

Ek bir güvenlik özelliği olarak yararlı olabilir. İstediğim tüm komut dosyaları belgenin belirli bir bölümüne yüklendi. Belgenin diğer bölümlerinde komut dosyası olmamalı ve varsa çalıştırılmamalıdır.


1
Farkında olduğumdan değil. Ben% 100 diyemem çünkü bu, bir açıklama yerine bir cevaptır
freefaller

4
@ chiastic-security DOM'u yalnızca DOM yüklendikten sonra gezinebilirsiniz. Bu bloktaki herhangi bir JS hangi noktaya kadar yürütülürdü.
kovanlar

8
En yakın düşünebildiğim, komut dosyalarını kökenlerine göre kısıtlayabileceğiniz içerik güvenliği politikasıdır (belki de istediğiniz budur). Örneğin script-src:"self", sayfada yalnızca alanınızdaki komut dosyalarının çalışmasına izin verdiğinizi belirterek . Eğer ilgileniyorsanız, Mike West'in CSP hakkındaki bu makalesini okuyun .
Christoph

1
@ chiastic-security Başlıklar zaten gönderildikten sonra bir başlıkta belirtilen kısıtlamayı nasıl gevşetmeyi planlıyorsunuz? Her neyse, CSP varsayılan olarak satır içi komut dosyalarını devre dışı bıraktığından ve beyaz listeye alınmadıkça uzak komut dosyaları yüklenmeyeceğinden, neden başka bir şeyle birleştirmeniz gereksin? CSP muhtemelen OP'nin en iyi bahisidir; Umarım birisi CSP cevabı bırakır.
Dagg Nabbit

6
Birinin HTML'nize rasgele bloklar enjekte etmesinden endişe ediyorsanız, önerilen çözümünüz </div>bu DOM öğesini kapatmak için a enjekte etmelerini ve ardından <div>komut dosyalarının çalışmadığı bir kardeşin kardeşi olacak yeni bir başlatmayı nasıl engelleyecektir ?
Damien_The_Unbeliever

Yanıtlar:


92

EVET, yapabilirsiniz :-) Cevap: İçerik Güvenliği Politikası (CSP).

Çoğu modern tarayıcı , tarayıcıya yalnızca güvenilir bir harici dosyadan JavaScript kodu yüklemesini ve tüm dahili JavaScript koduna izin vermemesini söyleyen bu bayrağı destekler ! Tek dezavantajı, sayfanızın tamamında herhangi bir satır içi JavaScript kullanamamanızdır (yalnızca bir tek için değil <div>). Farklı bir güvenlik politikasına sahip harici bir dosyadan div'i dinamik olarak dahil ederek bir geçici çözüm olabilir, ancak bundan emin değilim.

Ancak, sitenizi tüm JavaScript'i harici JavaScript dosyalarından yükleyecek şekilde değiştirebilirseniz, bu başlık ile satır içi JavaScript'i tamamen devre dışı bırakabilirsiniz!

İşte örnek içeren güzel bir öğretici: HTML5Rocks Eğitimi

Sunucuyu bu HTTP Başlığı bayrağını gönderecek şekilde yapılandırabilirseniz, dünya daha iyi bir yer olacaktır!


2
+1 Bu gerçekten harika, var olduğu hakkında hiçbir fikrim yoktu! (bir not, wiki bağlantınız doğrudan Almanca sürümüne yöneliktir
BrianH

4
Bunu yapsanız bile, bir sayfada kaçış karaktersiz kullanıcı girişine izin vermek yine de kötü bir fikirdir. Bu, temelde kullanıcının sayfayı istediği gibi görünmesi için değiştirmesine izin verir. Tüm İçerik Güvenliği Politikası (CSP) ayarları maksimuma ayarlanmış olsa bile (satır içi komut dosyalarına, stillere vb. İzin vermemekle birlikte), kullanıcı yine de GET istekleri için görüntü srcs kullanarak veya kullanıcıyı kandırarak siteler arası istek sahteciliği (CSRF) saldırısı gerçekleştirebilir. POST istekleri için bir form gönderme düğmesine tıklamak.
Ajedi32

@ Ajedi32 Elbette her zaman kullanıcı girdilerini sterilize etmelisiniz. Ancak CSP, görüntüler veya css gibi GET istekleri için politikalar bile belirleyebilir, yalnızca onları engellemekle kalmaz, aynı zamanda sunucunuzu bu konuda bilgilendirir!
Falco

1
@Falco Belgeleri okudum ve anladığım kadarıyla bu istekleri yalnızca belirli bir etki alanıyla sınırlayabilirsiniz, etki alanındaki belirli bir alt sayfa kümesiyle değil. Muhtemelen ayarladığınız etki alanı sitenizle aynı olacaktır, bu da CSRF saldırılarına hala açık olacağınız anlamına gelir.
Ajedi32

3
@Falco Evet, StackExchange'in yeni Yığın Parçacıkları özelliğiyle yaptığı şey buydu : blog.stackoverflow.com/2014/09/… Girdiyi düzgün bir şekilde temizlerseniz , ayrı bir alan adı gerekli değildir.
Ajedi32

13

Olayı <script>kullanarak yüklenen JavaScript'i engelleyebilirsiniz 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>

Bunun beforescriptexecuteHTML 5.0'da tanımlandığını ancak HTML 5.1'de kaldırıldığını unutmayın. Firefox, onu uygulayan tek ana tarayıcıdır.

Sayfanıza güvenilmeyen bir grup HTML ekliyorsanız, bu öğenin içindeki komut dosyalarını engellemenin daha fazla güvenlik sağlamayacağını unutmayın, çünkü güvenilmeyen HTML korumalı alanı kapatabilir ve bu nedenle komut dosyası dışarıya yerleştirilir ve çalıştırılır.

Ve bu gibi şeyleri engellemeyecek <img onerror="javascript:alert('foo')" src="//" />.


Keman beklendiği gibi çalışmıyor. "Engellenen" kısmı görememeliyim, değil mi?
Salman A

@SalmanA Kesinlikle. Muhtemelen tarayıcınız beforescriptexecuteetkinliği desteklemiyordur . Firefox'ta çalışıyor.
Oriol

Muhtemelen Chrome'da çalışmadığı için, sağlanan ön bilgi ile, ancak görüyorum ki sadece bir parçacığa dönüştürmüşsünüz :-)
Mark Hurd

beforescriptexecuteGörünüşe göre desteklenmiyor ve çoğu büyük tarayıcı tarafından desteklenmeyecek. developer.mozilla.org/en-US/docs/Web/Events/beforescriptexecute
Matt Pennington

8

İlginç soru, bunun mümkün olduğunu sanmıyorum. Ama öyle olsa bile, bir bilgisayar korsanlığı gibi görünüyor.

Bu div'in içeriği güvenilir değilse, HTTP yanıtında gönderilmeden ve tarayıcıda işlenmeden önce sunucu tarafındaki veriden çıkış yapmanız gerekir.

Yalnızca <script>etiketleri kaldırmak ve diğer html etiketlerine izin vermek istiyorsanız , bunları içerikten çıkarın ve gerisini bırakın.

XSS önleme konusuna bakın.

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


7

JavaScript, "satır içi", yani DOM'de göründüğü sırayla çalıştırılır (böyle değilse, farklı bir komut dosyasında tanımlanan bazı değişkenlerin, ilk kez kullandığınızda görünür olduğundan asla emin olamazsınız. ).

Bu, teoride, sayfanın başında <script>DOM'a bakan <script>ve içindeki tüm öğeleri ve olay işleyicileri kaldıran bir komut dosyası (yani ilk öğe) olabileceği anlamına gelir <div>.

Ancak gerçek daha karmaşıktır: DOM ve komut dosyası yükleme eşzamansız olarak gerçekleşir. Bu araçlar tarayıcı sadece script DOM parçası görebilirsiniz konusunda garanti içermediğini önce (şimdiye kadar bizim örneğimizde yani başlığını) buna. Bunun ötesinde hiçbir şeyin garantisi yoktur (bu ile ilgilidir document.write()). Yani bir sonraki komut dosyası etiketini görebilir veya görmeyebilirsiniz.

Sen mandalını olabilir onloadancak o zaman, kötü niyetli kodu zaten idam olabilirdi - Eğer bütün DOM var emin olur - Belgenin olay. Diğer komut dosyaları DOM'u işleyip oraya komut dosyaları eklediklerinde işler daha da kötüleşir. Dolayısıyla, her DOM değişikliğini de kontrol etmeniz gerekir.

Yani @cowls çözümü (sunucuda filtreleme) her durumda çalışması için yapılabilecek tek çözümdür.


1

Tarayıcınızda JavaScript kodunu görüntülemek istiyorsanız:

JavaScript ve HTML kullanarak , JavaScript kodunu görüntülemek ve bu kodun yürütülmesini önlemek için HTML varlıklarını kullanmanız gerekir. Burada HTML varlıklarının listesini bulabilirsiniz:

Sunucu tarafı bir komut dosyası dili (PHP, ASP.NET, vb.) Kullanıyorsanız, büyük olasılıkla, bir dizeden kaçan ve özel karakterleri HTML varlıklarına dönüştüren bir işlev vardır. PHP'de "htmlspecialchars ()" veya "htmlentities ()" kullanırsınız. İkincisi, tüm HTML karakterlerini kapsar.

JavaScript kodunuzu güzel bir şekilde görüntülemek istiyorsanız, kod vurgulayıcılardan birini deneyin:


1

Bir teorim var:

  • Belgenin belirli bir bölümünü bir noscriptetiketin içine sarın .
  • scriptEtiket içindeki tüm etiketleri atmak ve noscriptardından içeriğini açmak için DOM işlevlerini kullanın .

Kavram kanıtı örneği:

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'deki içerik güvenilmezse, sanırım soru verilir. <noscript>Etiketi kapatabilir ve sonra istediklerini enjekte edebilirler.
kovanlar

Evet, doğru çözüm, sorunu sunucu tarafında çözmektir. JavaScript aracılığıyla yaptığım şeyin sunucu tarafında daha iyi yapılması gerekir.
Salman A

0

Daha sonra komut dosyası etiketlerini yeniden etkinleştirmek isterseniz, benim çözümüm tarayıcı ortamını kırmaktı, böylece çalışan herhangi bir komut dosyası oldukça erken bir hata atacaktır. Ancak, tamamen güvenilir değildir, bu nedenle onu bir güvenlik özelliği olarak kullanamazsınız.

Global özelliklere erişmeye çalışırsanız, Chrome bir istisna oluşturur.

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

Üzerine tüm üzerine yazılabilir özelliklerin üzerine yazıyorum window, ancak diğer işlevselliği bozmak için genişletebilirsiniz.

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) {}
        }
    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.