Как создать временную ссылку для новой учетной записи пользователя? - PullRequest
0 голосов
/ 25 сентября 2018

Я создал одностраничное приложение, которое требует username и password для аутентификации пользователей.Каждая учетная запись в системе создается администраторами.Пользователь не может щелкнуть ссылку и создать учетную запись.После исследования и поиска лучшего решения первое, что я изменил, было то, как пользователь может восстановить свой пароль.Вот краткая схема того, как работает эта логика.Пользователь сначала должен нажать на ссылку Forgot Password, ввести свой адрес электронной почты и отправить форму.Они увидят сообщение:

An email has been sent to example@gmail.com with further instructions.

Функция, которая будет выполнять этот процесс, выглядит следующим образом:

cfstoredproc( procedure="CheckEmail", datasource=dsn ) {
    cfprocparam( maxlength=80, null=!len(trim(arguments.email)), cfsqltype="cf_sql_varchar", dbvarname="@Email", value=trim(arguments.email) );
    cfprocresult( name="EmailResult" );

}

if( EmailResult.recordCount EQ 1) {
    //  Generate new token 
    local.newToken = createUUID();
    //  Build URL with token parameter and add hashed value 
    local.theUrl = "https://example.com/Reset.cfm?token=" & local.newToken;
    //  Set expiration time (30 minutes) 
    local.Expires = DateAdd("n", 30, now());

    cfstoredproc( procedure="SaveToken", datasource=dsn ) {
        cfprocparam( maxlength=80, null=!len(trim(arguments.email)), cfsqltype="cf_sql_varchar", dbvarname="@Email", value=trim(arguments.email) );
        cfprocparam( maxlength=35, null=!len(trim(local.newToken)), cfsqltype="cf_sql_varchar", dbvarname="@Token", value=trim(local.newToken) );
        cfprocparam( null=!len(trim(local.Expires)), cfsqltype="cf_sql_timestamp", dbvarname="@Expires", value=trim(local.Expires) );
        cfprocresult( name="TokenResult" );

    }

    if ( len(TokenResult.RecID) ) {
        savecontent variable="mailBody"{   
            writeOutput('<br>Here is your password reset link: <a href="' & theUrl & '">Click here</a> as soon as possible and change your password.<br>'                           );  
         }

         local.mail = new mail();
         // Set it's properties
         local.mail.setSubject("Example Application");
         local.mail.setTo(arguments.email);
         local.mail.setFrom("noreply@example.com");
         local.mail.setType("html");
         // Send the email
         local.mail.send(body = mailBody);
         local.fnResults = {status : "200", message : "An email has been sent to <b>" & arguments.email & "</b> with further instructions."};
    } else {
         local.fnResults = {status : "400", message : "Error! Something went wrong."};
    }
}else{
    savecontent variable="mailBody"{   
        writeOutput('<br>We recieved a password reset request. The email you have provided does not exist in our system.<br>');  
    }

    local.mail = new mail();
    // Set it's properties
    local.mail.setSubject("Example Application");
    local.mail.setTo(arguments.email);
    local.mail.setFrom("noreply@example.com");
    local.mail.setType("html");
    // Send the email
    local.mail.send(body = mailBody);
    local.fnResults = {status : "200", message : "An email has been sent to <b>" & arguments.email & "</b> with further instructions."};
}

Тогда следующим шагом будет, если электронная почта существует и пользовательнажимая на ссылку, я либо покажу форму, где они могут ввести новый пароль, либо они увидят сообщение This link has expired or does not exist anymore..Вот пример страницы Reset.cfm:

if (structKeyExists(url,"token") && isValid("uuid", url.token) && len(trim(url.token)) == 35){
    cfstoredproc( procedure="CheckToken", datasource=dsn ) {
            cfprocparam( maxlength=35, null=!len(trim(url.token)), cfsqltype="cf_sql_varchar", dbvarname="@Token", value=trim(url.token) );
            cfprocresult( name="TokenResult" );

        }

        if( TokenResult.recordCount == 1 ){ //If token is valid (not expired) show the form.
            <form name="frmRecovery" id="frmRecovery" autocomplete="off">
                <input type="hidden" name="token" value="<cfoutput>#url.token#</cfoutput>">
                <div class="form-group">
                    <div class="alert alert-info"><strong>Info!</strong> After saving your changes, you will be taken back to the login screen. Log into the system with the account credentials you have just saved.</div>
                </div>
                <div class="form-group">
                   <label class="control-label" for="password"><span class="label label-primary">Password</span></label>
                   <input type="password" class="form-control" name="frmRecovery_password" id="frmRecovery_password" placeholder="Enter Password" maxlength="64" required>
                </div>
                <div class="form-group">
                   <button type="submit" class="btn btn-primary">Submit</button>
                </div>
                <div class="form-group">
                   <div class="alert message-submit"></div>
                </div>
            </form>
        }else{
            <div class="alert alert-warning">
                <strong>Warrning!</strong> This link has expired or does not exist anymore!  </div>
        }
    }

Если пользователь будет перенаправлен на форму для ввода пароля, я сохраню новый пароль и удалю токен.Следующим шагом является направление их на страницу входа, и они могут ввести учетные данные и войти в систему.Итак, мой вопрос: могу ли я использовать подобный подход, когда администратор создает новую учетную запись?администратор должен ввести first, last name, username и т. д. Затем нажмите кнопку Send Email, которая перешлет username и временную ссылку, где новый пользователь сможет ввести свой пароль и войти в приложение.Логика, которая используется ранее, заключается в создании временного пароля, пользователь входит в систему и затем должен сбросить пароль.Мне интересно, если решение, которое я предложил wold, имеет какие-либо риски для безопасности или так же хорошо, как решение с временным паролем?

1 Ответ

0 голосов
/ 26 сентября 2018

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

Для повышения безопасности, в зависимости от того, насколько вы усилены / защищены, некоторыепредложения:

GUID / UUID предназначены для того, чтобы быть уникальными, но, конечно, не предназначены для того, чтобы их нельзя было угадать.Их цель - избежать столкновения, а не предсказания.Если вы выводите набор UUID, сгенерированных Coldfusion, вы можете с некоторой степенью угадать некоторые возможные комбинации, чтобы симулировать процесс сброса, пока не найдете один - учитывая, что у вас был действительный UUID для начала, в течение нескольких миллионов или около того (оченьнемного).

Так будет работать эта атака - вы отправите два одновременных запроса на сброс для двух пользователей.Тот, который известен (скажем, сотрудник, у которого есть электронная почта, но с низким уровнем разрешений), и тот, кто неизвестен (скажем, менеджер, у которого у вас нет доступа к своей электронной почте).

У вас будет"seed" или известный UUID, созданный для сотрудника (злоумышленника) примерно в то же время, что и ключ сброса для менеджера (жертвы).Затем вы можете создать попытки сброса для этого блока UUID на основе известного UUID.Это не займет много вычислительной мощности или времени.

Если вместо этого вы создадите составную строку UUID в сочетании со случайно сгенерированной / хранимой солью (это будет дополнительное поле, которое вы хотели бы сохранитьдля пользовательской записи.) - это будет самая сильная мера, поскольку она будет уникальной и не может быть угадана с помощью известного ключа.

Другой способ - ограничить / ограничить запросы к процессу сброса (т. Е. Задержка в 500 мс и т. Д., Пользователь не видит заметных отличий, но строго ограничивает полезность автоматической атаки).

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

Ваши пароли также должны быть хешированы случайной солью, конечно.

Само собой разумеется, что более надежные пароли более безопасны.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...