با خیال راحت در IFrames sandboxed بازی کنید

Mike West

ایجاد یک تجربه غنی در وب امروزی تقریباً به طور اجتناب ناپذیری مستلزم جاسازی اجزا و محتوایی است که کنترل واقعی روی آنها ندارید. ویجت‌های شخص ثالث می‌توانند تعامل را افزایش دهند و نقش مهمی در تجربه کلی کاربر ایفا کنند، و محتوای تولید شده توسط کاربر گاهی حتی مهم‌تر از محتوای بومی سایت است. پرهیز از هر یک واقعاً یک گزینه نیست، اما هر دوی آنها خطر رخ دادن چیزی بد را در سایت شما افزایش می‌دهند. هر ویجتی که جاسازی می‌کنید - هر تبلیغ، هر ویجت رسانه‌های اجتماعی - یک بردار حمله بالقوه برای کسانی است که قصد مخرب دارند:

خط‌مشی امنیت محتوا (CSP) می‌تواند خطرات مربوط به هر دوی این نوع محتوا را با دادن توانایی در لیست سفید منابع خاص اسکریپت و سایر محتواها کاهش دهد. این یک گام بزرگ در مسیر درست است، اما شایان ذکر است که حفاظتی که اکثر دستورالعمل‌های CSP ارائه می‌دهند باینری است: منبع مجاز است یا مجاز نیست. مواقعی مفید است که بگوییم "مطمئن نیستم واقعاً به این منبع محتوا اعتماد دارم ، اما خیلی زیباست! لطفاً مرورگر، آن را جاسازی کنید، اما اجازه ندهید سایت من را خراب کند."

کمترین امتیاز

در اصل، ما به دنبال مکانیزمی هستیم که به ما امکان دهد محتوایی را که فقط حداقل سطح توانایی لازم برای انجام کار آن را تعبیه کرده ایم، اعطا کنیم. اگر یک ویجت نیازی به باز کردن یک پنجره جدید نداشته باشد، از بین بردن دسترسی به window.open ضرری ندارد. اگر به فلش نیاز ندارد، خاموش کردن پشتیبانی افزونه نباید مشکلی ایجاد کند. اگر از اصل حداقل امتیاز پیروی کنیم و تک تک ویژگی‌هایی را که مستقیماً به عملکردی که می‌خواهیم استفاده کنیم مرتبط نیستند، مسدود کنیم، تا آنجا که می‌توانیم ایمن هستیم. نتیجه این است که ما دیگر مجبور نیستیم کورکورانه اعتماد کنیم که برخی از محتوای جاسازی شده از امتیازاتی که نباید استفاده می‌کنند، استفاده نمی‌کنند. در وهله اول به سادگی به عملکرد دسترسی نخواهد داشت.

عناصر iframe اولین قدم به سمت یک چارچوب خوب برای چنین راه حلی هستند. بارگیری برخی از مؤلفه‌های نامعتبر در iframe معیاری از جدایی بین برنامه شما و محتوایی را که می‌خواهید بارگیری کنید، فراهم می‌کند. محتوای قاب شده به DOM صفحه شما یا داده‌هایی که به صورت محلی ذخیره کرده‌اید دسترسی نخواهد داشت، و همچنین نمی‌تواند به موقعیت‌های دلخواه در صفحه بکشد. محدوده آن به طرح کلی قاب محدود شده است. با این حال، جدایی واقعاً قوی نیست. صفحه موجود هنوز تعدادی گزینه برای رفتار آزاردهنده یا مخرب دارد: پخش خودکار ویدیو، افزونه‌ها و پنجره‌های بازشو نوک کوه یخ هستند.

ویژگی sandbox عنصر iframe دقیقاً آنچه را که برای تشدید محدودیت‌های محتوای فریم شده نیاز داریم به ما می‌دهد. ما می‌توانیم به مرورگر دستور دهیم که محتوای یک فریم خاص را در یک محیط با امتیاز پایین بارگذاری کند و تنها به زیرمجموعه‌ای از قابلیت‌های لازم برای انجام هر کاری که نیاز به انجام دارد اجازه دهد.

بچرخانید، اما تأیید کنید

دکمه «توییت» توییتر نمونه‌ای عالی از عملکردی است که می‌توان آن را با خیال راحت‌تر از طریق sandbox در سایت شما جاسازی کرد. توییتر به شما امکان می دهد دکمه را از طریق iframe با کد زیر جاسازی کنید :

<iframe src="https://platform.twitter.com/widgets/tweet_button.html"
        style="border: 0; width:130px; height:20px;"></iframe>

برای اینکه بفهمیم چه چیزی را می توانیم قفل کنیم، بیایید به دقت بررسی کنیم که دکمه به چه قابلیت هایی نیاز دارد. HTML که در فریم بارگذاری می‌شود، کمی جاوا اسکریپت را از سرورهای توییتر اجرا می‌کند و پس از کلیک کردن، یک پنجره بازشو ایجاد می‌کند که با یک رابط توییت پر شده است. این رابط برای اتصال توییت به حساب صحیح نیاز به دسترسی به کوکی‌های توییتر دارد و به توانایی ارسال فرم توییت نیاز دارد. تقریباً همین است. فریم نیازی به بارگذاری هیچ پلاگینی ندارد، نیازی به پیمایش در پنجره سطح بالا یا هر یک از تعدادی از عملکردهای دیگر ندارد. از آنجایی که به آن امتیازات نیازی ندارد، بیایید آنها را با جعبه شنی محتوای قاب حذف کنیم.

Sandboxing بر اساس یک لیست سفید کار می کند. ما با حذف همه مجوزهای ممکن شروع می کنیم و سپس با افزودن پرچم های خاص به پیکربندی جعبه شنی، قابلیت های فردی را دوباره فعال می کنیم. برای ویجت توییتر، ما تصمیم گرفته‌ایم جاوا اسکریپت، پنجره‌های بازشو، ارسال فرم و کوکی‌های twitter.com را فعال کنیم. ما می توانیم این کار را با افزودن یک ویژگی sandbox به iframe با مقدار زیر انجام دهیم:

<iframe sandbox="allow-same-origin allow-scripts allow-popups allow-forms"
    src="https://platform.twitter.com/widgets/tweet_button.html"
    style="border: 0; width:130px; height:20px;"></iframe>

همین است. ما تمام قابلیت‌هایی را که به فریم نیاز دارد داده‌ایم، و مرورگر به‌خوبی از دسترسی آن به هر یک از امتیازاتی که به‌صراحت از طریق مقدار ویژگی sandbox به آن اعطا نکرده‌ایم، جلوگیری می‌کند.

کنترل گرانول بر قابلیت ها

ما تعدادی از پرچم‌های sandboxing ممکن را در مثال بالا دیدیم، حالا اجازه دهید با کمی جزئیات بیشتر به بررسی عملکرد داخلی این ویژگی بپردازیم.

