Запрещаем браузеру Google Chrome сохранять и предлагать сохраненный пароль в нашем приложении на Vue.js(v2)

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

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

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

Сначала двинулся в сторону того же гугла — как отключить или запретить автосохранение или автоподстановку сохраненного пароля? Старые рецепты с 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.

@marsdenden
16.11.2021 14:25 UTC
Первоисточник

Комментарии

@Ilusha
16.11.2021 09:47 UTC
-3

2FA: Google Authenticator.

@KivApple
16.11.2021 10:59 UTC
0

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

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

@marsdenden
16.11.2021 11:01 UTC
0

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

16.11.2021 11:35 UTC
0

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

16.11.2021 12:21 UTC
+1

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

@kipar
16.11.2021 11:02 UTC
+4

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

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

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

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

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

@Ritan
16.11.2021 12:46 UTC
+1

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

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

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

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

@marsdenden
16.11.2021 15:02 UTC
-1

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

16.11.2021 17:50 UTC
+3

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

16.11.2021 19:20 UTC
+1

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

@Getequ
16.11.2021 13:07 UTC
+2

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

@dartraiden
16.11.2021 18:02 UTC
+6
Лично для меня актуальная иная проблема: я сам хочу решать, могу я вставить в поле скопированный из менеджера паролей пароль, а не терпеть навязчивость веб-мастера, который решил, что я должен свой 50-символьный пароль из букв, цифр и спецсимволов перепечатывать вручную.
18.11.2021 05:49 UTC
0

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

@aamonster
16.11.2021 18:39 UTC
0

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

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

@shifttstas
17.11.2021 07:16 UTC
+1

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

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

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

17.11.2021 10:24 UTC
+1

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

17.11.2021 12:28 UTC
+1

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

17.11.2021 12:46 UTC
0

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

17.11.2021 13:42 UTC
0

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

17.11.2021 16:13 UTC
0

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

18.11.2021 07:17 UTC
0

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

@dartraiden
16.11.2021 18:08 UTC
+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*/}})()
@VXP
16.11.2021 23:29 UTC
0

Благодарю)

@aamonster
16.11.2021 18:40 UTC
0

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

@marsdenden
17.11.2021 05:38 UTC
0

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

17.11.2021 06:16 UTC
+1

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

17.11.2021 10:22 UTC
0

ошибаетесь

@DarthVictor
16.11.2021 22:00 UTC
+1
Кастомным шрифтом, состоящим из одного символа на все буквы это делается. Для полей для кодов подтверждения, если их просят скрыть (я сам не люблю, когда скрывают, но это вопрос вкуса) это самое нормальное решение. Оно и кроссплатформено в отличие от --webkit-text-security. Хотя если FF не нужен, то последний короче.
Вопрос осмысленности запрета сохранения паролей я пожалуй обсуждать не буду.
@Dzzzen
17.11.2021 17:21 UTC
+2

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