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

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

خط‌مشی امنیت محتوا (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/ صفحه دیگری در همان مبدأ را با یک جعبه ایمنی که شامل پرچم‌های 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>