حفظ الأسرار في Linux: مصادقة JWT في تطبيق Python CLI

JSON Web Token هو معيار مفتوح لإنشاء رموز وصول بناءً على تنسيق JSON. تُستخدم عادةً لتمرير بيانات المصادقة في تطبيقات خادم العميل. ويكيبيديا

عندما يتعلق الأمر بتخزين البيانات الحساسة في المتصفح ، فأنت تحتاج فقط إلى استخدام أحد الخيارين المتاحين: ملفات تعريف الارتباط أو التخزين المحلي. هنا يختار الجميع الذوق. ومع ذلك ، فقد خصصت هذه المقالة لخدمة Secret Service ، وهي خدمة تعمل عبر D-Bus ومصممة لتخزين "الأسرار" في Linux.

تحتوي الخدمة على واجهة برمجة تطبيقات يستخدمها جنوم كيرينغ لتخزين أسرار التطبيق.

لماذا الخدمة السرية

الشيء هو أنني لم أتلق الرمز المميز في المتصفح. كنت أكتب مصادقة العميل لتطبيق وحدة تحكم مشابه للتطبيق المستخدم في git.

ظهر السؤال حول طريقة تخزين التفاصيل على الفور ، حيث لم أرغب في إجبار المستخدمين على تسجيل الدخول في المرة التالية التي يتم فيها إطلاق تطبيقي.

في البداية كان هناك خيار لتخزين الرمز المميز في ملف مشفر ، لكنه اختفى على الفور لأنني توقعت أن وظائف التشفير وفك التشفير الخاصة بي ستكون دراجة.

ثم فكرت كيف يحافظ Linux على الأسرار ، واتضح أن آليات مماثلة يتم تنفيذها في أنظمة تشغيل أخرى.

نتيجة لذلك ، ستكون كلمة مرور حساب مستخدم Linux بمثابة مفتاح الوصول إلى الرمز المميز.

لمحة عن هندسة الخدمة السرية

هيكل البيانات الرئيسي للخدمة السرية هو مجموعة من العناصر ذات السمات والسرية.

مجموعة

هذه مجموعة من جميع أنواع بيانات المصادقة. يستخدم النظام المجموعة الافتراضية تحت الاسم المستعار "افتراضي". جميع تطبيقات المستخدم مكتوبة عليها. سيرينا فرس البحر ذلك.

كما ترى ، يتم حفظ Google Chrome و VSCode في التخزين الخاص بي. سيتم حفظ طلبي هنا أيضًا.

كل سجل من هذا القبيل يسمى بندا.

جزء

جزء المجموعة الذي يخزن السمات والسر.

السمات

زوج من مفتاح النموذج ، القيمة ، والذي يحتوي على اسم التطبيق ويعمل على تحديد العنصر.

سر

, , , .

Secret Service

, flow chart.

  • « ?» — .

  • « » — .

  • « » — .

  • « API» — .

  • « » — .

  • « » — .

Python

Click Framework CLI .

import click

, , . . , .

@click.group()
def cli():
    pass

.

:

$ app login
Email:
Password:

:

$ app login
Logged in!

login

@cli.command(help="Login into your account.")
@click.option(
    '--email',
    prompt=True,
    help='Registered email address.')
@click.option(
    '--password',
    prompt=True,
    hide_input=True,
    help='Password provided at registration.'
    )
def login(email, password):
        pass

if __name__ == '__main__':
    cli()

login, , email password.

@cli.command , @click.option .

, hide_input .

prompt , lick Framework , .

True False , :

  • True Click Framework . . , WEB API Secret Service, Secret Service API;

  • False Click Framework . , , Secret Service.

, prompt_desicion. Secret Service. , Secret Service API .

. , , Click Framework.

, , Click Framework, .

app Click Framework. auth, Auth .

.
├── auth.py
└── app.py

prompt_desicion auth Auth auth.

@cli.command(help="Login into your account.")
@click.option(
    '--email',
    prompt=auth.prompt_desicion,
    help='Registered email address.')
@click.option(
    '--password',
    prompt=auth.prompt_desicion,
    hide_input=True,
    help='Password provided at registration.'
    )
def login(email, password):
        pass

if __name__ == '__main__':
    cli()

Python SecreteStorage, Secret Service API.

, Secret Service.

