Wiki

Clone wiki

sinops / Home

Добро пожаловать

Это страница документации пакета sinops.

Sinops (signed operations) - пакет для создания в django приложении представлений, допускающих исполнение, только при наличии специального документа подписанного с использованием ЭЦП.



Схема взаимодействия с клиентом.

Предположим имеется клиент-серверное приложение, которое предоставляет своим пользователям (клиентам) совершить определенные действия, только после предоставления специального документа, защищенного электроно-цифровой подписью. При использовании приложением пакета sinops процесс взаимодействия клиента с сервером будет следующим:

  1. Клиент посылает запрос на выполнение некоторого действия.
  2. Сервер определяет, что для успешного выполнения действия клиенту необходимо выполнить подписание некотрого документа.
    1. Сервер генерирует документ, описывающий действие которое клиент намеревается выполнить, a также определяет доступные способы его подписания.
    2. Сервер возвращает клиенту ответ, содержащий сгенерированный документ и информацию о допустимых форматах подписи, с помощю которых он может быть подписан.
  3. Клиент подписывает документ и выполняет действие повторно, но на этот раз добавляет к запросу также и информацию о подписанном документе (подпись и некоторые дополнительные атрибуты)
  4. Сервер снова принимает запрос и:
    1. Выполняется проверка, что формат подписи среди допустимых форматов.
    2. Выполняется проверка, подписан именно тот документ, который был сгенерирован сервером в пункте 2.1
    3. Выполняется проверка на то, что выполняемое в текущем запросе действие такое же как и в пункте 2. (т.е результат будет аналогичным.)
    4. Выполняется проверка валидности ЭЦП.
    5. В случае успешной проверки предыдущих пунктов, производиться попытка выполнить запрошенное действие.
    6. Производиться логирование операции.
    7. Клиенту возвращается ответ со статусом операции.
  5. Клиент получает ответ. Пользователю дается ссылка на загрузку подписанного документа либо сообщение об ошибке.

Установка и настройка

Для начала нужно добавить пакет sinops в INSTALLED_APPS, выполнить миграции и настроить интерфейс взаимодействия с клиентской частью:


# settings.py

INSTALLED_APPS += ('sinops',)

SINOPS_SETTINGS = {
    'DEFAULT_CLIENT_INTERFACE_CLASS': 'mypackage.MyInterface'
}

# mypackage.py

from django.shortcuts import render_to_response
from sinops.interface import ClientInterfaceBase

class MyInterface(ClientInterfaceBase)

    def ask_signature_result(self, request, operation):
        u"""
        Возвращает результат представления, который должен сообщить
        клиенту информацию о том, что совершаемая им операция
        должна быть подписана с использованием ЭЦП.
        """
        return render_to_response(
            'ask_sign_template.html', {msg: operation.get_content_to_sign()}
        )

    def operation_completed_result(self, request, operation, value):
        u"""
        Возвращает результат представления, информирующий
        клиента о том, что совершаемая им операция успешно выполненна.
        """
        return render_to_response('my_sucess_template.html', {value: value})

    def verify_error_result(self, request, operation, err):
        u"""
        Возвращает результат представления, информирующий
        клиента о том, что во время верификации операции произошла ошибка.
        """
        return render_to_response('my_error.html', {'err': unicode(err)})

    def operation_error_result(self, request, operation, err):
        u"""
        Возвращает результат представления, информирующий
        клиента о том, что операция была успешно верифицированна, однако
        соответствующим представлением было выброшено исключение.
        """
        return render_to_response('my_error.html', {'err': unicode(err)})

Теперь пакет готов к использованию.

Модуль sinops.operations

Концепция

Концепция работы с данным пакетом предполагает, что любое представление, требующее подписания, должно быть связанно с некоторым экземпляром "operation" класса, инкапсулирующим всю логику работы с ЭЦП начиная от формирования документа до верификации подписи и логирования.

Note

Под "operation" классом, здесь и далее будет пониматься любой потомок класса sinops.operation.SignedOperationBase. В модуле sinops.operation реализовано два "operation" класса SignedOperation и StatefulSignedOperation.

Любой "operation" класс должен реализовывать 3 метода:

  • get_client_interface - метод, который возвращает интерфейс взаимодействия с клиентом, необходимый для подписания операции и сообщения об ошибках.
  • verify - метод, который должен выполнять верификацию операции
  • save - Метод, определяющий как после успешного выполнения операции ее нужно залогировать.

Абстрагируясь от конкретной реализации "operation" классов, рассмотрим сначала примеры их использования.

Пример использования с декоратором

Самый простой способ добавить в представление функциональность запроса подписи - это использовать декоратор sign_required:


