برنامه نویسی

ایمن سازی اسرار تست با pytest-mask-secrets

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

چرا پوشش گردش کار CI به تنهایی ممکن است کافی نباشد

به عنوان مثال، GitHub Actions کار خوبی برای مدیریت اسرار انجام می دهد. هر راز تعریف شده در جریان کار به طور خودکار از خروجی گرفته شده پنهان می شود، که مانند یک افسون عمل می کند. با این حال، مانند هر سیستم CI، محدودیت های خود را دارد. اگر گزارش خروجی مسیر دیگری را طی کند – مانند ذخیره در یک فایل، junit تولید یا به یک فروشگاه گزارش راه دور ارسال شود – GitHub Actions توانایی بررسی محتوا و پنهان کردن اسرار را ندارد.

علاوه بر این، تست همیشه در یک گردش کار CI اجرا نمی‌شود، و حتی در این صورت، ممکن است هنوز اسرار پنهان شوند. تصور کنید که در حال اجرای آزمایش‌ها به صورت محلی هستید و یک گزارش را برای بحث در مورد یک مشکل به اشتراک می‌گذارید. بدون اینکه متوجه شوید، یک URL را با نشانه دسترسی خود وارد می کنید.

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

افزودن حفاظت در سطح مناسب

حفظ پنهان کردن اسرار مستقیماً در آزمایش‌ها می‌تواند نسبتاً پرهزینه و مستعد خطا باشد و اغلب شبیه یک نبرد شکست خورده است. به عنوان مثال، تصور کنید باید یک URL با یک توکن به عنوان پارامتر طراحی کنید. این URL برای استفاده در یک درخواست در مقایسه با حضور آن در گزارش باید متفاوت ارائه شود.

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

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

آزمایش کردن: یک نسخه آزمایشی عملی

برای نشان دادن این که چگونه این کار می کند، یک مثال ساده موثرترین خواهد بود. در زیر یک تست پیش پا افتاده است که ممکن است سناریوی آزمایشی در دنیای واقعی را نشان ندهد، اما در خدمت هدف نشان دادن است. pytest-mask-secrets کاملا خوب

import logging
import os


def test_password_length():
    password = os.environ["PASSWORD"]
    logging.info("Tested password: %s", password)
    assert len(password) > 18
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در این مثال، یک ادعا وجود دارد که دارای پتانسیل شکست است (و خواهد شد)، همراه با یک پیام گزارش که شامل یک راز است. بله، ممکن است احمقانه به نظر برسد که یک راز را در گزارش وارد کنید، اما سناریویی را در نظر بگیرید که در آن یک URL با یک نشانه به عنوان پارامتر دارید، و ثبت اشکال زدایی دقیق فعال است. در چنین مواردی، کتابخانه ها مانند requests ممکن است ناخواسته راز را از این طریق ثبت کند.

حالا برای تست ابتدا، راز مورد نیاز برای اهداف آزمایشی را تنظیم کنید:

(venv) $ export PASSWORD="TOP-SECRET"
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

بعد، تست را اجرا کنید:

(venv) $ pytest --log-level=info test.py 
============================= test session starts ==============================
platform linux -- Python 3.12.4, pytest-8.3.2, pluggy-1.5.0
rootdir: /tmp/tmp.AvZtz7nHZS
collected 1 item                                                               

test.py F                                                                [100%]

=================================== FAILURES ===================================
_____________________________ test_password_length _____________________________

    def test_password_length():
        password = os.environ["PASSWORD"]
        logging.info("Tested password: %s", password)
>       assert len(password) > 18
E       AssertionError: assert 10 > 18
E        +  where 10 = len('TOP-SECRET')

test.py:8: AssertionError
------------------------------ Captured log call -------------------------------
INFO     root:test.py:7 Tested password: TOP-SECRET
=========================== short test summary info ============================
FAILED test.py::test_password_length - AssertionError: assert 10 > 18
============================== 1 failed in 0.03s ===============================
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

به‌طور پیش‌فرض، مقدار مخفی دو بار در خروجی ظاهر می‌شود: یک بار در پیام گزارش ثبت شده و دوباره در ادعای ناموفق.

اما اگر pytest-mask-secrets نصب شده است؟

(venv) $ pip install pytest-mask-secrets
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

و بر این اساس پیکربندی شده است. باید لیستی از متغیرهای محیطی که اسرار را در خود نگه می دارند را بداند. این کار با تنظیم انجام می شود MASK_SECRETS متغیر:

(venv) $ export MASK_SECRETS=PASSWORD
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

حالا تست را دوباره اجرا کنید:

(venv) $ pytest --log-level=info test.py 
============================= test session starts ==============================
platform linux -- Python 3.12.4, pytest-8.3.2, pluggy-1.5.0
rootdir: /tmp/tmp.AvZtz7nHZS
plugins: mask-secrets-1.2.0
collected 1 item                                                               

test.py F                                                                [100%]

=================================== FAILURES ===================================
_____________________________ test_password_length _____________________________

    def test_password_length():
        password = os.environ["PASSWORD"]
        logging.info("Tested password: %s", password)
>       assert len(password) > 18
E       AssertionError: assert 10 > 18
E        +  where 10 = len('*****')

test.py:8: AssertionError
------------------------------ Captured log call -------------------------------
INFO     root:test.py:7 Tested password: *****
=========================== short test summary info ============================
FAILED test.py::test_password_length - AssertionError: assert 10 > 18
============================== 1 failed in 0.02s ===============================
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اکنون به جای مقدار مخفی، ستاره‌ها در هر جایی که راز چاپ می‌شد ظاهر می‌شود. کار انجام شده است و گزارش آزمایش اکنون فاقد داده های حساس است.

بستن افکار

از مثال، ممکن است به نظر برسد که pytest-mask-secrets خیلی بیشتر از آنچه GitHub Actions قبلاً به صورت پیش‌فرض انجام می‌دهد، انجام نمی‌دهد و باعث می‌شود تلاش‌ها اضافی به نظر برسد. با این حال، همانطور که قبلا ذکر شد، ابزارهای اجرای CI worklfow فقط اسرار خروجی گرفته شده را پنهان می‌کنند و فایل‌های JUnit و سایر گزارش‌ها را بدون تغییر می‌گذارند. بدون pytest-mask-secrets، داده های حساس همچنان می توانند در این فایل ها در معرض دید قرار گیرند—این برای هر گزارش تولید شده توسط pytest صدق می کند. از سوی دیگر، pytest-mask-secrets خروجی مستقیم را وقتی که log_cli گزینه استفاده می شود، بنابراین ویژگی های پوشاندن جریان های کاری CI هنوز مفید هستند. برای اطمینان از حفاظت از داده های حساس، اغلب بهتر است از هر دو ابزار به همراه یکدیگر استفاده کنید.

این است. از اینکه برای خواندن این پست وقت گذاشتید متشکرم. من امیدوارم که بینش های ارزشمندی را برای استفاده ارائه کرده باشد pytest-mask-secrets برای افزایش امنیت فرآیند تست شما.

تست مبارک!

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا