Мир стоит вверх ногами. Корпорация добра печется о нашей безопасности. И в то же время делает все, чтобы наши секреты стали достоянием гласности. Это я о политике гугл в плане сохранения логин-паролей в браузере, через него в аккаунте и о доступности всего нашего секретного добра всем, кто сможет подойти к нашим компьютерам.

Да, удобно. Но небезопасно. И временами очень вредно.

Запустил я очередной проект на работе. И самой большой проблемой по опыту старого проекта было то, что сотрудники вовсю пользовались автосохраненим паролей, поэтому под Васей мог сидеть Сергей и ни о какой нормальной аутентификации, а тем более разраничении прав доступа речи идти не могло.

Сначала двинулся в сторону того же гугла — как отключить или запретить автосохранение или автоподстановку сохраненного пароля? Старые рецепты с autocomplete="off" и autocomplete="new-password" уже не работают, последний работающий рецепт оказался прост — головную боль следует лечить усекновением болящей. То есть глобальным отключением автосохранения. Но это не вариант. Пусть юзеры дальше балуются своими паролями от почты, соцсетей и прочих радостей жизни, мне не жалко.

Далее последовал легкий инжиниринг. И вот какой результат — если на страничке есть input type="password", то при появлении первого символа в адресной строке появляется ключик. И все. Что ты ни делай, какие html тэги ни прописывай — есть password, значит, надо бежать и барину кланяться, предлагая все мыслимые и немыслимые блага от того, что барин позволит сохранить пароль. Ну и потом барин будет весьма доволен, что пароль не надо вспоминать, а просто его вставить.

Хмм... подумал я... И решил — раз у хрома такая нервная реакция на type="password", то надо просто от него отказаться и всех делов то!

Сказано — сделано. Изваял компонент, который незамысловато делает простейшую вещь — на экран выводит точечки, а в v-model передает то, что введено. Ну и плюсом отрабатывает клавишу Enter.

Конечно, не обошлось без ограничений. Но они очень малы и незначительны — нельзя курсор перемещать стрелками, только backspace. Но, учитывая, что под точками и так не видно, где и что надо подправить — вероятность испытать колоссальное неудобство от такого ограничения стремится к нулю. Не стал делать кнопочку с глазиком, чтобы посмотреть, что же я ввел, label для input оформил в стиле компонента vuetify, то есть текст-подсказка уползает вверх при фокусе и возвращается при утере фокуса при условии, что ничего не было введено.

Ну и использование такого компонента тоже очень простое — создать .vue файл, импортировать в проект и использовать в шаблоне.

Теперь, собственно, код.

Код под спойлером
<template>
    <div class="logpass">
        <input type="hidden" v-model="opass">
        <label :for="inpid">Пароль</label>
        <input style="width: 100%"
               :id="inpid"
               type="text"
               v-model="ipass"
               @input="input"
               @keydown="keyDown"
               @focus="focus"
               @blur="blur"
               @keydown.enter="$emit('keydown_enter',$event)">
    </div>
</template>

<script>
    export default {
        name: "login-password",
        data() {
            return {
                ipass:'', // input password
                opass:'', // output password
            }
        },
        computed: {
            inpid(){
                let length=7
                let result
                let characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
                let letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
                let el=''
                while(el!==null){ // void to duplicate id if node with same id exists
                    result=letters.charAt(Math.floor(Math.random() *
                        letters.length));
                    for (let i = 0; i < length; i++) {
                        result += characters.charAt(Math.floor(Math.random() *
                            characters.length));
                    }
                    el=document.querySelector('#'+result)
                }
                return result;
            },
        },
        methods: {
            focus(event){
                let el=document.querySelector(['label[for="'+event.target.id+'"]'])
                if(el){
                    el.classList.add('label-active')
                }
            },
            blur(event){
                let el=document.querySelector(['label[for="'+event.target.id+'"]'])
                if(el && this.opass==''){
                    el.classList.remove('label-active')
                }
            },
            input(event){
                if(this.opass.length<this.ipass.length){
                    let s = this.ipass.slice(-1)
                    this.opass=''.concat(this.opass,s)
                    s = ''
                    for(let i=0;i<this.ipass.length;i++)s=''.concat(s,'•')
                    this.ipass=s
                    this.$emit('input',this.opass)
                }else{
                    if(this.opass.length>this.ipass.length){
                        this.opass=this.opass.slice(0,this.ipass.length)
                        this.$emit('input',this.opass)
                    }
                }
            },
            keyDown(event){
                if(event.key=='ArrowLeft'||event.key=='ArrowRight'||event.key=='ArrowUp'||event.key=='ArrowDown'){
                    event.preventDefault()
                }
            },
        },
    }
</script>

<style scoped lang="scss">
.logpass{
    padding-top:14px;
    padding-bottom: 12px;
    display: flex;
    & label{
        padding: 5px;
        position: absolute;
        color: #999;
        transition: .3s cubic-bezier(.25,.8,.5,1);
    }
    & .label-active{
        font-size: 10px;
        margin-top: -20px;
        margin-left: -5px;
    }
}
</style>

