Защита данных от несанкционированного доступа

Этот способ организации данных подобен предыдущему, но содержит средства контроля для определения некорректных обращений. Для определения данных этого типа добавляется атрибут ENVIRONMENT (среда, окружение).

declare X ENVIRONMENT(STACK);

Для пользователя Х — это стек, к которому можно обращаться из процедуры STACK (например, с помощью функций POP, PUSH, EMPTY) аналогично тому, как если бы Х было действительное число и им можно было бы оперировать, используя операции сложения или умножения. Пользователь не знает, как составлен стек, и не имеет доступа к его внутренней структуре так же, как и большинство программистов находятся в неведении относительно того, каким образом величина с плавающей точкой записывается в память ЭВМ. Стек объявляется компилятором как переменная-указатель, но при этом накладываются дополнительные ограничения:

1) обработка стека Х происходит в процедуре FUNCTION в модуле с именем STACK;

2) описание стека Х может находиться только в операторе REP (представление для переменной) в модуле STACK;

3) любые другие обращения к стеку Х запрещены.

С точки зрения пользователя, данные абстрактного типа не должны отличаться от других переменных в программе. Однако имеется одно существенное различие между переменными типа FIXED и BLIPPO. Переменная типа FIXED размещается в памяти, когда начинает выполняться процедура (или блок), содержащая объявление переменной, и освобождает память, когда процедура (блок) завершится. А для переменной типа BLIPPO должна быть выделена постоянная память. Это может привести к двусмысленной ситуации. Так как одна из целей применения данных абстрактного типа заключается в расширении типов данных для прикладных задач, различие между типами данных, генерируемых компилятором, и данных, созданных пользователями, может вызвать недоразумения. Следовательно, одной из задач проектирования с использованием данных абстрактного типа должно быть автоматическое размещение таких данных при их использовании, так чтобы не было различий в стратегии использования памяти для указанных двух типов данных.

Для того чтобы использовать автоматическое распределение памяти, переменная Х инициируется при вызове модуля STACK. По существу, компилирование объявления ENVIRON­MENT эквивалентно следующей записи.

declare X POINTER INITIAL(STACK);

При первоначальном обращении к модулю STACK распределяется память для стека Х и возвращается ее адрес в переменную-указатель. Модуль, в котором происходит обработка стека Х, имеет вид:

STACK: ABSTRACTION;

REP 1 STACK[X], /* имя параметра стека*/

2 ENTRIES(100) FIXED,

2 TOPOFSTACK FIXED;

/* разместить стек */

/* присвоить начальные значения */

TOPOFSTACK = 0;

PUSH: function(X, элемент);

...

end;

POP: function(X, элемент);

...

end;

...

end;

Такую процедуру легко написать на современных языках, учитывая следующее.

Начало процедуры ABSTRACTION предназначено для размещения данных абстрактного типа и присвоения им начальных значений; однако действительным распределением памяти автоматически занимается компилятор. Это делается для сохранения совместимости с элементарными типами данных, такими, как FIXED и REAL.

Слово ABSTRACTION заменяется ключевым словом PROCEDURE и добавляется атрибут RETURN(POINTER). Кроме того, используются следующие операторы:

declare DUMMY POINTER;

ALLOCATE datatype SET(DUMMY);

Эти операторы позволяют автоматически распределять память для данных абстрактного типа. Для того чтобы обеспечить возврат из подпрограммы распределения памяти к соответствующему оператору declare, перед первым оператором FUNCTION помещают оператор RETURN(DUMMY). Вместо оператора REP объявляется структура типа BASED. Таким образом, транслятор транслирует следующие операторы:

STACK: procedure RETURNS(POINTER);

declare 1 STACK BASED(X),

2 ENTRIES(100) FIXED,

2 TOPOFSTACK FIXED;

declare DUMMY POINTER;

ALLOCATE STACK SET(DUMMY);

TOPOFSTACK = 0;

return(DUMMY);

end;

Вместо оператора FUNCTION подставляется последовательность ENTRY BEGIN. Для соответствующих операторов END добавляются операторы RETURN. Таким образом, имеем:

STACK: procedure RETURNS(POINTER);

declare 1 STACK BASED(X),

2 ENTRIES(100) FIXED,

2 TOPOFSTACK FIXED;

declare DUMMY POINTER;

ALLOCATE STACK SET(DUMMY);

TOPOFSTACK = 0;

return(DUMMY)

PUSH: function(X, элемент);

...

return;

end;

POP: function(X, элемент);

...

return;

end;

...

end;

Доступ к данным. При указанном способе организации данных защитные меры приняты лишь отчасти: модуль не может получить представление любой переменной абстрактного типа, определенное в некотором другом модуле. Однако должен ли вообще данный модуль иметь доступ к определенным данным абстрактного типа? Окончательная версия программы имеет следующую структуру:

Модуль1

Модуль2

Модульn

При такой структуре программы каждый модуль может пользоваться любой функцией любого другого модуля. Однако это может оказаться нежелательным. Предположим, что модуль X использует абстрактный тип данных Y, который, в свою очередь, обращается к абстрактному типу данных Z (рис. 4.14).

Х вызывает Z непосредственно б  

Рис. 4.14 — Нарушение принципа информационной локализованности

При написании программы программист, стремясь к большей эффективности программы, записывает вызов из модуля Х данных Z, тем самым неумышленно нарушает спецификацию относительно того, что о структуре данных Z известно только в модуле Y. Однако компилятор не в состоянии проверить это нарушение, а ошибка может быть выявлена гораздо позже, например, если модуль Y когда-либо изменится.

В операционных системах имеется возможность защиты от некорректных действий; такую форму защиты можно добавить в языки программирования. В операционных системах имеется вектор возможностей, связанный с каждым независимо выполняемым процессом. Этот вектор описывает функции, которые может выполнять данный процесс. Так как процессы начинаются и заканчиваются, когда задания пользователя соответственно поступают в систему или выходят из нее, вектор возможностей динамичен, и производится проверка правомерности операций во время работы системы.

В языках программирования аналогом такого вектора является право доступа. По сравнению с вектором возможностей права доступа обладают одним значительным преимуществом: проверка производится во время компиляции, а не во время выполнения программы. Программа статична, все ее процедуры известны до начала выполнения, все абстрактные типы данных известны, и проверка правильности обращения к данным может быть выполнена компилятором.

Заметим, что цель использования прав доступа заключается в ограничении доступа к абстрактным типам данных, а не в обеспечении безопасности системы. Предполагается, что программист может обращаться ко всем элементам системы, поэтому права доступа могут изменяться.