from django.http import HttpResponse
from sinops.utils import sign_required
from sinops.operation import SignedOperation

@sign_required(SignedOperation(u'Это сообщение(документ) для подписания'))
def foo_view(request):

    # Тут можно быть уверенным что документ подписан и успешно верифицирован.
    # Совершаем требуемые действия:
    execute_something()

    return HttpResponse(u'Операция выполнена успешно!')

В данный декоратор первым аргументом можно также передать callable объект, который вернет экземпляр SignedOperation. Переданный callable будет вызван с тем же набором аргументов что и декорируемое представление:


def get_operation(request, arg1, arg2):
    message = (
        u'Пользоваетель {0} Вы делаете что то серьезное.'
        u'Данное сообщение сформированно {1}'
    ).format(request.user.username, datetime.now())
    return SignedOperation(message)


@sign_required(get_operation)
def bar_view(request, arg1, arg2):

    # Тут можно быть уверенным что документ подписан и успешно верифицирован.
    # Совершаем требуемые действия:
    execute_something()

    return HttpResponse(u'Операция выполнена успешно!')

Расширенный пример использования

Приведем пример view с подробными комментариями, демонстрирующее использование "operation" классов, более явно:


# SignedOperation - некоторая реализация SignedOperationBase
from sinops.operation import SignedOperation


def delete_obj_view(request):
    u"""
    Некоторая view (без особых ограничений можно также
    считать что это метод run произвольного экшена)
    Для выполнения данной view может понадобиться подписанный
    с помощю ЭЦП документ.
    """

    response = HttpResponse(u'Удаление успешно выполнено')

    obj = get_object(request.REQUEST['object_id'])

    if obj in IMPORTANT_OBJECTS:
        # удаляется какой-либо важный объект
        # Считаем это значимым действием, требующее подписания.

        message = (
            u'Пользоваетель ПЕТРОВ Вы удаляете объект такой-то'
            u' данное сообщение подписано тогда-то'
        )

        signed_op = SignedOperation(
          message,
          # Каждая операция должна быть определенного типа.
          # Если тип не будет указан, то будет использован тип по умолчанию
          operation_type_id=DELETE_IMPORTATN_OBJECT_OPERATION_ID
        )

        # Перед верификацией signed_op должна быть инициализированна
        signed_op.initialize(request)

        # Получаем интерфейс взаимодействия с клиентом
        interface = signed_op.get_client_interface()

        try:
            signed_op.verify()
        except exceptions.SignatureDoesNotSupplied:
            # Ошибка проверки, но только потому что данные ЭЦП
            # отсутствуют в параметрах запроса.
            # Поэтому вернем респонс который запрашивает ЭЦП
            response = interface.ask_signature_result(request, signed_op)
        except exceptions.VerificationError as err:
            # Ошибка проверки выполняемой операции
            # Поэтому вернем респонс который сообщает об ошибке
            response = interface.verify_error_result(request, signed_op, err)
        else:
            # Операция была успешно верифицирована. Выполняем удаление
            obj.delete()

            # Респонс будет сообщать об успешном выполнении операции
            response = interface.operation_completed_result(
                request, signed_op, response,
            )
    else:
        # удаляется какой-либо не важный объект
        # Это можно сделать без ЭЦП
        obj.delete()

    return response

Класс sinops.operation.SignedOperation

Основной "operation" класс модуля.

При инстанцировании принимает некоторые данные для подписания и не обязательный аргумент - id типа операции.

По умолчанию использует классы подписей, интерфейс взаимодействия с клиентом и бэкенды верификации ЭЦП задаваемые соответствующими настройками пакета:

SINOPS_SETTINGS = {
    'DEFAULT_SIGNATURE_CLASSES': [
        'sinops.signature.cades.CAdESXLT1',
    ],
    'DEFAULT_VERIFIER_CLASSES': [
        'sinops.verification.smev.SMEVVerifier',
    ],
    'DEFAULT_CLIENT_INTERFACE_CLASS': 'sinops.interface.ClientInterfaceBase',
}

Класс sinops.operation.StatefulSignedOperation

Operation класс сохраняющий хеш данных для подписания.

Основное отличие данного класса от sinops.operation.SignedOperation состоит в том, что он обладает измененной логикой верификации операции (см. метод .verify_operation()). Так как подписанные клиентом данные могут отличаться от данных которые предполагаются быть подписанными, но при этом все же быть валидными (например, могут отличаются временным маркером содержащимся в сообщении), то это может привести к ошибке верификации. Для обхода этой проблемы, верификация операции в данном клаcсе реализуется не путем сравнения подписанного сообщения и сообщения которое планируется быть подписанным, а несколько иначе:

  1. При получении данных для подписания, вычисляется хеш от от этих данных и некоторых параметров запроса. Вычисленный хеш сохраняется в сессии.
  2. При верификации операции, снова вычисляется хеш, но теперь уже от подписанных данных и новых параметров запроса. Сравнивается только что вычисленных хеш и хеш сохраненный в сесси.

