Coldfusion — создайте безопасную область входа в онлайн-аккаунт на основе имени пользователя, пароля и user_id (CFParam URL.user_id)

Я пытаюсь создать безопасную область входа в систему с помощью Coldfusion, где имя пользователя, пароль и user_id (возможно, с использованием CFParam URL.user_id) определяют, какая информация отображается после входа в систему — что-то вроде онлайн-аккаунта. Таким образом, используя свои собственные учетные данные для входа, пользователь будет входить в систему и иметь доступ к своей информации ТОЛЬКО без страниц по умолчанию для первого пользователя в базе данных или путем доступа к информации другого пользователя, просто изменив user_id в браузере. bar (возможно, зашифровав переменные URL). Вот то, что я всегда использовал для безопасного входа в систему в прошлом (который работает), который также имеет 3 неудачных попытки входа в систему и сеанс тайм-аута:

<cfquery name="rs_user" datasource="source">
SELECT user_id
FROM table
WHERE user_id = <cfqueryparam value="#URL.user_id#" cfsqltype="cf_sql_integer"> 
</cfquery>

<cfif IsDefined("URL.MM_logout") AND URL.MM_logout EQ "1">
  <cflock scope="Session" type="Exclusive" timeout="30" throwontimeout="no">
    <cfset Session.MM_Username="">
    <cfset Session.MM_UserAuthorization="">
  </cflock>
  <cfset MM_logoutRedirectPage="index.cfm">
  <cfif MM_logoutRedirectPage EQ "">
    <cfset MM_logoutRedirectPage=CGI.SCRIPT_NAME>
  </cfif>
  <cfset MM_logoutQuery=ListDeleteAt(CGI.QUERY_STRING,ListContainsNoCase(CGI.QUERY_STRING,"MM_logout=","&"),"&")>
  <cfif MM_logoutQuery NEQ "">
    <cfif Find("?",MM_logoutRedirectPage) EQ 0>
      <cfset MM_logoutRedirectPage=MM_logoutRedirectPage & "?" & MM_logoutQuery>
      <cfelse>
      <cfset MM_logoutRedirectPage=MM_logoutRedirectPage & "&" & MM_logoutQuery>
    </cfif>
  </cfif>
  <cflocation url="#MM_logoutRedirectPage#" addtoken="no">
</cfif>
<cfset CurrentPage=GetFileFromPath(GetBaseTemplatePath())>

<cfif IsDefined("FORM.loginfield")>
  <cfset MM_redirectLoginSuccess="table/landing_page.cfm?user_id=#rs_user.user_id#">
  <cfset MM_redirectLoginFailed="#CurrentPage#?loginfailed=True&loginfield=#form.loginfield#">
  <cfset variables.blnAllowLogin=false>
  <cfset variables.isLocked=false>
  <!--- lets find the user --->
  <cfquery name="qryUserAttempts" datasource="source">
    SELECT
      user_id,
      dateLocked,
      failedLoginAttempt
    FROM 
      table
    WHERE
      username=<cfqueryparam value="#FORM.loginfield#" cfsqltype="cf_sql_varchar" maxlength="255">
  </cfquery>
  <cfif qryUserAttempts.recordcount>
    <cfif isDate(qryUserAttempts.dateLocked)>
      <cfset intMinSinceLocked=DateDiff("n",qryUserAttempts.dateLocked,now())>
      <!--- number of mins to lock (set to 5) --->
      <cfif intMinSinceLocked GT 5>
        <cfset variables.blnAllowLogin=true>
      <cfelse>
        <cfset variables.isLocked=true>
      </cfif>
    <cfelse>
      <cfset variables.blnAllowLogin=true>
    </cfif>
  <cfelse>
    <cfset variables.blnAllowLogin=true>
  </cfif>
  <cfif variables.blnAllowLogin>
    <cfquery  name="MM_rsUser" datasource="source">
      SELECT user_id, username, password
      FROM table
      WHERE username=<cfqueryparam value="#FORM.loginfield#" cfsqltype="cf_sql_varchar" maxlength="255">
      AND password=<cfqueryparam value="#FORM.password#" cfsqltype="cf_sql_varchar" maxlength="255">
    </cfquery>
    <cfif MM_rsUser.RecordCount>
      <cflock scope="Session" timeout="30" type="Exclusive">
        <cfset Session.MM_Username=FORM.loginfield>
        <cfset Session.MM_UserAuthorization=MM_rsUser.user_id[1]>
      </cflock>
      <cfset MM_redirectLoginSuccess="table/landing_page.cfm?user_id=#rs_user.user_id#">
      <cfquery name="qryUserAttemptsSuccessful" datasource="source">
        UPDATE table 
        SET
          failedLoginAttempt=0
          ,dateLocked=NULL
        WHERE
          user_id=<cfqueryparam value="#qryUserAttempts.user_id#" cfsqltype="cf_sql_integer">
      </cfquery>
      <cflocation url="#MM_redirectLoginSuccess#" addtoken="no">
      <!--- <cfelse>
        <cfif ArrayLen(Session.MM_rsUser.attempts) GTE 3><h2>You've exceeded your login attempts. Please try again later.</h2>
        <cfabort>
      <cfelse>
        <cfset ArrayAppend(Sessionm.MM_rsUser.attempts,Now())>
      </cfif> --->
    <cfelse>
      <cfset variables.failedLoginAttempt="">
      <cfif qryUserAttempts.recordcount>
        <cfif isNumeric(qryUserAttempts.failedLoginAttempt)>
          <cfset variables.failedLoginAttempt=qryUserAttempts.failedLoginAttempt+1>
        <cfelse>
          <cfset variables.failedLoginAttempt=1>
        </cfif>
        <cfquery name="qryUserAttempts" datasource="source">
          UPDATE table SET
            failedLoginAttempt=<cfqueryparam value="#variables.failedLoginAttempt#" cfsqltype="cf_sql_integer">
            <cfif variables.failedLoginAttempt GTE 3>
            ,dateLocked=<cfqueryparam value="#now()#" cfsqltype="cf_sql_timestamp">
            </cfif>
          WHERE
            user_id=<cfqueryparam value="#qryUserAttempts.user_id#" cfsqltype="cf_sql_integer">
        </cfquery>	
      </cfif>
      <cflocation url="#MM_redirectLoginFailed#&failedLoginAttempt=#variables.failedLoginAttempt#" addtoken="no">
    </cfif>
  <cfelse>
    <cflocation url="#MM_redirectLoginFailed#&accountlocked=true" addtoken="no">
  </cfif>
  <!--- End code for handling failed login attempts 
      <cfif IsDefined("URL.accessdenied") AND true>
        <cfset MM_redirectLoginSuccess=URL.accessdenied>
      </cfif>
      <cflocation url="#MM_redirectLoginSuccess#" addtoken="no">

  </cfif>
  <cflocation url="#MM_redirectLoginFailed#" addtoken="no">
  <cfelse> --->
</cfif>
<cfset MM_LoginAction=CGI.SCRIPT_NAME>
<cfif CGI.QUERY_STRING NEQ "">
  <cfset MM_LoginAction=MM_LoginAction & "?" & XMLFormat(CGI.QUERY_STRING)>
</cfif> 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<!-- Microdata markup added by Google Structured Data Markup Helper. -->

