Статьи

Предотвращение CSRF-атак с использованием типов событий в Model-Glue

Запрос межсайтовое подлог (CSRF) происходит , когда хакер использует тот факт , что пользователи не всегда выходите из веб — сайтов и веб — приложений , которые они посещают. Хакер создает на веб-сайте, которым он управляет, URL-адрес или форму, которая передает действительные данные в действительное место назначения на целевом веб-сайте, и надеется, что пользователь, который все еще проходит проверку подлинности на этом целевом веб-сайте, щелкает этот вредоносный URL-адрес или форму. Если такой пользователь попадет в ловушку, целевой веб-сайт обработает запрос так же, как если бы пользователь выполнил действие на целевом веб-сайте при нормальных обстоятельствах.

Одним из распространенных методов предотвращения CSRF-атак является создание уникального значения каждый раз, когда пользователь посещает форму на веб-сайте, и сохранение этого значения как в сеансе пользователя, так и в самой форме в виде скрытого поля. Когда форма отправляется, значение в форме сравнивается со значением, сохраненным в сеансе пользователя, и, если они не совпадают, отправка формы не обрабатывается. В следующий раз, когда пользователь встречает форму (даже если это та же форма), генерируется новое уникальное значение. Без возможности узнать, что это уникальное значение в любой момент времени, хакер не может создать форму или создать URL, который имитирует законный запрос, и атака завершается неудачей.

Вместо того, чтобы не забывать создавать эти уникальные значения и включать их в каждую форму (или в каждый URL, который выполнил какую-то операцию с данными), а затем проверять достоверность отправленного значения на каждой странице обработки, я хотел бы увидеть, было ли способ, которым я мог бы встроить защиту CSRF в структуру моих приложений Model-Glue.

Во-первых, я написал две новые функции контроллера в моем контроллере аутентификации / авторизации CFC:

<cffunction name="secureTransmissionURL" access="public" returntype="void" output="false">
    <cfargument name="event" type="any">
    <cfif arguments.event.valueExists("token") EQ false>
        <cfset session.token= CreateUUID()>
    </cfif>
    <cfset arguments.event.setValue("secureMyself","index.cfm?token=" & Hash(session.token,"SHA-256") & "&" & arguments.event.getValue("eventValue") & "=")>
</cffunction>
	
<cffunction name="validateTransmissionURL" access="public" returntype="void" output="false">
    <cfargument name="event" type="any">
    <cfif arguments.event.valueExists("token") EQ false>
        <cfset arguments.event.setValue("validationProblem","noToken")>
        <cfset arguments.event.addResult("invalidTransmission")>
    <cfelseif arguments.event.getValue("token") NEQ Hash(session.token,"SHA-256")>
        <cfset arguments.event.setValue("validationProblem","tokenMismatch")>
        <cfset arguments.event.addResult("invalidTransmission")>
    </cfif>
</cffunction>

Функция secureTransmissionURL () проверяет, есть ли переменная с именем «token», хранящаяся в объекте события (для тех, кто не знаком с Model-Glue, объект события служит контейнером для всех переменных, связанных с запросом, включая форму и переменные URL). Если переменная токена не существует в реквете, переменная с именем «токен» создается или обновляется в рамках сеанса и получает уникальное значение UUID.

В последней строке функции secureTransmissionURL () в объекте события создается новая переменная «secureMyself», которой присваивается значение, которое служит отправной точкой для любого URL-адреса, который я хочу защитить от атак CSRF внутри моей модели. Нанесение клея. Эта строка заслуживает немного справочной информации …

В Model-Glue (как и во многих средах ColdFusion) все запросы выполняются через страницу index.cfm, и вы определяете, как запрос должен быть перенаправлен, добавляя определенную переменную (называемую переменной «eventValue» в Model-Glue) на URL. В Model-Glue именем этой переменной по умолчанию является «событие», поэтому, если вы оставите это значение по умолчанию, URL-адреса, которые вы будете использовать для навигации в приложении Model-Glue, будут выглядеть следующим образом:

index.cfm?event=deleteRecord&recordId=12

Поскольку можно изменить это имя переменной eventValue с «event» на что-то другое («action», «goto» и т. Д.), Просто изменив настройки конфигурации Model-Glue в своем приложении, жестко программировать небезопасно «index.cfm? event =» в ваши URL на ваших страницах. Таким образом, стандартная практика заключается в использовании переменной события, автоматически предоставляемой Model-Glue под названием «я», для создания ваших URL. Поэтому вместо того, чтобы писать «index.cfm? Event = mainMenu», вы должны сделать что-то подобное на своей веб-странице:

<cfset deleteRecordURL= event.getValue("myself") & "deleteRecord">
<cfoutput>
...
<a href="#deleteRecordURL#&recordId=12">Delete record 12</a>
...
</cfoutput>

(Примечание: обычно имя обработчика события / события, в данном случае «deleteRecord», на самом деле также берется из переменной события, но я хотел, чтобы мой пример был простым)

Переменная «secureMyself», созданная в этой последней строке secureTransmissionURL (), служит защищенной CSRF альтернативой переменной «себе», предоставляемой Model-Glue. Он добавляет хешированное значение UUID, хранящееся в области сеанса, к переменной URL-адреса «токен» перед переменной eventValue в строке URL-адреса, что позволяет мне использовать «secureMyself» для создания своих URL-адресов вместо «я», где необходимо (примечание: я все еще использовал бы «себя» для создания URL-адресов, которые не обрабатывают данные напрямую, например URL-адрес, который отправил пользователя на страницу меню):

<cfset deleteRecordURL= event.getValue("secureMyself") & "deleteRecord">
<cfoutput>
...
<a href="#deleteRecordURL#&recordId=12">Delete record 12</a>
...
</cfoutput>

Когда ColdFusion отображает защищенный URL-адрес, он выглядит примерно так:

index.cfm?token=524606212847B725A8AD313DD466CDCF66F8FC22B49DC31D525D51FC33B1E297&event=deleteRecord&recordId=12

Вторая функция validateTransmissionURL () предназначена для выполнения до обработки запроса страницы, защищенного CSRF. Он проверяет, была ли переменная токена включена в запрос, и если да, то проверяет, соответствует ли значение переданного токена хешированному значению переменной токена, сохраненной в сеансе. Если любое из этих условий не выполняется, результат «invalidTransmission» добавляется к объекту события, что эффективно предотвращает переход запроса к коду, который обрабатывает запрос (который взаимодействует с вашими постоянными данными).

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

<event-types>

    <event-type name="permitted">
        <before>
            <broadcasts>
                <message name="checkAuthorization" />
                <message name="secureTransmissionURL" />
            </broadcasts>
            <results>
                ...<!--Whatever you do if user is not authenticated yet-->
            </results>
        </before>
    </event-type>

    <event-type name="validateTransmission">
        <before>
            <broadcasts>
                <message name="validateTransmissionURL" />
            </broadcasts>
            <results>
                <result name="invalidTransmission" append="validationProblem" do="invalidTransmission" redirect="true" />
            </results>
        </before>
    </event-type>

</event-types>

Если вы не знакомы с типами событий в Model-Glue, я рекомендую вам прочитать документацию на вики-сайте Model-Glue . В моем приложении все мои обработчики событий, которые доступны пользователю только после того, как они аутентифицировались / вошли в систему, заключены в «разрешенный» тип события, определенный выше. Добавляя широковещательную рассылку сообщений, которая вызывает мою функцию контроллера secureTransmissionURL () для этого типа события, я гарантирую, что переменная события «secureMyself» доступна для использования на всех моих страницах просмотра с проверкой подлинности.

Тип события «validateTransmission», который гарантирует, что представленный URL-адрес содержит правильное значение токена, будет применяться только к моим обработчикам событий, которые обрабатывают отправку формы или выполняют транзакцию базы данных на основе переменных URL-адреса, отправленных в запросе. Если токен отсутствует или неверен, Model-Glue перенаправляет действие в обработчик событий invalidTransmission, который будет реагировать на возможную атаку CSRF. Мой обработчик события invalidTransmission представляет обычное сообщение об ошибке пользователю (поскольку пользователь, а не хакер увидит результат атаки), но уведомляет меня о возможной атаке CSRF.

После того, как у меня были эти функции контроллера и типы событий, все, что мне нужно было сделать, чтобы защитить свои формы и гиперссылки от CSRF-атак, — это использовать переменную события secureMyself, чтобы создать URL-адреса для этих форм и гиперссылок и добавить тип события «validateTransmission» тем обработчикам событий, которые обрабатывали представления из этих форм и гиперссылок.

Если вы хотите узнать больше о защите от CSRF-атак, я бы порекомендовал вам прочитать некоторые посты в блоге, которые помогли мне понять CSRF-атаки:

http://shiflett.org/articles/cross-site-request-forgeries

http://www.mollerus.net/tom/blog/2009/01/an_easy_block_for_crosssite_request_forgeries_csrf.html

http://www.12robots.com/index.cfm/2008/8/25/Request-Forgeries-and-ColdFusion—Security-Series-9

http://www.12robots.com/index.cfm/2009/2/9/Enhancing-Request-Forgery-Protection—Security-Series-91