Дополнительно, добавлена возможность ограничить время на выполнение подписания. см StatefulSignedOperation.OPERATION_TIMEOUT.

Интерфейс взаимодействия с клиентом

Интерфейс взаимодействия с клиентом - это любой класс наследуемый от абстрактного класса sinops.interface.ClientInterfaceBase.

Назначение данных интерфейс-классов легко понять рассмотрев код базового класса и прочитав докстринги его методов:


class ClientInterfaceBase(object):

    u"""Базовый интерфейс для взаимодействия с клиентом."""

    def ask_signature_result(self, request, operation):
        u"""
        Возвращает результат представления, который должен сообщить
        клиенту информацию о том, что совершаемая им операция
        должна быть подписана с использованием ЭЦП.

        :param request: Объект запроса.
        :type request: django.http.HttpRequest.
        :param operation: Выполняющаяся операция.
        :type operation: Потомок sinops.operation.SignedOperationBase.
        """
        raise NotImplementedError

    def operation_completed_result(self, request, operation, value):
        u"""
        Возвращает результат представления, информирующий
        клиента о том, что совершаемая им операция успешно выполненна.

        :param request: Объект зпроса.
        :type request: django.http.HttpRequest.
        :param operation: Выполняющаяся операция.
        :type operation: Потомок sinops.operation.SignedOperationBase.
        :param value: Результат выполнения представления.
        """
        raise NotImplementedError

    def verify_error_result(self, request, operation, err):
        u"""
        Возвращает результат представления, информирующий
        клиента о том, что во время верификации операции произошла ошибка.

        .. note::
            Если метод не знает тип ошибки, то он должен вернуть None.

        :param request: Объект запроса.
        :type request: django.http.HttpRequest.
        :param operation: Выполняющаяся операция.
        :type operation: Потомок sinops.operation.SignedOperationBase.
        :param err: Отловленная ошибка верификации.
        """
        raise NotImplementedError

    def operation_error_result(self, request, operation, err):
        u"""
        Возвращает результат представления, информирующий
        клиента о том, что операция была успешно верифицированна, однако
        соответствующим представлением было выброшено исключение.

        .. note::
            Если метод не знает тип ошибки, то он должен вернуть None.

        :param request: Объект запроса.
        :type request: django.http.HttpRequest.
        :param operation: Выполняющаяся операция.
        :type operation: Потомок sinops.operation.SignedOperationBase.
        :param err: Отловленная ошибка выполнения представления.
        """
        raise NotImplementedError

Бэкенды верификации ЭЦП

Бэкенд верификации ЭЦП - это отдельная сущность, назначение которой проверка валидности ЭЦП.

Note

Несмотря на то что верификация операции - это комплексная проверка, которая проверяет не только ЭЦП, но некоторые другие параметры (см. схема взаимодействия), бекенд верификации ЭЦП, должен верифицировать только ЭЦП сообщения.

Бекенд должен наследовать базовый класс sinops.verification.base.VerifierBase и определять два метода:

  • verify - метод должен проверять ЭЦП сообщения. В случае успешного выполнения должен возвращать None, иначе выбрасывать исключение VerificationError.
  • is_suitable - classmethod, должен определять может ли данный бэкенд верифицировать подпись signature.

Оба метода принимают единственный аргумент экземпляр класса сигнатуры.

Бэкенд верификации SMEV

Данный бекенд находится в модуле sinops.verification.smev.SMEVVerifier. Бэкенд выполняет проверку отправляя запрос специальному сервису СМЭВ.

Адрес этого сервиса задается настройкой:

SINOPS_SETTINGS = {
    'SMEV_VERIFIER_SERVICE_URL': 'http://195.245.214.33:7777/esv/SignatureTool.asmx'
}

На данный момент бэкенд поддерживает подпись формата CAdES, определяемую классом сигнатуры sinops.signature.cades.CAdESXLT1, однако, так как СМЭВ сервис может проверять и другие типы подписи, то данный бэкенд может быть легко расширен.

Пример использования

from sinops.signature.cades import CAdESXLT1
from sinops.verification.smev import SMEVVerifier
from sinops.exceptions import VerificationError

signature = CAdESXLT1(open('/signed_message.der', 'rb').read())
verifier = SMEVVerifier()

try:
    verifier.verify(signature)
except VerificationError as err:
    print u'Подпись неверна ' + err.message
else:
    print u'Подпись верна'

Updated