<html xmlns="http://www.w3.org/1999/xhtml" prefix="og: http://ogp.me/ns#">
  <head>
    <title>login</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <style>
      #target-content {
        position: fixed;
        top: 0;
        right: 0;
        bottom: 0;
        left: 0;
        pointer-events: none;
        opacity: 0;
        -webkit-transition: opacity 200ms;
        transition: opacity 200ms;
        z-index:3000;
      }

      #target-content:target {
        pointer-events: all;
        opacity: 1;
      }

      #target-content #target-inner {
        position: absolute;
        display: block;
        padding-left: 30px;
        padding-right:48px;
        padding-top:30px;
        padding-bottom:48px;
        line-height: 1.8;
        width: 45%;
        top: 50%;
        left: 50%;
        -webkit-transform: translateX(-50%) translateY(-50%);
        -ms-transform: translateX(-50%) translateY(-50%);
        transform: translateX(-50%) translateY(-50%);
        box-shadow: 0px 12px 24px rgba(0, 0, 0, 0.2);
        background: white;
        color: #34495E;
      }

      #target-content #target-inner h2 { margin-top: 0; }

      #target-content #target-inner code { font-weight: bold; }

      #target-content a.close {
        content: "";
        position: absolute;
        top: 0;
        right: 0;
        bottom: 0;
        left: 0;
        background-color: #000;
        opacity: 0.7;
        -webkit-transition: opacity 200ms;
        transition: opacity 200ms;
      }

      #target-content a.close:hover { opacity: 0.7; }


      input[type=submit] {
        -webkit-appearance: none;
      }

      .button_login {
          font: 16px/22px "Open Sans", Arial, sans-serif;
          display: inline-block;
        color: #FFF;
        text-transform: uppercase;
        display:inline-block;
        padding:12px 20px 12px 23px;
        font-weight:500;
        text-decoration:none;
        border:none;
        position:relative;
        z-index:1;
        background-color: #77bc1f;
        
      }
      .button_login:hover {
        text-decoration:none;
        background-color: #7d868c;
        color:#fff;
        
      }
    </style>
  </head>
  <body>
    <h2 style="text-align:left;">LOGIN</h2>
    <cfif IsDefined("URL.loginfailed")>
      <h5 style="text-align:left;">Authentication failed. <br />Please try again.</h5>
    </cfif>
      
    <cfif StructKeyExists(URL,"accountlocked")><h5 style="text-align:left;">You've exceeded your login attempts.<br />Please try again later.</h5></cfif>
      <form id="loginform" name="loginform" method="POST" action="<cfoutput>#MM_loginAction#</cfoutput>">
        <h5 style="margin-top:20px; color:#686868; margin-bottom:5px;" >Username: </h5>

        <input name="loginfield" type="text" id="loginfield"  style="font-size:18px; font-weight: 300; width:100%; height:30%;  border:none; padding-right:10px; padding-left:10px; padding-top:5px;  padding-bottom:5px;  position:relative; z-index:1;  	font-family: Tahoma, sans-serif; background: #f5f6f6; color:#7d868c; text-align:left; border-radius:0; -webkit-appearance: none;" />
        <h5 style="margin-top:20px; color:#686868; margin-bottom:5px;" >Password:</h5>

        <input name="password" type="text" id="password"   style="font-size:18px; font-weight: 300; width:100%; height:30%; border:none; padding-left:10px; padding-right:10px; padding-top:5px;  padding-bottom:5px; position:relative; z-index:1; 	font-family: Tahoma, sans-serif; background: #f5f6f6; color:#7d868c; text-align:left; margin:0; -webkit-appearance: none; border-radius:0;"/></h4>

        <input name="login" type="submit" id="login" value="LOGIN" />
      </form>
  </body>
</html>