Как видим — достаточно простая логика — при вводе очередного символа он заменяется на точку и дублируется в переменную, переданную в v-model. Именно запрет на перемещение курсора в поле ввода позволил реализацию такой логики.

Еще одно замечание — этот компонент не использует инициализацию значением, которое, возможно уже было в v-model-переменной.

Пример использования:

// какой-нибудь наш модуль на vue, в котором необходим ввод пароля
<template>
	<div>
    логин: <input type="text" v-model="mylogin">
    <login-password v-model="mypassword" @keydown_enter="makeLogin">
    </login-password>
  </div>
</template>
<script>
    export default {
        name: "Login",
        data() {
            return {
                mylogin: '',
                mypassword:''
            }
        },
        components: {
            LoginPassword:()=>import('./login-password')
        },      	
      	methods:{
          makeLogn(){
            .....
          }
          .......
        

              

Ну и напоследок — код свободный, берите, кто хотите, возможно, кому-то идея подойдет на React или Angular.

Комментарии (31)


  1. Ilusha
    16.11.2021 12:47
    -3

    2FA: Google Authenticator.


  1. KivApple
    16.11.2021 13:59

    Очевидное техническое решение административных проблем.

    Какими html тегами будете бороться с записью пароля на стикер на мониторе?


    1. marsdenden Автор
      16.11.2021 14:01

      ну как только всплывет косяк под учеткой одного, сделанный другим - сами будут думать.


      1. KivApple
        16.11.2021 14:35

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


        1. marsdenden Автор
          16.11.2021 15:21
          +1

          вот оно мне надо - бегать и искать эти косяки? Сиди потом, ковыряйся в логах, смотри на камеры, опознавай, кто подходил и что делал... Я сразу перекрыл этот путь. Зашли под твоим паролем - твои проблемы. Либо ты его раздавал, либо тупо вкладку не закрыл.


  1. kipar
    16.11.2021 14:02
    +4

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

    Но ваш метод явно лучше.


  1. NikitchenkoSergey
    16.11.2021 14:06
    +3

    Можно проще: сделать type=«text» и навесить стиль -webkit-text-security
    developer.mozilla.org/en-US/docs/Web/CSS/-webkit-text-security

    работает не везде, где не работает можно оставить type=«password», т.к. только хром так навязчиво с паролями работает. Ну и в интернете есть полифилы на шрифтах.


    1. marsdenden Автор
      16.11.2021 14:23

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


  1. Ritan
    16.11.2021 15:46
    +1

    Любая поломка UX - зло невозможное. Очень и очень надеюсь, что подобный мусор останется только там, где его и сваяли и не выползет никуда за пределы.

    Но они очень малы и незначительны — нельзя курсор перемещать стрелками, только backspace.

    А то каждый мнит, что ему решать что неудобство, а что - мелочь. Я, может, помню что у меня за пароль и знаю где опечатался. В конце-концов пропуск первого символа уж точно легко заметить и исправить

    Если люди захотят, они и на рабочем столе файл passwords.txt оставят. И никакие подобные "умные" решения их не остановят


    1. marsdenden Автор
      16.11.2021 18:02
      -1

      зло - это когда тебе делают нечто, тебя не спросив. И не предоставив адекватных вариантов. Так что я считаю злом навязчивость корпорации. А если нужен гиковский функционал вслепую править вбиваемый пароль - велкам, код открыт, сделайте свой вариант.
      ЗЫ. Кстати, да, спасибо за подсказку. Нужно еще home/end включить в блокировку


      1. Ritan
        16.11.2021 20:50
        +3

        Щелчки мышью ещё запретите. А лучше сразу мышь отключайте на странице, чтобы тупой пользователь куда-нибудь не туда не ткнул в вашем поделии. И клавиатуру заодно, а то там можно ещё пяток способ переходить по тексту придумать.


      1. aamonster
        16.11.2021 22:20
        +1

        В хроме есть прекрасная кнопка – не запоминать пароли на этом сайте. Прямо в окошке "сохранить пароль?" (Кстати, это как раз вопрос. Точно "не спросив"?)


  1. Getequ
    16.11.2021 16:07
    +2

    Непонятно почему заминусили - проблема очень актуальная, особенно неприятно когда на сайте банка срабатывает автокомплит логина+пароля простым щелчком в поле логина не вводя ни одной буквы


    1. dartraiden
      16.11.2021 21:02
      +6

      Лично для меня актуальная иная проблема: я сам хочу решать, могу я вставить в поле скопированный из менеджера паролей пароль, а не терпеть навязчивость веб-мастера, который решил, что я должен свой 50-символьный пароль из букв, цифр и спецсимволов перепечатывать вручную.


      1. anba8005
        18.11.2021 08:49

        Корпоративная политика она такая. Не нравится - свободен :)


    1. aamonster
      16.11.2021 21:39

      Вполне понятно: любой, кто старается сделать людям неудобно – враг, независимо от того, насколько благими намерениями он руководствуется.

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


    1. shifttstas
      17.11.2021 10:16
      +1

      Так это проблема вашей платформы или браузера, на маке надо Touch ID пройти что бы сработал автокомплит, на iPhone - Face ID.

      Очевидным решением было бы разделение сайтов по уровням конфиденциальности и там где риски высокие - требовать что бы браузер проводил сверку через биометрию.

      PS С друной стороны - зачем всё это если есть 2FA/OTP ?


      1. marsdenden Автор
        17.11.2021 13:24
        +1

        есть жизнь за мкадом и без айфонов.


        1. shifttstas
          17.11.2021 15:28
          +1

          При отсутствии сенсоров бимотреии пароли можно хранить в менеджере и вставлять только при вводе мастер-пароля


          1. marsdenden Автор
            17.11.2021 15:46

            бегать и всем мастер-пароли устанавливать? а есть и такие, до которых 200км бежать


            1. shifttstas
              17.11.2021 16:42

              200км до клавиатуры? мастер-пароль я имею ввиду в вашем менеджере паролей.


              1. marsdenden Автор
                17.11.2021 19:13

                вы не поняли тему. Это сделано для приложения, которое используется внутри компании. Есть сотрудники, которые сидят удаленно, есть по городу. Если по городу могу всеми управлять удаленно, то теми, кто на реальной удаленке управлять нет возможности. Поэтому мастер-пароль - это вообще не то. И мой менеджер паролей тоже не при чем.


                1. shifttstas
                  18.11.2021 10:17

                  Если мы говорим о Enterprise рынке - тогда ограничение на ввод и/или использование того или иного софта должно быть сделано на уровне устройства пользователя (корпоративного или BYOD) через MDM. Это даже еще проще - просто отключить автокомплит через групповые политики.


  1. dartraiden
    16.11.2021 21:08
    +9

    Немного букмарклетов, позволяющий бороться с навязчивостью создателей веб-ресурсов, считающих, что им виднее:

    Разрешить операции копирования и вставки на странице:

    javascript:(function(w){var%20arr=['contextmenu','copy','cut','paste','mousedown','mouseup','beforeunload','beforeprint'];for(var%20i=0,x;x=arr[i];i++){if(w['on'+x])w['on'+x]=null;w.addEventListener(x,function(e){e.stopPropagation()},true)};for(var%20j=0,f;f=w.frames[j];j++){try{arguments.callee(f)}catch(e){}}})(window);


    Сделать доступными заблокированные поля:
    javascript:(function(){function process(tag){var i,l,els=document.getElementsByTagName(tag);for(i=0,l=els.length;i<l;i++){els[i].readOnly = null;els[i].disabled = null;}}process('input');process('select');process('textarea');process('button');})()


    Показать значения, скрытые за «звёздочками»:
    javascript:(function(){var%20s,F,j,f,i;%20s%20=%20"";%20F%20=%20document.forms;%20for(j=0;%20j<F.length;%20++j)%20{%20f%20=%20F[j];%20for%20(i=0;%20i<f.length;%20++i)%20{%20if%20(f[i].type.toLowerCase()%20==%20"password")%20s%20+=%20f[i].value%20+%20"\n";%20}%20}%20if%20(s)%20alert("Passwords%20in%20forms%20on%20this%20page:\n\n"%20+%20s);%20else%20alert("There%20are%20no%20passwords%20in%20forms%20on%20this%20page.");})();


    Показать все скрытые поля:
    javascript:(function(){var%20i,f,j,e,div,label,ne;%20for(i=0;f=document.forms[i];++i)for(j=0;e=f[j];++j)if(e.type=="hidden"){%20D=document;%20function%20C(t){return%20D.createElement(t);}%20function%20A(a,b){a.appendChild(b);}%20div=C("div");%20label=C("label");%20A(div,%20label);%20A(label,%20D.createTextNode(e.name%20+%20":%20"));%20e.parentNode.insertBefore(div,%20e);%20e.parentNode.removeChild(e);%20ne=C("input");/*for%20ie*/%20ne.type="text";%20ne.value=e.value;%20A(label,%20ne);%20label.style.MozOpacity=".6";%20--j;/*for%20moz*/}})()


    1. VXP
      17.11.2021 02:29

      Благодарю)


  1. aamonster
    16.11.2021 21:40

    И кого больше получилось в результате вашего хака – пользователей с паролем qwerty123 или пользователей со стикером на мониторе? ;-)


    1. marsdenden Автор
      17.11.2021 08:38

      ни одного в обоих категориях. Решение для внутреннего использования, в проде уже неделю, негативной реакции нет.


      1. aamonster
        17.11.2021 09:16
        +1

        Поздравляю, у вас обратной связи с пользователями нет вообще.


        1. marsdenden Автор
          17.11.2021 13:22

          ошибаетесь


  1. DarthVictor
    17.11.2021 01:00
    +1

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


  1. Dzzzen
    17.11.2021 20:21
    +2

    А не проще было через групповые политики отключить сохранение паролей в Chrome сразу во всей организации?