19.03.2012

Реализация инструментов для создания контента OVAL на Python

image

Исследовательский центр Positive Research представляет серию статей, знакомящих читателей с мировыми стандартами ИБ в сфере передачи и обработки секьюрити-контента. Центр обладает компетенцией и базой знаний по выявлению и устранению уязвимостей и угроз в информационных системах.

Автор: Positive Research

В процессе исследования языка OVAL, про который написано ранее в одной из статей, и концепции SCAP-сканера мы столкнулись с довольно серьезной проблемой, а именно с отсутствием удобных инструментов для создания контента на языке OVAL. Нет, мы не утверждаем, что нет совсем ничего. Есть небольшой набор утилит, представленных на официальном сайте. Большая часть из них платная, остальные же представляют собой не очень удобные решения, больше всего похожие на XML-Notepad. В итоге мы решили создать небольшой необходимый для работы инструмент самостоятельно, используя в качестве языка Python


Такой выбор обусловлен хорошей репутацией языка Python как «rapid development language» и наличием богатой палитры сторонних библиотек. Вооружившись документацией по языку, попытались воссоздать задумки MITRE в реальности. Конечной целью поставили реализацию объектов языка OVAL и системы их хранения и индексации.

Первоочередным вопросом был выбор способа хранения собранной информации. Первым (и неудачным) решением стал SQLite 3. Не будем особо задерживаться на этом печальном опыте, но как показала практика, — хранение непредсказуемого дерева в реляционной базе данных слишком сложная задача. Поэтому мы обратили внимание на базы данных NoSQL. Так как планировалось ограничиться монопольным использованием базы, выбор пал на ZoDB. Конечно же, это решение тоже было поспешным. Но переделывать на MongoDB было уже поздно. Так что для дальнейших выкладок кода хотелось бы отметить, что PersistentMapping() — это, по сути, обычный dict(). Приняв во внимание неопределенность восприятия хэшмапов базой данных, в качестве хэшмапов использовались ключи того же класса. PersistentList() — это эквивалент list(). Данная замена необходима для хранения подобных структур в ZoDB. Это обусловлено внутренней логикой работы базы данных. Для сохранения класса в базе данных целиком необходимо, чтобы класс являлся наследником класса persistent.Persistent.

Кроме того, просим заранее простить нас за нешаблонное именование переменных, методов и классов и возможную «косорукость»: я не волшебник, я только учусь. И с удовольствием примем любые правки и замечания от более осведомленных коллег.

Во избежание цитирования излишне больших кусков кода сразу приведем ссылку на исходник на code.google.com.

В целом при анализе структур языка OVAL мы отметили, что его главной особенностью является расширяемость. Если необходимо ввести собственную структуру, достаточно описать ее в XSD, определяющим ваш namespace. Поскольку структуры в большинстве своем представляют типовые наборы данных, то мы решили создать выделенный класс для каждого объекта языка. Основная цель такой реализации — создание контейнера для информации, который будет удобно закладывать в БД. Таким образом, в качестве базового объекта выступил элемент OBJECT:
# UNIVERSAL OVAL OBJECTS
class oval_object(persistent.Persistent):
    def __init__(self):
        self.id = str()
        self.tag = "object"
        self.namespace = str()
        self.oval = str()
        self.version = 0
        self.vHash = 0
        self.deprecated = False
        self.variables = PersistentList()
        self.notes = PersistentList()
        self.comment = str()
        self.signature = str()

    def assign_id(self, UniqId):
        self.id = "oval:"+self.oval+":obj:"+str(UniqId)

    def hash(self):
        summ_hash = 0
        summ_hash += hash(self.tag)
        summ_hash += hash(self.namespace)
        summ_hash += hash(self.comment)
        for var in self.variables:
            if isinstance(var, variable):
                summ_hash += var.hashme()
            else:
                raise Exception("Error input parameters in object variables")
        return summ_hash

    def hashme(self):
        self.vHash = self.hash()
        return self.vHash

    def clearme(self):
        hashmap = PersistentMapping()
        for var in self.variables:
            var.clearme()
            var_hash = var.hashme()
            if var_hash not in hashmap.keys():
                hashmap[var_hash] = None
            else:
                self.variables.remove(var)
Данный класс является базовым для всех основных элементов OVAL.Definition, Test, State и Oval_variable будут являться наследниками этого класса. Для того, чтобы реализовать возможность сравнения элементов, были созданы методы hash() (универсальный для всех) и hashme()(переопределяемый у потомков).

Как видно из названий переменных, все основные структуры заданы статически. Динамическими же являются возможные «потомки», структуры которых мы не знаем. Для реализации таких «потомков» был сделал класс Variable, во многом повторяющий по идеологии Element из ElementTree:
class variable(persistent.Persistent):
    def __init__(self, tag=str(), body = str(), attributes = None, variables = None):
        if not attributes:
            self.attributes = PersistentMapping()
        if not variables:
            self.variables = PersistentList()
        if attributes and not isinstance(attributes, PersistentMapping):
            attributes = PersistentMapping(attributes)
        if variables and not isinstance(variables, PersistentList):
            variables = PersistentList(variables)
        self.tag = tag
        self.body = body
        if attributes:
            self.attributes = attributes
        if variables:
            self.variables = variables
        self.vHash = 0

    def clearme(self):
        hashmap = PersistentMapping()
        for var in self.variables:
            var.clearme()
            var_hash = var.hashme()
            if var_hash not in hashmap.keys():
                hashmap[var_hash] = None
            else:
                self.variables.remove(var)

    def hashme(self):
        summ_hash = 0
        summ_hash += hash(self.tag)
        summ_hash += hash(self.body)
        if self.attributes:
            for attribute in self.attributes.keys():
                summ_hash += hash(attribute) + hash(self.attributes[attribute])
        if self.variables:
            for var in self.variables:
                if isinstance(var, variable):
                    if var !self:
                        summ_hash += var.hashme()
                else:
                    raise Exception("Error input parameters in variables. Self append ?")
        self.vHash = summ_hash
        return self.vHash
Как и в случае с OBJECT, потребовалась функция расчета хэша. Структура класса максимально подогнана к ElementTree.Element, чтобы упростить в будущем выгрузку базы данных в XML-формат.

В целом получился набор объектов, удобных для заполнения. Но мало их заполнить: при создании собственных объектов необходимо выполнять требования регулятора (в нашем случае MITRE) к заполнению и хранению новых Definition. Соответственно, мне потребовалась система индексации и контроля изменения версии Definition. Для этого был создан класс oval_suite(). В нем реализован базовый набор методов (put, get, delete, search, import_xml, export_xml), который облегчает работу с контентом и позволяет не заморачиваться на вышеупомянутых проблемах.

Одна из специфичных проблем, с которыми столкнулись в процессе создания oval_suite(),- нехватка памяти при попытке экспортировать в XML-формат большой объем данных (порядка 100Мб) за один заход. Поэтому для реализации этой функции пришлось создавать конечный файл поэтапно и использовать функцию удаления элементов ElementTree.Element, позаимствованную из StackOverflow:
def _garbager(self, root):
        for element in list(root):
            self._garbager(element)
        root.clear()
        del root
В целом и общем, получились наброски инструментария для работы с OVAL на Python, который может пригодиться при сборке и создании секьюрити-контента. Данный модуль позволяет выполнять сборку секьюрити-контента и «упаковку» его в базу данных с контролем версий и уникальности идентификаторов.

Спасибо за внимание! Надеемся, кто-нибудь заинтересуется тематикой OVAL и данные наработки окажутся ему полезны.
или введите имя

CAPTCHA