person user3067421    schedule 23.01.2019    source источник
comment
Если опубликованный код работал в прошлом, в чем проблема сейчас?   -  person Dan Bracuk    schedule 24.01.2019
comment
@DanBracuk Как уже упоминалось, то, чего я пытаюсь достичь, похоже на вход в онлайн-аккаунт (т.е. на банковский счет). Где каждый пользователь имеет доступ к своей личной информации ТОЛЬКО. Сейчас все страницы на защищенной стороне динамические. Однако после входа в систему по умолчанию используется первый user_id или первая строка в таблице. При входе в систему мне нужно связать информацию из базы данных на основе user_id, имени пользователя и пароля - что-то вроде CFQuery WHERE user_id=cfqueryparam value =URL.user_id, а также сохранить все функции безопасности, которые у меня есть выше.   -  person user3067421    schedule 24.01.2019
comment
Похоже, вам нужно сохранить переменную сеанса и использовать ее.   -  person James A Mohler    schedule 24.01.2019
comment
Какая это версия ColdFusion?   -  person Shawn    schedule 24.01.2019
comment
Первая команда представляет собой запрос без предложения where. Это может быть источником вашей проблемы.   -  person Dan Bracuk    schedule 24.01.2019
comment
Еще один комментарий: ...AND password=<cfqueryparam value="#FORM.password#" cfsqltype="cf_sql_clob" maxlength="255"> указывает на то, что вы можете хранить пароль в открытом виде. Если да, то это очень и очень плохо. Только один человек должен знать пароль в открытом виде — пользователь. Если его может увидеть или восстановить кто-то другой, это невероятно небезопасно.   -  person Shawn    schedule 24.01.2019
comment
И если вы ограничиваете запись до 255 символов, вам, вероятно, не нужно использовать clob для ее хранения.   -  person Shawn    schedule 24.01.2019
comment
owasp.org/index.php/Password_Storage_Cheat_Sheet   -  person Shawn    schedule 24.01.2019
comment
Похоже на код Dreamweaver. Мастера IDE печально известны тем, что генерируют МНОГО постороннего/сомнительного кода, поэтому, возможно, вы захотите просмотреть его, чтобы урезать вещи и исправить проблемы, упомянутые выше.   -  person SOS    schedule 24.01.2019
comment
Кроме того, будьте осторожны, чтобы не перепутать переменные URL и FORM. Форма POST создает переменные FORM, а не URL.   -  person SOS    schedule 24.01.2019
comment
@Ageax хороший совет! Я использую форму внизу страницы, где пользователь может ввести свое имя пользователя и пароль и нажать «Отправить».   -  person user3067421    schedule 24.01.2019
comment
@ user3067421 Какую версию ColdFusion вы используете?   -  person Shawn    schedule 24.01.2019
comment
Однако после входа в систему по умолчанию используется первый user_id. Как говорили другие, это связано с тем, что 1-й запрос не имеет фильтра, И код перенаправления использует этот запрос для передачи идентификатора пользователя на целевую страницу. Другими словами, он делает это: table/landing_page.cfm?user_id=#UseTheVeryFirstUserIDInTheQueryResults#. Однако, как указал @JamesAMohler, вы должны использовать переменные сеанса, а не передавать идентификатор в URL-адресе (который можно подделать). В конечном счете, не полагайтесь на код, сгенерированный мастером. Обычно он раздут и не предназначен для обеспечения безопасности/лучших практик ;-)   -  person SOS    schedule 25.01.2019
comment
@Ageax Мне нравится то, что вы говорите, и это звучит как правильное решение, к сожалению, я недостаточно знаю, как включить переменную сеанса. Есть ли ссылка, по которой вы можете сослаться на меня, или пример, который вы могли бы предоставить? Любая помощь, которую вы могли бы предоставить, будет ОЧЕНЬ, ОЧЕНЬ высоко оценена. Большое спасибо!   -  person user3067421    schedule 25.01.2019
comment
@user3067421 user3067421 - Это все равно, что присвоить любое другое значение переменной. Разница только в том, что 1) вы должны включить область сеанса в файле Application.cfm/cfc, прежде чем сможете использовать эту оценку. Затем 2) вы используете сеанс имени области при присвоении значения, то есть <cfset session.user_id = 1234>. Используете ли вы в настоящее время Application.cfm/cfc? Дополнительные сведения о переменных сеанса см. в разделе helpx.adobe.com/coldfusion/developing-applications/   -  person SOS    schedule 25.01.2019
comment
О, подождите секунду... код уже использует переменные сеанса ;-) Просто найдите в приведенном выше коде сеанс.   -  person SOS    schedule 26.01.2019
comment
@Ageax Да, у меня есть файл Application.cfm. К сожалению, я не написал исходный код выше, поэтому я ищу помощи. Я попытался добавить следующий <cfset Session.user_id = rs_user.user_id› в свой Application.cfm, как было предложено, но это должно быть неправильно. Это не работает. :-(   -  person user3067421    schedule 26.01.2019
comment
@ user3067421 - Да, это не то имя запроса. Помните, я упоминал, что существует несколько запросов? rs_user - это тот, который, как вы согласились, не нужен. По правде говоря, сгенерированный Dreamweaver материал содержит МНОГО избыточного кода, из-за чего трудно понять, что он ДЕЙСТВИТЕЛЬНО делает, даже для людей, которые знают CF ;-) Если бы это был я, я бы, вероятно, выбросил его и начал с кода, который я понял. . В любом случае, вам нужно выполнить старомодную отладку. А именно - (временно) закомментируйте все cflocations, чтобы вы могли выводить различные значения на экран, чтобы выяснить, какие значения использует код.   -  person SOS    schedule 28.01.2019