Secret Service API WEB API, prompt_desicion .

  • requests — HTTP WEB API.

  • secretstorage — Secret Service API.

  • json — .

import requests
import secretstorage
import json

Secrete Storage

class Auth:
    def __init__(self, email=None, password=None):
        # ,     
        # Secret Service
        self._attributes = {'application': 'MyApp'}
        #   Dbus
        self._connection = secretstorage.dbus_init()
        #   -
        self._collection = secretstorage.collection.get_default_collection(
            self._connection
            )
        #       
        self._items = self._collection.search_items(self._attributes)
        #   
        self._stored_secret = self.get_stored_secret()

.

Secret Service self._attributes.

«_»

, . , . , . , , . , .

, . () SecretStorage () . , , self._items .

get_stored_secret, .

class Auth:
    def get_stored_secret(self):
        for item in self._items:
            if item:
                return json.loads(item.get_secret())

Item secretstorage, get_secret, .

. .

.

True False — ,

, , : « — False».

class Auth:        
    def __init__(self, email=None, password=None):
        # ,     
        self.prompt_desicion = False

; , .

. , .

class Auth:        
    def __init__(self, email=None, password=None):
        # ,     
        #   
        if self._stored_secret:
            #      token
            self.token = self._stored_secret['token']
        #       
        elif email and password:
            #    WEB API
            self.token = self.get_token(email, password)
            #    
            self._valid_secret = {'token': self.token}
            #    Secret Service
            self.set_stored_secret()
        else:
            #     Secret Storage,     
            # 
            self.prompt_desicion = True

- .

  1. Secret Storage API.

  2. , .

  3. Secret Storage.

  1. Secret Storage API.

  2. , Secret Storage.

  3. , .

  4. , WEB API.

  5. , Secret Storage.

Secret Storage WEB API.

WEB API

class Auth:        
    def get_token(self, email: str, password: str) -> str:
        try:
            response = requests.post(
                API_URL,
                data= {
                    'email': email,
                    'passwd': password
                    })
            data = response.json()
        except requests.exceptions.ConnectionError:
            raise requests.exceptions.ConnectionError()
        if response.status_code != 200:
            raise requests.exceptions.HTTPError(data['msg'])
        return data['data']['token']

API_URL API. , . , POST «email» «passwd».

API , API .

API «msg» . try .

«data».

Secret Storage

class Auth:        
    def set_stored_secret(self):
        self._collection.create_item(
            'MyApp',
            self._attributes,
            bytes((json.dumps(self._valid_secret)), 'utf-8')
            )

create_item , .

lick Framework

auth.

from auth import Auth

Secret Storage.

auth = Auth()

.

@cli.command(help="Login into VPN Manager account.")
@click.option(
    '--email',
    prompt=auth.prompt_desicion,
    help='Registered email address.')
@click.option(
    '--password',
    prompt=auth.prompt_desicion,
    hide_input=True,
    help='Password provided at registration.'
    )

login.

def login(email, password):
    global auth
    try:
      	#         
        if auth.prompt_desicion:
          	#        Secret Storage
            auth = Auth(email, password)
    except Exception:
        return click.echo('No API connection')
		#     ,   .
    click.echo(auth.token)

انقر فوق Framework ينشئ المساعدة تلقائيًا. للقيام بذلك ، يحتاج إلى الخطوط التي حددتها في معلمة helpمصممي الديكور.

$ python app.py 
Usage: app.py [OPTIONS] COMMAND [ARGS]...

Options:
  --help  Show this message and exit.

Commands:
  login  Login into your account.

تعليمات أمر تسجيل الدخول

$ python app.py login --help
Usage: app.py login [OPTIONS]

  Login into your account

Options:
  --email TEXT     Registered email address
  --password TEXT  Password provided at registration
  --help           Show this message and exit.

التحقق من

بعد بدء التطبيق بالأمر ، python app.py loginسيطلب بريدًا وكلمة مرور. إذا كانت هذه البيانات صحيحة ، فسيظهر العنصر المقابل في الخدمة السرية.

في الواقع يخزن الرمز المميز.

عند إعادة تشغيل التطبيق ، لن يطلب منك التفاصيل ، ولكنه سيقوم بتنزيل الرمز المميز من الخدمة السرية.

الروابط




All Articles