با توجه به iframe با ویژگی sandbox خالی، سند قاب شده به طور کامل sandbox می شود و آن را تحت محدودیت های زیر قرار می دهد:

  • جاوا اسکریپت در سند قاب شده اجرا نمی شود. این نه تنها شامل جاوا اسکریپت است که به صراحت از طریق تگ‌های اسکریپت بارگیری می‌شود، بلکه شامل کنترل‌کننده‌های رویداد درون خطی و جاوا اسکریپت: URL‌ها نیز می‌شود. این همچنین به این معنی است که محتوای موجود در برچسب‌های noscript نمایش داده می‌شود، دقیقاً همانطور که کاربر خود اسکریپت را غیرفعال کرده است.
  • سند قاب شده در یک مبدأ منحصربه‌فرد بارگذاری می‌شود، به این معنی که همه بررسی‌های یک منبع ناموفق خواهند بود. خاستگاه‌های منحصربه‌فرد با هیچ ریشه دیگری، حتی با خودشان همخوانی ندارند. از جمله تأثیرات دیگر، این بدان معناست که سند به داده‌های ذخیره شده در کوکی‌های منبع یا مکانیسم‌های ذخیره‌سازی دیگر (ذخیره‌سازی DOM، DB فهرست‌شده، و غیره) دسترسی ندارد.
  • سند قاب شده نمی تواند پنجره ها یا دیالوگ های جدیدی ایجاد کند (مثلاً از طریق window.open یا target="_blank" .
  • فرم ها قابل ارسال نیستند.
  • پلاگین ها بارگیری نمی شوند.
  • سند قاب شده فقط می تواند خودش را پیمایش کند، نه والد سطح بالای آن. تنظیم window.top.location یک استثنا ایجاد می کند و کلیک روی پیوند با target="_top" هیچ تاثیری نخواهد داشت.
  • ویژگی‌هایی که به‌طور خودکار فعال می‌شوند (عناصر فرم با فوکوس خودکار، پخش خودکار ویدیوها و غیره) مسدود می‌شوند.
  • قفل اشاره گر را نمی توان بدست آورد.
  • ویژگی seamless در iframes سند قاب شده حاوی آن است نادیده گرفته می شود.

این بسیار سخت است و یک سند بارگذاری شده در یک iframe کاملاً sandboxed خطر بسیار کمی دارد. البته، همچنین نمی‌تواند ارزش زیادی داشته باشد: ممکن است بتوانید از یک جعبه ماسه‌بازی کامل برای برخی از محتوای ثابت خلاص شوید، اما در بیشتر مواقع می‌خواهید کمی مسائل را شل کنید.

به استثنای پلاگین ها، هر یک از این محدودیت ها را می توان با افزودن یک پرچم به مقدار ویژگی sandbox برداشت. اسناد Sandboxed هرگز نمی‌توانند پلاگین‌ها را اجرا کنند، زیرا افزونه‌ها کدهای بومی بدون سندباکس هستند، اما هر چیز دیگری یک بازی منصفانه است:

  • allow-forms اجازه ارسال فرم را می دهد.
  • allow-popups اجازه می دهد (شوک!) پنجره های بازشو.
  • allow-pointer-lock اجازه می دهد (تعجب!) اشاره گر.
  • allow-same-origin به سند اجازه می دهد تا اصل خود را حفظ کند. صفحات بارگیری شده از https://example.com/ دسترسی به داده های آن مبدا را حفظ خواهند کرد.
  • allow-scripts به اجرای جاوا اسکریپت اجازه می دهد و همچنین به ویژگی ها اجازه می دهد تا به طور خودکار فعال شوند (زیرا اجرای آنها از طریق جاوا اسکریپت بی اهمیت است).
  • allow-top-navigation می دهد تا با پیمایش در پنجره سطح بالا، سند از کادر خارج شود.

با در نظر گرفتن این موارد، می‌توانیم دقیقاً ارزیابی کنیم که چرا به مجموعه خاصی از پرچم‌های sandboxing در مثال بالا توییتر رسیدیم:

  • allow-scripts مورد نیاز است، زیرا صفحه بارگذاری شده در فریم مقداری جاوا اسکریپت را برای مقابله با تعامل کاربر اجرا می کند.
  • allow-popups مورد نیاز است، زیرا دکمه یک فرم توییت را در یک پنجره جدید نمایش می‌دهد.
  • allow-forms مورد نیاز است، زیرا فرم توییت باید قابل ارسال باشد.
  • allow-same-origin ضروری است، زیرا کوکی‌های twitter.com در غیر این صورت غیرقابل دسترسی هستند و کاربر نمی‌تواند برای ارسال فرم وارد سیستم شود.

نکته مهمی که باید به آن توجه داشت این است که پرچم های sandboxing اعمال شده بر روی یک قاب برای هر پنجره یا قاب ایجاد شده در sandbox نیز اعمال می شود. این بدان معناست که ما باید allow-forms به جعبه شنی فریم اضافه کنیم، حتی اگر فرم فقط در پنجره‌ای وجود داشته باشد که فریم ظاهر می‌شود.

با وجود ویژگی sandbox ، ویجت فقط مجوزهای مورد نیاز خود را دریافت می کند و قابلیت هایی مانند پلاگین ها، پیمایش بالا و قفل اشاره گر مسدود می شوند. ما خطر جاسازی ویجت را کاهش داده‌ایم، بدون هیچ گونه تأثیر سوء. این یک پیروزی برای هر کسی است.

تفکیک امتیاز

سندباکس کردن محتوای شخص ثالث به منظور اجرای کدهای نامعتبر آنها در محیطی با امتیاز پایین کاملاً سودمند است. اما کد خودت چطور؟ به خودت اعتماد داری، درسته؟ پس چرا نگران سندباکسینگ باشید؟

من این سوال را برعکس می کنم: اگر کد شما به پلاگین نیاز ندارد، چرا به آن اجازه دسترسی به افزونه ها را بدهید؟ در بهترین حالت، این امتیازی است که هرگز از آن استفاده نمی‌کنید، در بدترین حالت، یک عامل بالقوه برای مهاجمان است که پا به درب بگذارند. کد همه دارای اشکالاتی است و عملاً هر برنامه ای به نوعی در برابر سوء استفاده آسیب پذیر است. Sandboxing کد خود به این معنی است که حتی اگر یک مهاجم با موفقیت برنامه شما را زیر و رو کند، به آنها دسترسی کامل به مبدا برنامه داده نخواهد شد. آنها فقط می توانند کارهایی را انجام دهند که برنامه می تواند انجام دهد. هنوز هم بد است، اما نه به آن بدی که می تواند باشد.

شما می توانید با تقسیم برنامه خود به قطعات منطقی و جعبه شنی هر قطعه با حداقل امتیاز ممکن، خطر را حتی بیشتر کاهش دهید. این تکنیک در کدهای بومی بسیار رایج است: برای مثال کروم خود را به یک فرآیند مرورگر با امتیاز بالا که به هارد دیسک محلی دسترسی دارد و می‌تواند اتصالات شبکه برقرار کند، تجزیه می‌کند و بسیاری از فرآیندهای رندر با امتیاز پایین که تجزیه محتوای غیرقابل اعتماد را انجام می‌دهند. رندرها نیازی به لمس دیسک ندارند، مرورگر مراقب است که تمام اطلاعاتی را که برای رندر کردن صفحه نیاز دارند به آنها بدهد. حتی اگر یک هکر باهوش راهی برای فاسد کردن یک رندر پیدا کند، هنوز خیلی پیشرفت نکرده است، زیرا رندر نمی‌تواند به تنهایی کارهای زیادی را انجام دهد: تمام دسترسی‌های با امتیاز بالا باید از طریق فرآیند مرورگر هدایت شوند. مهاجمان باید چندین سوراخ در قطعات مختلف سیستم پیدا کنند تا هر گونه آسیبی را وارد کنند، که خطر موفقیت آمیز pwnage را بسیار کاهش می دهد.

eval() sandboxing امن

با sandboxing و postMessage API ، موفقیت این مدل برای اعمال در وب نسبتاً ساده است. بخش‌هایی از برنامه شما می‌توانند در iframe های sandboxed قرار بگیرند و سند والد می‌تواند با ارسال پیام‌ها و گوش دادن به پاسخ‌ها، ارتباط بین آنها را واسطه‌ای کند. این نوع ساختار تضمین می کند که اکسپلویت ها در هر قسمت از برنامه حداقل آسیب ممکن را وارد می کنند. همچنین این مزیت را دارد که شما را مجبور به ایجاد نقاط ادغام واضح می کند، بنابراین دقیقاً می دانید کجا باید مراقب اعتبارسنجی ورودی و خروجی باشید. بیایید یک نمونه اسباب بازی را مرور کنیم تا ببینیم چگونه ممکن است کار کند.

Evalbox یک برنامه هیجان انگیز است که یک رشته را می گیرد و آن را به عنوان جاوا اسکریپت ارزیابی می کند. وای درسته؟ همان چیزی که در تمام این سال های طولانی منتظرش بودید. البته، این یک برنامه نسبتاً خطرناک است، زیرا اجازه دادن به جاوا اسکریپت دلخواه برای اجرای به این معنی است که تمام داده‌هایی که یک منبع ارائه می‌دهد قابل استفاده است. ما با اطمینان از اینکه کد در داخل یک جعبه ماسه‌بازی اجرا می‌شود، خطر وقوع چیزهای بد را کاهش می‌دهیم، که آن را تا حدی ایمن‌تر می‌کند. ما راه خود را از طریق کد از داخل به بیرون انجام می دهیم و از محتویات قاب شروع می کنیم:

<!-- frame.html -->
<!DOCTYPE html>
<html>
    <head>
    <title>Evalbox's Frame</title>
    <script>
        window.addEventListener('message', function (e) {
        var mainWindow = e.source;
        var result = '';
        try {
            result = eval(e.data);
        } catch (e) {
            result = 'eval() threw an exception.';
        }
        mainWindow.postMessage(result, event.origin);
        });
    </script>
    </head>
</html>

در داخل قاب، ما یک سند حداقل داریم که به سادگی با اتصال به رویداد message شی window ، به پیام‌های والد خود گوش می‌دهد. هر زمان که والد postMessage را روی محتویات iframe اجرا کند، این رویداد راه‌اندازی می‌شود و به ما امکان می‌دهد به رشته‌ای که والدین ما می‌خواهند اجرا کنیم، دسترسی پیدا کنیم.

در هندلر، ویژگی source رویداد را که پنجره والد است، می گیریم. پس از اتمام کار، از این برای ارسال مجدد نتیجه کار سخت خود استفاده خواهیم کرد. سپس با ارسال داده‌هایی که به eval() داده‌ایم، کارهای سنگین را انجام می‌دهیم. این تماس در یک بلوک try بسته شده است، زیرا عملیات ممنوعه در داخل یک iframe sandboxed اغلب استثناهای DOM را ایجاد می کند. ما آن‌ها را می‌گیریم و در عوض یک پیام خطای دوستانه گزارش می‌کنیم. در نهایت، نتیجه را به پنجره والد ارسال می کنیم. این چیز بسیار ساده ای است.

والدین نیز به همین ترتیب بدون عارضه هستند. ما یک رابط کاربری کوچک با یک textarea برای کد، و یک button برای اجرا ایجاد می‌کنیم، و از طریق یک iframe sandboxed، frame.html می‌کشیم و فقط امکان اجرای اسکریپت را فراهم می‌کند:

<textarea id='code'></textarea>
<button id='safe'>eval() in a sandboxed frame.</button>
<iframe sandbox='allow-scripts'
        id='sandboxed'
        src='frame.html'></iframe>

حالا ما چیزها را برای اجرا سیم کشی می کنیم. ابتدا به پاسخ‌های iframe گوش می‌دهیم و آنها را به کاربران خود alert() . احتمالاً یک برنامه واقعی کاری کمتر آزاردهنده انجام می دهد:

window.addEventListener('message',
    function (e) {
        // Sandboxed iframes which lack the 'allow-same-origin'
        // header have "null" rather than a valid origin. This means you still
        // have to be careful about accepting data via the messaging API you
        // create. Check that source, and validate those inputs!
        var frame = document.getElementById('sandboxed');
        if (e.origin === "null" &amp;&amp; e.source === frame.contentWindow)
        alert('Result: ' + e.data);
    });

در مرحله بعد، یک کنترل کننده رویداد را به کلیک روی button متصل می کنیم. هنگامی که کاربر کلیک می‌کند، محتویات فعلی textarea را می‌گیریم و آن‌ها را برای اجرا به کادر ارسال می‌کنیم:

function evaluate() {
    var frame = document.getElementById('sandboxed');
    var code = document.getElementById('code').value;
    // Note that we're sending the message to "*", rather than some specific
    // origin. Sandboxed iframes which lack the 'allow-same-origin' header
    // don't have an origin which you can target: you'll have to send to any
    // origin, which might alow some esoteric attacks. Validate your output!
    frame.contentWindow.postMessage(code, '*');
}

document.getElementById('safe').addEventListener('click', evaluate);

آسان است، درست است؟ ما یک API ارزیابی بسیار ساده ایجاد کرده‌ایم، و می‌توانیم مطمئن باشیم که کد ارزیابی شده به اطلاعات حساس مانند کوکی‌ها یا فضای ذخیره‌سازی DOM دسترسی ندارد. به همین ترتیب، کد ارزیابی شده نمی تواند پلاگین ها، پنجره های جدید یا هر یک از تعدادی از فعالیت های مزاحم یا مخرب دیگر را بارگیری کند.

شما می توانید همین کار را برای کد خود با شکستن برنامه های یکپارچه به اجزای تک منظوره انجام دهید. هر کدام را می‌توان در یک API پیام‌رسانی ساده قرار داد، درست مانند آنچه در بالا نوشتیم. پنجره والد با امتیاز بالا می تواند به عنوان یک کنترل کننده و توزیع کننده عمل کند و پیام ها را به ماژول های خاصی ارسال کند که هر کدام کمترین امتیاز ممکن را برای انجام کارهای خود دارند، به نتایج گوش می دهد و اطمینان حاصل می کند که هر ماژول فقط با اطلاعات مورد نیاز تغذیه می شود.

با این حال، توجه داشته باشید که هنگام برخورد با محتوای قاب شده ای که از همان منبع اصلی است، باید بسیار مراقب باشید. اگر صفحه‌ای در https://example.com/ صفحه دیگری را در همان مبدأ با جعبه‌ای sandbox قاب می‌کند که شامل پرچم‌های allow-same-origin و allow-scripts است، آن‌وقت صفحه قاب‌شده می‌تواند به والد برسد و ویژگی sandbox را به طور کامل حذف کند.

در جعبه شنی خود بازی کنید

Sandboxing اکنون در انواع مرورگرها برای شما در دسترس است: Firefox 17+، IE10+، و Chrome در زمان نگارش مقاله ( البته caniuse یک جدول پشتیبانی به روز دارد ). اعمال ویژگی sandbox به iframes که شامل می‌شوید به شما این امکان را می‌دهد که امتیازات خاصی را به محتوایی که نمایش می‌دهند، اعطا کنید، فقط آن امتیازهایی که برای عملکرد صحیح محتوا ضروری هستند. این به شما این فرصت را می‌دهد تا خطرات مرتبط با گنجاندن محتوای شخص ثالث را، بالاتر و فراتر از آنچه قبلاً با خط‌مشی امنیت محتوا ممکن است، کاهش دهید.

علاوه بر این، sandboxing یک تکنیک قدرتمند برای کاهش خطر این است که یک مهاجم باهوش بتواند از حفره‌های کد شما سوء استفاده کند. با تفکیک یک برنامه یکپارچه به مجموعه‌ای از سرویس‌های sandboxed، که هر کدام مسئول بخش کوچکی از عملکردهای مستقل هستند، مهاجمان مجبور می‌شوند نه تنها محتوای فریم‌های خاص، بلکه کنترل‌کننده‌شان را نیز به خطر بیندازند. این کار بسیار دشوارتر است، به ویژه از آنجایی که کنترلر می تواند تا حد زیادی از دامنه آن کاسته شود. اگر از مرورگر برای بقیه کمک بخواهید، می‌توانید تلاش‌های مرتبط با امنیت خود را صرف بررسی آن کد کنید.

این بدان معنا نیست که sandboxing یک راه حل کامل برای مشکل امنیت در اینترنت است. این سیستم دفاعی عمیق ارائه می‌کند و تا زمانی که کنترلی روی مشتریان کاربران خود نداشته باشید، هنوز نمی‌توانید به پشتیبانی مرورگر برای همه کاربران خود تکیه کنید (اگر مشتریان کاربران خود را کنترل می‌کنید - برای مثال یک محیط سازمانی - حیف!). روزی... اما در حال حاضر سندباکسینگ لایه دیگری از محافظت برای تقویت دفاع شماست، این یک دفاع کامل نیست که بتوانید تنها به آن تکیه کنید. با این حال، لایه ها عالی هستند. پیشنهاد میکنم از این یکی استفاده کنید

ادامه مطلب

  • " تفکیک امتیاز در برنامه های HTML5 " مقاله جالبی است که از طریق طراحی یک چارچوب کوچک و کاربرد آن در سه برنامه موجود HTML5 کار می کند.

  • هنگامی که با دو ویژگی جدید iframe ترکیب شود، Sandboxing می‌تواند انعطاف‌پذیرتر باشد: srcdoc و seamless . اولی به شما امکان می دهد یک فریم را با محتوا بدون سربار درخواست HTTP پر کنید، و دومی اجازه می دهد تا سبک به محتوای قاب شده جریان یابد. هر دو در حال حاضر پشتیبانی نسبتاً بدی از مرورگر دارند (شب‌های Chrome و WebKit). اما در آینده ترکیب جالبی خواهد بود. برای مثال می‌توانید از طریق کد زیر روی یک مقاله نظر جعبه شنی ارائه کنید:

        <iframe sandbox seamless
                srcdoc="<p>This is a user's comment!
                           It can't execute script!
                           Hooray for safety!</p>"></iframe>
    
،

Mike West

ایجاد یک تجربه غنی در وب امروزی تقریباً به طور اجتناب ناپذیری مستلزم جاسازی اجزا و محتوایی است که کنترل واقعی روی آنها ندارید. ویجت‌های شخص ثالث می‌توانند تعامل را افزایش دهند و نقش مهمی در تجربه کلی کاربر ایفا کنند، و محتوای تولید شده توسط کاربر گاهی حتی مهم‌تر از محتوای بومی سایت است. پرهیز از هر یک واقعاً یک گزینه نیست، اما هر دوی آنها خطر رخ دادن چیزی بد را در سایت شما افزایش می‌دهند. هر ویجتی که جاسازی می‌کنید - هر تبلیغ، هر ویجت رسانه‌های اجتماعی - یک بردار حمله بالقوه برای کسانی است که قصد مخرب دارند:

خط‌مشی امنیت محتوا (CSP) می‌تواند خطرات مربوط به هر دوی این نوع محتوا را با دادن توانایی در لیست سفید منابع خاص اسکریپت و سایر محتواها کاهش دهد. این یک گام بزرگ در مسیر درست است، اما شایان ذکر است که حفاظتی که اکثر دستورالعمل‌های CSP ارائه می‌دهند باینری است: منبع مجاز است یا مجاز نیست. مواقعی مفید است که بگوییم "مطمئن نیستم واقعاً به این منبع محتوا اعتماد دارم ، اما خیلی زیباست! لطفاً مرورگر، آن را جاسازی کنید، اما اجازه ندهید سایت من را خراب کند."

کمترین امتیاز

در اصل، ما به دنبال مکانیزمی هستیم که به ما امکان دهد محتوایی را که فقط حداقل سطح توانایی لازم برای انجام کار آن را تعبیه کرده ایم، اعطا کنیم. اگر یک ویجت نیازی به باز کردن یک پنجره جدید نداشته باشد، از بین بردن دسترسی به window.open ضرری ندارد. اگر به فلش نیاز ندارد، خاموش کردن پشتیبانی افزونه نباید مشکلی ایجاد کند. اگر از اصل حداقل امتیاز پیروی کنیم و تک تک ویژگی‌هایی را که مستقیماً به عملکردی که می‌خواهیم استفاده کنیم مرتبط نیستند، مسدود کنیم، تا آنجا که می‌توانیم ایمن هستیم. نتیجه این است که ما دیگر مجبور نیستیم کورکورانه اعتماد کنیم که برخی از محتوای جاسازی شده از امتیازاتی که نباید استفاده می‌کنند، استفاده نمی‌کنند. در وهله اول به سادگی به عملکرد دسترسی نخواهد داشت.

عناصر iframe اولین قدم به سمت یک چارچوب خوب برای چنین راه حلی هستند. بارگیری برخی از مؤلفه‌های نامعتبر در iframe معیاری از جدایی بین برنامه شما و محتوایی را که می‌خواهید بارگیری کنید، فراهم می‌کند. محتوای قاب شده به DOM صفحه شما یا داده‌هایی که به صورت محلی ذخیره کرده‌اید دسترسی نخواهد داشت، و همچنین نمی‌تواند به موقعیت‌های دلخواه در صفحه بکشد. محدوده آن به طرح کلی قاب محدود شده است. با این حال، جدایی واقعاً قوی نیست. صفحه موجود هنوز تعدادی گزینه برای رفتار آزاردهنده یا مخرب دارد: پخش خودکار ویدیو، افزونه‌ها و پنجره‌های بازشو نوک کوه یخ هستند.

ویژگی sandbox عنصر iframe دقیقاً آنچه را که برای تشدید محدودیت‌های محتوای فریم شده نیاز داریم به ما می‌دهد. ما می‌توانیم به مرورگر دستور دهیم که محتوای یک فریم خاص را در یک محیط با امتیاز پایین بارگذاری کند و تنها به زیرمجموعه‌ای از قابلیت‌های لازم برای انجام هر کاری که نیاز به انجام دارد اجازه دهد.

بچرخانید، اما تأیید کنید

دکمه «توییت» توییتر نمونه‌ای عالی از عملکردی است که می‌توان آن را با خیال راحت‌تر از طریق sandbox در سایت شما جاسازی کرد. توییتر به شما امکان می دهد دکمه را از طریق iframe با کد زیر جاسازی کنید :

<iframe src="https://platform.twitter.com/widgets/tweet_button.html"
        style="border: 0; width:130px; height:20px;"></iframe>

برای اینکه بفهمیم چه چیزی را می توانیم قفل کنیم، بیایید به دقت بررسی کنیم که دکمه به چه قابلیت هایی نیاز دارد. HTML که در فریم بارگذاری می‌شود، کمی جاوا اسکریپت را از سرورهای توییتر اجرا می‌کند و پس از کلیک کردن، یک پنجره بازشو ایجاد می‌کند که با یک رابط توییت پر شده است. این رابط برای اتصال توییت به حساب صحیح نیاز به دسترسی به کوکی‌های توییتر دارد و به توانایی ارسال فرم توییت نیاز دارد. تقریباً همین است. فریم نیازی به بارگذاری هیچ پلاگینی ندارد، نیازی به پیمایش در پنجره سطح بالا یا هر یک از تعدادی از عملکردهای دیگر ندارد. از آنجایی که به آن امتیازات نیازی ندارد، بیایید آنها را با جعبه شنی محتوای قاب حذف کنیم.

Sandboxing بر اساس یک لیست سفید کار می کند. ما با حذف همه مجوزهای ممکن شروع می کنیم و سپس با افزودن پرچم های خاص به پیکربندی جعبه شنی، قابلیت های فردی را دوباره فعال می کنیم. برای ویجت توییتر، ما تصمیم گرفته‌ایم جاوا اسکریپت، پنجره‌های بازشو، ارسال فرم و کوکی‌های twitter.com را فعال کنیم. ما می توانیم این کار را با افزودن یک ویژگی sandbox به iframe با مقدار زیر انجام دهیم:

<iframe sandbox="allow-same-origin allow-scripts allow-popups allow-forms"
    src="https://platform.twitter.com/widgets/tweet_button.html"
    style="border: 0; width:130px; height:20px;"></iframe>

همین است. ما تمام قابلیت‌هایی را که به فریم نیاز دارد داده‌ایم، و مرورگر به‌خوبی از دسترسی آن به هر یک از امتیازاتی که به‌صراحت از طریق مقدار ویژگی sandbox به آن اعطا نکرده‌ایم، جلوگیری می‌کند.

کنترل گرانول بر قابلیت ها

ما تعدادی از پرچم‌های sandboxing ممکن را در مثال بالا دیدیم، حالا اجازه دهید با کمی جزئیات بیشتر به بررسی عملکرد داخلی این ویژگی بپردازیم.

با توجه به iframe با ویژگی sandbox خالی، سند قاب شده به طور کامل sandbox می شود و آن را تحت محدودیت های زیر قرار می دهد:

  • جاوا اسکریپت در سند قاب شده اجرا نمی شود. این نه تنها شامل جاوا اسکریپت است که به صراحت از طریق تگ‌های اسکریپت بارگیری می‌شود، بلکه شامل کنترل‌کننده‌های رویداد درون خطی و جاوا اسکریپت: URL‌ها نیز می‌شود. این همچنین به این معنی است که محتوای موجود در برچسب‌های noscript نمایش داده می‌شود، دقیقاً همانطور که کاربر خود اسکریپت را غیرفعال کرده است.
  • سند قاب شده در یک مبدأ منحصربه‌فرد بارگذاری می‌شود، به این معنی که همه بررسی‌های یک منبع ناموفق خواهند بود. خاستگاه‌های منحصربه‌فرد با هیچ ریشه دیگری، حتی با خودشان همخوانی ندارند. از جمله تأثیرات دیگر، این بدان معناست که سند به داده‌های ذخیره شده در کوکی‌های منبع یا مکانیسم‌های ذخیره‌سازی دیگر (ذخیره‌سازی DOM، DB فهرست‌شده، و غیره) دسترسی ندارد.
  • سند قاب شده نمی تواند پنجره ها یا دیالوگ های جدیدی ایجاد کند (مثلاً از طریق window.open یا target="_blank" .
  • فرم ها قابل ارسال نیستند.
  • پلاگین ها بارگیری نمی شوند.
  • سند قاب شده فقط می تواند خودش را پیمایش کند، نه والد سطح بالای آن. تنظیم window.top.location یک استثنا ایجاد می کند و کلیک روی پیوند با target="_top" هیچ تاثیری نخواهد داشت.
  • ویژگی‌هایی که به‌طور خودکار فعال می‌شوند (عناصر فرم با فوکوس خودکار، پخش خودکار ویدیوها و غیره) مسدود می‌شوند.
  • قفل اشاره گر را نمی توان بدست آورد.
  • ویژگی seamless در iframes سند قاب شده حاوی آن است نادیده گرفته می شود.

این بسیار سخت است و یک سند بارگذاری شده در یک iframe کاملاً sandboxed خطر بسیار کمی دارد. البته، همچنین نمی‌تواند ارزش زیادی داشته باشد: ممکن است بتوانید از یک جعبه ماسه‌بازی کامل برای برخی از محتوای ثابت خلاص شوید، اما در بیشتر مواقع می‌خواهید کمی مسائل را شل کنید.

به استثنای پلاگین ها، هر یک از این محدودیت ها را می توان با افزودن یک پرچم به مقدار ویژگی sandbox برداشت. اسناد Sandboxed هرگز نمی‌توانند پلاگین‌ها را اجرا کنند، زیرا افزونه‌ها کدهای بومی بدون سندباکس هستند، اما هر چیز دیگری یک بازی منصفانه است:

  • allow-forms اجازه ارسال فرم را می دهد.
  • allow-popups اجازه می دهد (شوک!) پنجره های بازشو.
  • allow-pointer-lock اجازه می دهد (تعجب!) اشاره گر.
  • allow-same-origin به سند اجازه می دهد تا اصل خود را حفظ کند. صفحات بارگیری شده از https://example.com/ دسترسی به داده های آن مبدا را حفظ خواهند کرد.
  • allow-scripts به اجرای جاوا اسکریپت اجازه می دهد و همچنین به ویژگی ها اجازه می دهد تا به طور خودکار فعال شوند (زیرا اجرای آنها از طریق جاوا اسکریپت بی اهمیت است).
  • allow-top-navigation می دهد تا با پیمایش در پنجره سطح بالا، سند از کادر خارج شود.

با در نظر گرفتن این موارد، می‌توانیم دقیقاً ارزیابی کنیم که چرا به مجموعه خاصی از پرچم‌های sandboxing در مثال بالا توییتر رسیدیم:

  • allow-scripts مورد نیاز است، زیرا صفحه بارگذاری شده در فریم مقداری جاوا اسکریپت را برای مقابله با تعامل کاربر اجرا می کند.
  • allow-popups مورد نیاز است، زیرا دکمه یک فرم توییت را در یک پنجره جدید نمایش می‌دهد.
  • allow-forms مورد نیاز است، زیرا فرم توییت باید قابل ارسال باشد.
  • allow-same-origin ضروری است، زیرا کوکی‌های twitter.com در غیر این صورت غیرقابل دسترسی هستند و کاربر نمی‌تواند برای ارسال فرم وارد سیستم شود.

نکته مهمی که باید به آن توجه داشت این است که پرچم های sandboxing اعمال شده بر روی یک قاب برای هر پنجره یا قاب ایجاد شده در sandbox نیز اعمال می شود. این بدان معناست که ما باید allow-forms به جعبه شنی فریم اضافه کنیم، حتی اگر فرم فقط در پنجره‌ای وجود داشته باشد که فریم ظاهر می‌شود.

با وجود ویژگی sandbox ، ویجت فقط مجوزهای مورد نیاز خود را دریافت می کند و قابلیت هایی مانند پلاگین ها، پیمایش بالا و قفل اشاره گر مسدود می شوند. ما خطر جاسازی ویجت را کاهش داده‌ایم، بدون هیچ گونه تأثیر سوء. این یک پیروزی برای هر کسی است.

تفکیک امتیاز

سندباکس کردن محتوای شخص ثالث به منظور اجرای کدهای نامعتبر آنها در محیطی با امتیاز پایین کاملاً سودمند است. اما کد خودت چطور؟ به خودت اعتماد داری، درسته؟ پس چرا نگران سندباکسینگ باشید؟

من این سوال را برعکس می کنم: اگر کد شما به پلاگین نیاز ندارد، چرا به آن اجازه دسترسی به افزونه ها را بدهید؟ در بهترین حالت، این امتیازی است که هرگز از آن استفاده نمی‌کنید، در بدترین حالت، یک عامل بالقوه برای مهاجمان است که پا به درب بگذارند. کد همه دارای اشکالاتی است و عملاً هر برنامه ای به نوعی در برابر سوء استفاده آسیب پذیر است. Sandboxing کد خود به این معنی است که حتی اگر یک مهاجم با موفقیت برنامه شما را زیر و رو کند، به آنها دسترسی کامل به مبدا برنامه داده نخواهد شد. آنها فقط می توانند کارهایی را انجام دهند که برنامه می تواند انجام دهد. هنوز هم بد است، اما نه به آن بدی که می تواند باشد.

شما می توانید با تقسیم برنامه خود به قطعات منطقی و جعبه شنی هر قطعه با حداقل امتیاز ممکن، خطر را حتی بیشتر کاهش دهید. این تکنیک در کدهای بومی بسیار رایج است: برای مثال کروم خود را به یک فرآیند مرورگر با امتیاز بالا که به هارد دیسک محلی دسترسی دارد و می‌تواند اتصالات شبکه برقرار کند، تجزیه می‌کند و بسیاری از فرآیندهای رندر با امتیاز پایین که تجزیه محتوای غیرقابل اعتماد را انجام می‌دهند. رندرها نیازی به لمس دیسک ندارند، مرورگر مراقب است که تمام اطلاعاتی را که برای رندر کردن صفحه نیاز دارند به آنها بدهد. حتی اگر یک هکر باهوش راهی برای فاسد کردن یک رندر پیدا کند، هنوز خیلی پیشرفت نکرده است، زیرا رندر نمی‌تواند به تنهایی کارهای زیادی را انجام دهد: تمام دسترسی‌های با امتیاز بالا باید از طریق فرآیند مرورگر هدایت شوند. مهاجمان باید چندین سوراخ در قطعات مختلف سیستم پیدا کنند تا هر گونه آسیبی را وارد کنند، که خطر موفقیت آمیز pwnage را بسیار کاهش می دهد.

eval() sandboxing امن

با sandboxing و postMessage API ، موفقیت این مدل برای اعمال در وب نسبتاً ساده است. بخش‌هایی از برنامه شما می‌توانند در iframe های sandboxed قرار بگیرند و سند والد می‌تواند با ارسال پیام‌ها و گوش دادن به پاسخ‌ها، ارتباط بین آنها را واسطه‌ای کند. این نوع ساختار تضمین می کند که اکسپلویت ها در هر قسمت از برنامه حداقل آسیب ممکن را وارد می کنند. همچنین این مزیت را دارد که شما را مجبور به ایجاد نقاط ادغام واضح می کند، بنابراین دقیقاً می دانید کجا باید مراقب اعتبارسنجی ورودی و خروجی باشید. بیایید یک نمونه اسباب بازی را مرور کنیم تا ببینیم چگونه ممکن است کار کند.

Evalbox یک برنامه هیجان انگیز است که یک رشته را می گیرد و آن را به عنوان جاوا اسکریپت ارزیابی می کند. وای درسته؟ همان چیزی که در تمام این سال های طولانی منتظرش بودید. البته، این یک برنامه نسبتاً خطرناک است، زیرا اجازه دادن به جاوا اسکریپت دلخواه برای اجرای به این معنی است که تمام داده‌هایی که یک منبع ارائه می‌دهد قابل استفاده است. ما با اطمینان از اینکه کد در داخل یک جعبه ماسه‌بازی اجرا می‌شود، خطر وقوع چیزهای بد را کاهش می‌دهیم، که آن را تا حدی ایمن‌تر می‌کند. ما راه خود را از طریق کد از داخل به بیرون انجام می دهیم و از محتویات قاب شروع می کنیم:

<!-- frame.html -->
<!DOCTYPE html>
<html>
    <head>
    <title>Evalbox's Frame</title>
    <script>
        window.addEventListener('message', function (e) {
        var mainWindow = e.source;
        var result = '';
        try {
            result = eval(e.data);
        } catch (e) {
            result = 'eval() threw an exception.';
        }
        mainWindow.postMessage(result, event.origin);
        });
    </script>
    </head>
</html>

در داخل قاب، ما یک سند حداقل داریم که به سادگی با اتصال به رویداد message شی window ، به پیام‌های والد خود گوش می‌دهد. هر زمان که والد postMessage را روی محتویات iframe اجرا کند، این رویداد راه‌اندازی می‌شود و به ما امکان می‌دهد به رشته‌ای که والدین ما می‌خواهند اجرا کنیم، دسترسی پیدا کنیم.

در هندلر، ویژگی source رویداد را که پنجره والد است، می گیریم. پس از اتمام کار، از این برای ارسال مجدد نتیجه کار سخت خود استفاده خواهیم کرد. سپس با ارسال داده‌هایی که به eval() داده‌ایم، کارهای سنگین را انجام می‌دهیم. این تماس در یک بلوک try بسته شده است، زیرا عملیات ممنوعه در داخل یک iframe sandboxed اغلب استثناهای DOM را ایجاد می کند. ما آن‌ها را می‌گیریم و در عوض یک پیام خطای دوستانه گزارش می‌کنیم. در نهایت، نتیجه را به پنجره والد ارسال می کنیم. این چیز بسیار ساده ای است.

والدین نیز به همین ترتیب بدون عارضه هستند. ما یک رابط کاربری کوچک با یک textarea برای کد، و یک button برای اجرا ایجاد می‌کنیم، و از طریق یک iframe sandboxed، frame.html می‌کشیم و فقط امکان اجرای اسکریپت را فراهم می‌کند:

<textarea id='code'></textarea>
<button id='safe'>eval() in a sandboxed frame.</button>
<iframe sandbox='allow-scripts'
        id='sandboxed'
        src='frame.html'></iframe>

حالا ما چیزها را برای اجرا سیم کشی می کنیم. ابتدا به پاسخ‌های iframe گوش می‌دهیم و آنها را به کاربران خود alert() . احتمالاً یک برنامه واقعی کاری کمتر آزاردهنده انجام می دهد:

window.addEventListener('message',
    function (e) {
        // Sandboxed iframes which lack the 'allow-same-origin'
        // header have "null" rather than a valid origin. This means you still
        // have to be careful about accepting data via the messaging API you
        // create. Check that source, and validate those inputs!
        var frame = document.getElementById('sandboxed');
        if (e.origin === "null" &amp;&amp; e.source === frame.contentWindow)
        alert('Result: ' + e.data);
    });

در مرحله بعد، یک کنترل کننده رویداد را به کلیک روی button متصل می کنیم. هنگامی که کاربر کلیک می‌کند، محتویات فعلی textarea را می‌گیریم و آن‌ها را برای اجرا به کادر ارسال می‌کنیم:

function evaluate() {
    var frame = document.getElementById('sandboxed');
    var code = document.getElementById('code').value;
    // Note that we're sending the message to "*", rather than some specific
    // origin. Sandboxed iframes which lack the 'allow-same-origin' header
    // don't have an origin which you can target: you'll have to send to any
    // origin, which might alow some esoteric attacks. Validate your output!
    frame.contentWindow.postMessage(code, '*');
}

document.getElementById('safe').addEventListener('click', evaluate);

آسان است، درست است؟ ما یک API ارزیابی بسیار ساده ایجاد کرده‌ایم، و می‌توانیم مطمئن باشیم که کد ارزیابی شده به اطلاعات حساس مانند کوکی‌ها یا فضای ذخیره‌سازی DOM دسترسی ندارد. به همین ترتیب، کد ارزیابی شده نمی تواند پلاگین ها، پنجره های جدید یا هر یک از تعدادی از فعالیت های مزاحم یا مخرب دیگر را بارگیری کند.

شما می توانید همین کار را برای کد خود با شکستن برنامه های یکپارچه به اجزای تک منظوره انجام دهید. هر کدام را می‌توان در یک API پیام‌رسانی ساده قرار داد، درست مانند آنچه در بالا نوشتیم. پنجره والد با امتیاز بالا می تواند به عنوان یک کنترل کننده و توزیع کننده عمل کند و پیام ها را به ماژول های خاصی ارسال کند که هر کدام کمترین امتیاز ممکن را برای انجام کارهای خود دارند، به نتایج گوش می دهد و اطمینان حاصل می کند که هر ماژول فقط با اطلاعات مورد نیاز تغذیه می شود.

با این حال، توجه داشته باشید که هنگام برخورد با محتوای قاب شده ای که از همان منبع اصلی است، باید بسیار مراقب باشید. اگر صفحه‌ای در https://example.com/ صفحه دیگری را در همان مبدأ با جعبه‌ای sandbox قاب می‌کند که شامل پرچم‌های allow-same-origin و allow-scripts است، آن‌وقت صفحه قاب‌شده می‌تواند به والد برسد و ویژگی sandbox را به طور کامل حذف کند.

در جعبه شنی خود بازی کنید

Sandboxing اکنون در انواع مرورگرها برای شما در دسترس است: Firefox 17+، IE10+، و Chrome در زمان نگارش مقاله ( البته caniuse یک جدول پشتیبانی به روز دارد ). اعمال ویژگی sandbox به iframes که شامل می‌شوید به شما این امکان را می‌دهد که امتیازات خاصی را به محتوایی که نمایش می‌دهند، اعطا کنید، فقط آن امتیازهایی که برای عملکرد صحیح محتوا ضروری هستند. این به شما این فرصت را می‌دهد تا خطرات مرتبط با گنجاندن محتوای شخص ثالث را، بالاتر و فراتر از آنچه قبلاً با خط‌مشی امنیت محتوا ممکن است، کاهش دهید.

علاوه بر این، sandboxing یک تکنیک قدرتمند برای کاهش خطر این است که یک مهاجم باهوش بتواند از حفره‌های کد شما سوء استفاده کند. با تفکیک یک برنامه یکپارچه به مجموعه‌ای از سرویس‌های sandboxed، که هر کدام مسئول بخش کوچکی از عملکردهای مستقل هستند، مهاجمان مجبور می‌شوند نه تنها محتوای فریم‌های خاص، بلکه کنترل‌کننده‌شان را نیز به خطر بیندازند. این کار بسیار دشوارتر است، به ویژه از آنجایی که کنترلر می تواند تا حد زیادی از دامنه آن کاسته شود. اگر از مرورگر برای بقیه کمک بخواهید، می‌توانید تلاش‌های مرتبط با امنیت خود را صرف بررسی آن کد کنید.

این بدان معنا نیست که sandboxing یک راه حل کامل برای مشکل امنیت در اینترنت است. این سیستم دفاعی عمیق ارائه می‌کند و تا زمانی که کنترلی روی مشتریان کاربران خود نداشته باشید، هنوز نمی‌توانید به پشتیبانی مرورگر برای همه کاربران خود تکیه کنید (اگر مشتریان کاربران خود را کنترل می‌کنید - برای مثال یک محیط سازمانی - حیف!). روزی... اما در حال حاضر سندباکسینگ لایه دیگری از محافظت برای تقویت دفاع شماست، این یک دفاع کامل نیست که بتوانید تنها به آن تکیه کنید. با این حال، لایه ها عالی هستند. پیشنهاد میکنم از این یکی استفاده کنید

ادامه مطلب

  • " تفکیک امتیاز در برنامه های HTML5 " مقاله جالبی است که از طریق طراحی یک چارچوب کوچک و کاربرد آن در سه برنامه موجود HTML5 کار می کند.

  • ماسهبازی در هنگام ترکیب با دو ویژگی جدید Iframe دیگر می تواند انعطاف پذیر تر باشد: srcdoc و seamless . اولی به شما امکان می دهد یک قاب را با محتوا بدون سرپرستی درخواست HTTP جمع کنید و دومی به سبک اجازه می دهد تا به محتوای قاب بندی شده جریان یابد. هر دو در حال حاضر از مرورگر نسبتاً بدبختی برخوردار هستند (شبانها Chrome و WebKit). اما در آینده یک ترکیب جالب خواهد بود. به عنوان مثال ، می توانید از طریق کد زیر درباره یک مقاله در مورد مقاله اظهار نظر کنید:

        <iframe sandbox seamless
                srcdoc="<p>This is a user's comment!
                           It can't execute script!
                           Hooray for safety!</p>"></iframe>
    
،

Mike West

ساختن یک تجربه غنی در وب امروز تقریباً به طور اجتناب ناپذیر شامل تعبیه مؤلفه ها و محتوایی است که بر آنها کنترل واقعی ندارید. ویجت های شخص ثالث می توانند تعامل را هدایت کرده و نقش مهمی در تجربه کلی کاربر داشته باشند و محتوای تولید شده توسط کاربر گاهی اوقات حتی از محتوای بومی سایت نیز مهمتر است. ممتنع از هر یک از آنها واقعاً گزینه ای نیست ، اما هر دو خطر را افزایش می دهند که ممکن است اتفاق بدی در سایت شما رخ دهد. هر ویجت که شما تعبیه کرده اید - هر تبلیغ ، هر ویجت رسانه های اجتماعی - یک بردار حمله بالقوه برای کسانی است که با هدف مخرب:

خط مشی امنیت محتوا (CSP) می تواند خطرات مرتبط با هر دو نوع محتوا را با این امکان که به شما در لیست سفید کردن منابع خاص اسکریپت و سایر مطالب ، به شما کاهش دهد ، کاهش دهد. این یک گام بزرگ در جهت درست است ، اما شایان ذکر است که حفاظتی که بیشتر دستورالعمل های CSP ارائه می دهند باینری است: منبع مجاز است ، یا اینگونه نیست. مواقعی وجود دارد که می توان گفت: "من مطمئن نیستم که واقعاً به این منبع محتوا اعتماد دارم ، اما خیلی زیبا است! لطفاً آن را تعبیه کنید ، مرورگر ، اما اجازه ندهید سایت من را بشکند."

کمترین امتیاز

در اصل ، ما به دنبال مکانیسمی هستیم که به ما امکان اعطای محتوا را می دهد که فقط حداقل سطح توانایی لازم برای انجام کار خود را تعبیه کرده ایم. اگر ویجت نیازی به ظاهر شدن یک پنجره جدید ندارد ، دسترسی به Window.Open نمی تواند صدمه ببیند. اگر نیازی به فلاش نداشته باشد ، خاموش کردن پشتیبانی از افزونه نباید مشکلی باشد. اگر ما از اصل کمترین امتیاز پیروی کنیم ، ایمن هستیم و هر ویژگی را که مستقیماً به عملکردی که دوست داریم از آن استفاده کنیم ، مسدود می کنیم. نتیجه این است که ما دیگر لازم نیست کورکورانه اعتماد کنیم که برخی از محتوای تعبیه شده از امتیازاتی که نباید از آن استفاده کند استفاده نمی کند. در وهله اول به راحتی به عملکرد دسترسی نخواهد داشت.

عناصر iframe اولین قدم به سوی یک چارچوب خوب برای چنین راه حلی هستند. بارگیری برخی از مؤلفه های غیرقابل اعتماد در یک iframe ، اندازه گیری جدایی بین برنامه شما و محتوای مورد نظر برای بارگیری را فراهم می کند. محتوای قاب بندی شده به DOM صفحه شما یا داده هایی که به صورت محلی ذخیره کرده اید دسترسی نخواهد داشت ، و همچنین قادر به جلب موقعیت های دلخواه در صفحه نیست. این محدوده محدود به طرح فریم است. جدایی واقعاً قوی نیست. صفحه موجود هنوز هم گزینه های مختلفی برای رفتار آزار دهنده یا مخرب دارد: ویدیوی خودکار ، افزونه ها و پنجره ها نوک کوه یخ هستند.

ویژگی sandbox از عنصر iframe فقط آنچه را که برای محکم کردن محدودیت های مربوط به محتوای قاب شده نیاز داریم ، به ما می دهد. ما می توانیم به مرورگر دستور دهیم که محتوای یک قاب خاص را در یک محیط کم ارزش بارگذاری کند و فقط به زیر مجموعه قابلیت های لازم برای انجام هر کاری که نیاز به انجام دارد ، می دهد.

Twust ، اما تأیید کنید

دکمه "توییت" توییتر نمونه ای عالی از عملکرد است که می تواند با خیال راحت تر از طریق ماسهبازی در سایت شما تعبیه شود. توییتر به شما امکان می دهد دکمه را از طریق iframe با کد زیر جاسازی کنید :

<iframe src="https://platform.twitter.com/widgets/tweet_button.html"
        style="border: 0; width:130px; height:20px;"></iframe>

برای اینکه بفهمیم چه چیزی را می توانیم قفل کنیم ، بیایید با دقت بررسی کنیم که این دکمه به چه قابلیت هایی نیاز دارد. HTML که در قاب بارگذاری شده است ، کمی از جاوا اسکریپت را از سرورهای توییتر اجرا می کند و هنگام کلیک ، یک پنجره پرجمعیت را با رابط توییت ایجاد می کند. این رابط به منظور پیوند دادن توییت به حساب صحیح ، نیاز به دسترسی به کوکی های توییتر دارد و به توانایی ارسال فرم توییت نیاز دارد. تقریباً همین است. این قاب نیازی به بارگذاری افزونه ها ندارد ، نیازی به حرکت در پنجره سطح بالا یا هر یک از تعدادی از عملکردهای دیگر نیست. از آنجا که به آن امتیازات احتیاج ندارد ، بیایید آنها را با ماسه زدن محتوای قاب حذف کنیم.

ماسهبازی بر اساس یک لیست سفید کار می کند. ما با از بین بردن تمام مجوزهای ممکن شروع می کنیم و سپس با اضافه کردن پرچم های خاص به پیکربندی Sandbox ، قابلیت های فردی را دوباره روشن می کنیم. برای ویجت توییتر ، ما تصمیم گرفتیم JavaScript ، Popups ، Form Form و کوکی های Twitter.com را فعال کنیم. ما می توانیم با اضافه کردن یک ویژگی sandbox به iframe با مقدار زیر این کار را انجام دهیم:

<iframe sandbox="allow-same-origin allow-scripts allow-popups allow-forms"
    src="https://platform.twitter.com/widgets/tweet_button.html"
    style="border: 0; width:130px; height:20px;"></iframe>

همین است. ما تمام قابلیت های مورد نیاز خود را به این قاب داده ایم و مرورگر با کمک آن به هر یک از امتیازاتی که ما صریحاً آن را از طریق ارزش ویژگی sandbox اعطا نکردیم ، به شما کمک می کند.

کنترل گرانول بر روی قابلیت ها

ما چند مورد از پرچم های ماسهبازی احتمالی را در مثال بالا دیدیم ، بیایید با جزئیات بیشتری با جزئیات بیشتر از این ویژگی استفاده کنیم.

با توجه به یک IFRAME با یک ویژگی ماسه ای خالی ، سند قاب بندی شده کاملاً ماسهبازی خواهد شد و آن را مطابق با محدودیت های زیر قرار می دهد:

  • JavaScript در سند قاب اجرا نمی شود. این نه تنها شامل JavaScript صریحاً از طریق برچسب های اسکریپت بارگیری می شود ، بلکه دستگیرندگان رویداد Inline و JavaScript: URL ها نیز هستند. این همچنین بدان معنی است که محتوای موجود در برچسب های Noscript نمایش داده می شود ، دقیقاً به نظر می رسد که کاربر خودش اسکریپت را غیرفعال کرده است.
  • سند قاب بندی شده به یک منشأ منحصر به فرد بارگیری می شود ، به این معنی که تمام چک های همان منبعی شکست می خورند. منشأ های ​​منحصر به فرد با هیچ ریشه دیگری مطابقت ندارند ، حتی خودشان. از دیگر تأثیرات ، این بدان معنی است که این سند دسترسی به داده های ذخیره شده در کوکی های Origin یا هر مکانیزم ذخیره سازی دیگر (ذخیره سازی DOM ، DB فهرست بندی شده و غیره) ندارد.
  • سند قاب بندی شده نمی تواند ویندوز یا دیالوگ های جدیدی ایجاد کند (از طریق window.open یا target="_blank" ، به عنوان مثال).
  • فرم ها نمی توانند ارسال شوند.
  • افزونه ها بارگیری نمی شوند.
  • سند قاب فقط می تواند خود را حرکت کند ، نه والدین سطح بالا. تنظیم window.top.location یک استثنا را پرتاب می کند و با کلیک بر روی پیوند با target="_top" هیچ تاثیری نخواهد داشت.
  • ویژگی هایی که به طور خودکار تحریک می شوند (عناصر فرم فوکوس خودکار ، فیلم های خودکار و غیره) مسدود می شوند.
  • قفل نشانگر قابل دستیابی نیست.
  • ویژگی seamless در iframes که سند قاب بندی شده حاوی آن نادیده گرفته می شود.

این به خوبی Draconian است ، و سندی که در یک iframe کاملاً ماسهبازی قرار گرفته است ، خطر بسیار کمی دارد. البته ، همچنین نمی تواند ارزش زیادی انجام دهد: ممکن است شما بتوانید با یک جعبه ماسه ای کامل برای برخی از مطالب استاتیک دور شوید ، اما بیشتر اوقات می خواهید کمی چیزها را شل کنید.

به استثنای افزونه ها ، هر یک از این محدودیت ها را می توان با اضافه کردن پرچم به مقدار ویژگی Sandbox برداشت. اسناد Sandboxed هرگز نمی توانند افزونه ها را اجرا کنند ، زیرا افزونه ها کد بومی بدون جعبه هستند ، اما همه چیز دیگر بازی عادلانه است:

  • allow-forms اجازه می دهد تا فرم ارسال شود.
  • allow-popups اجازه می دهد (شوک!) پنجره ها.
  • allow-pointer-lock اجازه می دهد تا قفل نشانگر (تعجب آور!).
  • allow-same-origin به سند اجازه می دهد تا منشأ خود را حفظ کند. صفحات بارگذاری شده از https://example.com/ دسترسی به داده های آن Origin را حفظ می کنند.
  • allow-scripts اجازه اجرای JavaScript را می دهد ، و همچنین به ویژگی ها اجازه می دهد تا به صورت خودکار تحریک شوند (زیرا آنها برای اجرای از طریق JavaScript بی اهمیت هستند).
  • allow-top-navigation اجازه می دهد تا با پیمایش در پنجره سطح بالا ، سند از قاب خارج شود.

با توجه به این موارد ، ما می توانیم دقیقاً ارزیابی کنیم که چرا ما با مجموعه خاصی از پرچم های ماسهبازی در مثال توییتر به پایان رسیدیم:

  • allow-scripts مورد نیاز است ، زیرا صفحه بارگذاری شده در قاب برخی از JavaScript را برای مقابله با تعامل کاربر اجرا می کند.
  • allow-popups نیاز است ، زیرا دکمه در یک پنجره جدید فرم صدای جیر جیر را ظاهر می کند.
  • allow-forms مورد نیاز است ، زیرا فرم توییت باید قابل ارسال باشد.
  • allow-same-origin لازم باشد ، زیرا کوکی های Twitter.com در غیر این صورت غیرقابل دسترسی خواهند بود و کاربر نمی تواند برای ارسال فرم وارد سیستم شود.

نکته مهمی که باید به آن توجه داشت این است که پرچم های ماسهبازی که روی یک قاب اعمال می شود نیز برای هر ویندوز یا قاب های ایجاد شده در ماسهبازی اعمال می شود. این بدان معنی است که ما باید allow-forms به جعبه قاب اضافه کنیم ، حتی اگر فرم فقط در پنجره ای که قاب ظاهر می شود وجود داشته باشد.

با استفاده از ویژگی sandbox در محل ، ویجت فقط مجوزهای مورد نیاز خود را دریافت می کند و قابلیت هایی مانند افزونه ها ، ناوبری بالا و قفل نشانگر مسدود می شود. ما خطر تعبیه ویجت را کاهش داده ایم ، بدون عوارض. این یک پیروزی برای همه افراد نگران است.

جدایی امتیاز

برای اجرای کد غیر قابل اعتماد خود در یک محیط کم ارزش ، ماسهبازی محتوای شخص ثالث به منظور اجرای کد غیر قابل اعتماد خود ، کاملاً آشکار است. اما در مورد کد خودت چطور؟ به خودت اعتماد داری ، درست است؟ پس چرا نگران ماسهبازی هستید؟

من این سؤال را به وجود می آورم: اگر کد شما به افزونه ها احتیاج ندارد ، چرا به افزونه ها دسترسی پیدا می کنید؟ در بهترین حالت ، این یک امتیاز است که شما هرگز از آن استفاده نمی کنید ، در بدترین حالت این یک بردار بالقوه برای مهاجمان است که بتوانند پا را در درب بگیرند. کد همه دارای اشکالات است و تقریباً هر برنامه کاربردی در برابر بهره برداری از یک طریق یا روش دیگر آسیب پذیر است. ماسهبازی کد شخصی شما به این معنی است که حتی اگر یک مهاجم با موفقیت برنامه شما را خراب کند ، به آنها دسترسی کامل به منشأ برنامه داده نمی شود. آنها فقط قادر به انجام کارهایی هستند که برنامه می تواند انجام دهد. هنوز هم بد است ، اما به همان اندازه که ممکن است بد نیست.

شما می توانید با شکستن برنامه خود به قطعات منطقی و ماسهبازی هر قطعه با حداقل امتیاز ممکن ، خطر را حتی بیشتر کاهش دهید. این تکنیک در کد بومی بسیار متداول است: به عنوان مثال ، Chrome خود را به یک فرایند مرورگر با ارزش بالا که به هارد دیسک محلی دسترسی دارد ، می شکند و می تواند اتصالات شبکه را ایجاد کند ، و بسیاری از فرآیندهای رندر کم ارزش که باعث افزایش سنگین تجزیه محتوای نامعقول می شود. رندرها نیازی به لمس دیسک ندارند ، مرورگر مراقبت می کند تا تمام اطلاعات مورد نیاز خود را برای ارائه یک صفحه به آنها بدهد. حتی اگر یک هکر هوشمند راهی برای فاسد کردن یک رندر پیدا کند ، او خیلی دور نشده است ، زیرا رندر نمی تواند علاقه زیادی به خود داشته باشد: تمام دسترسی با ارزش بالا باید از طریق فرایند مرورگر مسیریابی شود. مهاجمان برای انجام هرگونه خسارت باید چندین سوراخ در قطعات مختلف سیستم پیدا کنند که این امر خطر ابتلا به pwnage موفق را به شدت کاهش می دهد.

با خیال راحت ماسه eval()

با وجود ماسهبازی و API postMessage ، موفقیت این مدل نسبتاً ساده است که در وب اعمال می شود. بخش هایی از برنامه شما می توانند در iframe S Sandboxed زندگی کنند ، و سند والدین می تواند با ارسال پیام و گوش دادن به پاسخ ، ارتباط بین آنها را انجام دهد. این نوع ساختار تضمین می کند که سوء استفاده در هر یک از برنامه ها حداقل آسیب ممکن را انجام می دهد. همچنین این مزیت را دارد که شما را مجبور به ایجاد نقاط ادغام واضح کنید ، بنابراین دقیقاً می دانید که در مورد اعتبارسنجی ورودی و خروجی باید مراقب باشید. بیایید یک نمونه اسباب بازی را طی کنیم ، فقط برای دیدن اینکه چگونه ممکن است کار کند.

Evalbox یک برنامه هیجان انگیز است که یک رشته را می گیرد و آن را به عنوان JavaScript ارزیابی می کند. وای درسته؟ فقط آنچه در این سالهای طولانی منتظر بوده اید. البته این یک کاربرد نسبتاً خطرناک است ، زیرا اجازه می دهد JavaScript دلخواه برای اجرای آن به معنای این باشد که هر و تمام داده هایی که یک مبدا ارائه می دهد برای گرفتن مناسب است. ما با اطمینان از اجرای کد در داخل ماسهبازی ، خطر ابتلا به اتفاقات بد را کاهش خواهیم داد. ما با شروع محتوای قاب ، راه خود را از طریق کد از داخل کار می کنیم:

<!-- frame.html -->
<!DOCTYPE html>
<html>
    <head>
    <title>Evalbox's Frame</title>
    <script>
        window.addEventListener('message', function (e) {
        var mainWindow = e.source;
        var result = '';
        try {
            result = eval(e.data);
        } catch (e) {
            result = 'eval() threw an exception.';
        }
        mainWindow.postMessage(result, event.origin);
        });
    </script>
    </head>
</html>

در داخل قاب ، ما یک سند حداقل داریم که با اتصال به رویداد message شیء window ، پیام های والدین خود را به سادگی گوش می دهد. هر زمان که والدین در محتوای Iframe پست را انجام دهند ، این رویداد باعث می شود که به ما دسترسی داشته باشیم و به رشته ای دسترسی پیدا کنیم که والدین ما دوست دارند ما را اجرا کنیم.

در کنترل کننده ، ویژگی source این رویداد را که پنجره والدین است ، می گیریم. ما از این استفاده خواهیم کرد تا نتیجه کار سخت خود را پس از اتمام انجام دهیم. سپس با عبور از داده هایی که به ما داده شده است eval() ، وزنه برداری سنگین را انجام خواهیم داد. این تماس در یک محوطه آزمایشی پیچیده شده است ، زیرا عملیات ممنوعه در داخل یک iframe ماسهبازی اغلب استثنائات DOM ایجاد می کند. ما آنها را می گیریم و به جای آن یک پیام خطای دوستانه گزارش خواهیم داد. سرانجام ، نتیجه را دوباره به پنجره والدین ارسال می کنیم. این چیزهای کاملاً ساده است.

والدین به همین ترتیب بدون عارضه هستند. ما یک UI کوچک با یک textarea برای کد و یک button برای اجرای ایجاد خواهیم کرد و ما از طریق یک iframe Sandboxed به frame.html می کشیم و فقط اجرای اسکریپت را می دهیم:

<textarea id='code'></textarea>
<button id='safe'>eval() in a sandboxed frame.</button>
<iframe sandbox='allow-scripts'
        id='sandboxed'
        src='frame.html'></iframe>

اکنون ما برای اعدام کارها را سیم می کنیم. ابتدا ما به پاسخ های iframe و alert() آنها به کاربران خود گوش خواهیم داد. احتمالاً یک برنامه واقعی می تواند کاری کمتر آزار دهنده انجام دهد:

window.addEventListener('message',
    function (e) {
        // Sandboxed iframes which lack the 'allow-same-origin'
        // header have "null" rather than a valid origin. This means you still
        // have to be careful about accepting data via the messaging API you
        // create. Check that source, and validate those inputs!
        var frame = document.getElementById('sandboxed');
        if (e.origin === "null" &amp;&amp; e.source === frame.contentWindow)
        alert('Result: ' + e.data);
    });

در مرحله بعد ، ما یک کنترل کننده رویداد را با کلیک روی button وصل خواهیم کرد. هنگامی که کاربر کلیک می کند ، ما محتوای فعلی textarea را می گیریم و آنها را برای اجرای به داخل قاب منتقل می کنیم:

function evaluate() {
    var frame = document.getElementById('sandboxed');
    var code = document.getElementById('code').value;
    // Note that we're sending the message to "*", rather than some specific
    // origin. Sandboxed iframes which lack the 'allow-same-origin' header
    // don't have an origin which you can target: you'll have to send to any
    // origin, which might alow some esoteric attacks. Validate your output!
    frame.contentWindow.postMessage(code, '*');
}

document.getElementById('safe').addEventListener('click', evaluate);

آسان است، درست است؟ ما یک API ارزیابی بسیار ساده ایجاد کرده ایم و می توانیم مطمئن باشیم که کدی که ارزیابی شده است به اطلاعات حساس مانند کوکی ها یا ذخیره سازی DOM دسترسی ندارد. به همین ترتیب ، کد ارزیابی شده نمی تواند افزونه ها ، پنجره های جدید یا هر یک از تعدادی دیگر از فعالیت های آزار دهنده یا مخرب را بارگذاری کند.

شما می توانید با شکستن برنامه های یکپارچه به اجزای یک منظوره ، همین کار را برای کد خود انجام دهید. هر کدام را می توان در یک API پیام رسانی ساده ، دقیقاً مانند آنچه در بالا نوشتیم ، پیچیده کنید. پنجره والدین با ارزش بالا می تواند به عنوان یک کنترل کننده و توزیع کننده عمل کند و پیام هایی را به ماژول های خاص ارسال می کند که هرکدام کمترین امتیاز ممکن برای انجام کارهای خود ، گوش دادن به نتایج را دارند و اطمینان حاصل می کنند که هر ماژول فقط با اطلاعات مورد نیاز خود تغذیه می شود.

با این حال ، توجه داشته باشید که شما باید هنگام برخورد با محتوای قاب که از همان منشأ والدین ناشی می شود ، بسیار مراقب باشید. اگر صفحه ای در https://example.com/ صفحه دیگری را در همان مبدأ با یک جعبه ماسه ای که شامل هر دو پرچم-Origin است و به پرچم های اسکریپت اجازه می دهد ، قاب می کند ، آنگاه صفحه قاب می تواند به والدین برسد و ویژگی Sandbox را به طور کامل حذف کند.

در ماسهبازی خود بازی کنید

Sandboxing اکنون در انواع مرورگرها برای شما در دسترس است: Firefox 17+ ، IE10+و Chrome در زمان نوشتن ( البته Caniuse دارای یک جدول پشتیبانی به روز است ). استفاده از ویژگی sandbox در iframes که در آن قرار دارد ، به شما امکان می دهد امتیازات خاصی را به محتوایی که نشان می دهند اعطا کنید ، فقط آن امتیازاتی که برای عملکرد صحیح محتوا لازم است. این به شما این فرصت را می دهد تا خطر مرتبط با درج محتوای شخص ثالث ، بالاتر و فراتر از آنچه در حال حاضر با سیاست امنیت محتوا امکان پذیر است ، کاهش دهید.

علاوه بر این ، Sandboxing یک تکنیک قدرتمند برای کاهش این خطر است که یک مهاجم باهوش قادر به سوءاستفاده از سوراخ ها در کد شخصی شما باشد. با جدا کردن یک برنامه یکپارچه به مجموعه ای از خدمات ماسهبازی ، که هر یک از آنها مسئول یک قطعه کوچک از قابلیت های خود هستند ، مهاجمان مجبور می شوند نه تنها محتوای قاب های خاص را به خطر بیاندازند ، بلکه کنترل کننده آنها نیز هستند. این یک کار بسیار دشوارتر است ، به خصوص که کنترل کننده می تواند در دامنه بسیار کاهش یابد. اگر از مرورگر برای کمک به بقیه کمک می کنید ، می توانید در مورد آن کد را برای حسابرسی آن کد صرف کنید.

این بدان معنا نیست که ماسهبازی یک راه حل کامل برای مشکل امنیت در اینترنت است. این دفاع را به عمق ارائه می دهد ، و مگر اینکه کنترل مشتری های کاربران خود را داشته باشید ، هنوز نمی توانید به پشتیبانی مرورگر برای همه کاربران خود اعتماد کنید (اگر مشتری های کاربران خود را کنترل کنید - یک محیط سازمانی ، به عنوان مثال - Hooray!). روزی ... اما در حال حاضر ماسهبازی لایه دیگری از محافظت برای تقویت دفاع شما است ، این یک دفاع کامل نیست که بتوانید به آن اعتماد کنید. هنوز لایه ها عالی هستند. پیشنهاد می کنم از این یکی استفاده کنید.

ادامه مطلب

  • " جدایی امتیاز در برنامه های HTML5 " یک مقاله جالب است که از طریق طراحی یک چارچوب کوچک کار می کند و کاربرد آن در سه برنامه HTML5 موجود است.

  • ماسهبازی در هنگام ترکیب با دو ویژگی جدید Iframe دیگر می تواند انعطاف پذیر تر باشد: srcdoc و seamless . اولی به شما امکان می دهد یک قاب را با محتوا بدون سرپرستی درخواست HTTP جمع کنید و دومی به سبک اجازه می دهد تا به محتوای قاب بندی شده جریان یابد. هر دو در حال حاضر از مرورگر نسبتاً بدبختی برخوردار هستند (شبانها Chrome و WebKit). اما در آینده یک ترکیب جالب خواهد بود. به عنوان مثال ، می توانید از طریق کد زیر درباره یک مقاله در مورد مقاله اظهار نظر کنید:

        <iframe sandbox seamless
                srcdoc="<p>This is a user's comment!
                           It can't execute script!
                           Hooray for safety!</p>"></iframe>