Ответы (1)


Вы не хотите использовать переменную URL для хранения/передачи идентификатора пользователя. Вы хотите сохранить это как переменную сеанса.

Затем, как заметил Дэн Бракук, в первом запросе нет предложения «где», ограничивающего возвращаемую информацию о пользователе.

Вы должны использовать переменную области сеанса, в которой вы сохранили user_id в этом предложении «где», и убедитесь, что вы параметризовали его с помощью <cfqueryparam>

person Scott Stroz    schedule 24.01.2019
comment
На самом деле первый запрос, вероятно, даже не нужен. Хотя согласен, что это определенно часть проблемы. Некоторый код перенаправления использует неправильный запрос `?user_id=#rs_user.user_id#, то есть rs_user вместо MM_rsuser - person SOS; 24.01.2019
comment
Спасибо всем за все ваши предложения! Да, он был создан с помощью Dreamweaver. Я попытался использовать первый запрос с оператором Where, но безуспешно, и да, согласился, что запрос не нужен, я забыл удалить перед публикацией кода. Yikes, да, спасибо, что поймали Clob, это должен быть Varchar. Я попробую использовать запрос MM_rsuser для перенаправления. Я мало знаю о переменных сеанса. Если бы я использовал переменную сеанса, как бы она выглядела и где бы я ее разместил? Спасибо! - person user3067421; 24.01.2019
comment
Я попробовал использовать MM_rsuser в перенаправлении и снова получил внутренний код ошибки 500. Я также попытался добавить WHERE = ‹cfqueryparam value=#URL.user_id# cfsqltype=cf_sql_numeric› в запрос MM_rsuser и все равно получил внутренний код ошибки 500. - person user3067421; 24.01.2019
comment
Я также обнаружил, что использование первого запроса работает до тех пор, пока я не добавлю оператор Where, после чего снова получаю внутреннюю ошибку 500. - person user3067421; 24.01.2019
comment
@user3067421 user3067421 Ошибка 500 — это, по сути, Упс. Что-то сломалось. сообщение. Посмотрите в свои журналы, чтобы увидеть, что такое фактическая ошибка. - person Shawn; 24.01.2019
comment
Надеюсь, фактическое предложение WHERE также включает имя столбца. Если нет, это определенно приведет к поломке запроса (-; - person SOS; 24.01.2019
comment
@ user3067421 - Игнорирование отсутствующего столбца на секунду - зачем вам вообще нужен этот первый запрос? то есть, если это форма входа в систему, какой смысл запускать запрос информации о пользователе - до того, как текущий пользователь даже отправит форму, чтобы сообщить вам, кто они? - person SOS; 25.01.2019
comment
@Ageax Вы правы, мне не нужен дополнительный запрос вверху. Я пытался понять, как это сделать, как я описал в предыдущем комментарии. Ищу любую помощь. - person user3067421; 25.01.2019
comment
@user3067421 user3067421 Какая это версия CF? Это может иметь огромное значение в некоторых предложениях по решениям. - person Shawn; 26.01.2019
comment
@ Шон, я не совсем уверен. Я использую Dreamweaver CS6 для кода, но на моем компьютере также установлен Coldfusion2016. Надеюсь, это поможет. - person user3067421; 26.01.2019
comment
Так это работает на ColdFusion2016? Это учебное упражнение? В любом случае, если вы собираетесь редактировать код, вы окажете себе огромную услугу, отказавшись от Dreamwweaver и переключившись на Visual Studio Code или SublimeText или что-то в этом роде. Оба бесплатны. И код, который пишет Dreamweaver, иногда особенный. - person Shawn; 27.01.2019